Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
39cf70ec
Commit
39cf70ec
authored
Jun 08, 2016
by
cahrens
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add new xblock config models.
TNL-4666
parent
1cda718f
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
370 additions
and
2 deletions
+370
-2
common/djangoapps/xblock_django/admin.py
+58
-2
common/djangoapps/xblock_django/api.py
+53
-0
common/djangoapps/xblock_django/migrations/0003_add_new_config_models.py
+53
-0
common/djangoapps/xblock_django/models.py
+68
-0
common/djangoapps/xblock_django/tests/test_api.py
+138
-0
No files found.
common/djangoapps/xblock_django/admin.py
View file @
39cf70ec
...
@@ -3,7 +3,63 @@ Django admin dashboard configuration.
...
@@ -3,7 +3,63 @@ Django admin dashboard configuration.
"""
"""
from
django.contrib
import
admin
from
django.contrib
import
admin
from
config_models.admin
import
ConfigurationModelAdmin
from
config_models.admin
import
ConfigurationModelAdmin
,
KeyedConfigurationModelAdmin
from
xblock_django.models
import
XBlockDisableConfig
from
xblock_django.models
import
(
XBlockDisableConfig
,
XBlockConfiguration
,
XBlockStudioConfiguration
,
XBlockStudioConfigurationFlag
)
from
django.utils.translation
import
ugettext_lazy
as
_
admin
.
site
.
register
(
XBlockDisableConfig
,
ConfigurationModelAdmin
)
admin
.
site
.
register
(
XBlockDisableConfig
,
ConfigurationModelAdmin
)
class
XBlockConfigurationAdmin
(
KeyedConfigurationModelAdmin
):
"""
Admin for XBlockConfiguration.
"""
fieldsets
=
(
(
'XBlock Name'
,
{
'fields'
:
(
'name'
,)
}),
(
'Enable/Disable XBlock'
,
{
'description'
:
_
(
'To disable the XBlock and prevent rendering in the LMS, leave "Enabled" deselected; '
'for clarity, update XBlockStudioConfiguration support state accordingly.'
),
'fields'
:
(
'enabled'
,)
}),
(
'Deprecate XBlock'
,
{
'description'
:
_
(
"Only XBlocks listed in a course's Advanced Module List can be flagged as deprecated. "
"Remember to update XBlockStudioConfiguration support state accordingly, as deprecated "
"does not impact whether or not new XBlock instances can be created in Studio."
),
'fields'
:
(
'deprecated'
,)
}),
)
class
XBlockStudioConfigurationAdmin
(
KeyedConfigurationModelAdmin
):
"""
Admin for XBlockStudioConfiguration.
"""
fieldsets
=
(
(
''
,
{
'fields'
:
(
'name'
,
'template'
)
}),
(
'Enable Studio Authoring'
,
{
'description'
:
_
(
'XBlock/template combinations that are disabled cannot be edited in Studio, regardless of support '
'level. Remember to also check if all instances of the XBlock are disabled in XBlockConfiguration.'
),
'fields'
:
(
'enabled'
,)
}),
(
'Support Level'
,
{
'description'
:
_
(
"Enabled XBlock/template combinations with full or provisional support can always be created "
"in Studio. Unsupported XBlock/template combinations require course author opt-in."
),
'fields'
:
(
'support_level'
,)
}),
)
admin
.
site
.
register
(
XBlockConfiguration
,
XBlockConfigurationAdmin
)
admin
.
site
.
register
(
XBlockStudioConfiguration
,
XBlockStudioConfigurationAdmin
)
admin
.
site
.
register
(
XBlockStudioConfigurationFlag
,
ConfigurationModelAdmin
)
common/djangoapps/xblock_django/api.py
0 → 100644
View file @
39cf70ec
"""
API methods related to xblock state.
"""
from
xblock_django.models
import
XBlockConfiguration
,
XBlockStudioConfiguration
,
XBlockStudioConfigurationFlag
def
deprecated_xblocks
():
"""
Return the QuerySet of deprecated XBlock types. Note that this method is independent of
`XBlockStudioConfigurationFlag` and `XBlockStudioConfiguration`.
"""
return
XBlockConfiguration
.
objects
.
current_set
()
.
filter
(
deprecated
=
True
)
def
disabled_xblocks
():
"""
Return the QuerySet of disabled XBlock types (which should not render in the LMS).
Note that this method is independent of `XBlockStudioConfigurationFlag` and `XBlockStudioConfiguration`.
"""
return
XBlockConfiguration
.
objects
.
current_set
()
.
filter
(
enabled
=
False
)
def
authorable_xblocks
(
allow_unsupported
=
False
,
name
=
None
):
"""
If Studio XBlock support state is enabled (via `XBlockStudioConfigurationFlag`), this method returns
the QuerySet of XBlocks that can be created in Studio (by default, only fully supported and provisionally
supported). If `XBlockStudioConfigurationFlag` is not enabled, this method returns None.
Note that this method does not take into account fully disabled xblocks (as returned
by `disabled_xblocks`) or deprecated xblocks (as returned by `deprecated_xblocks`).
Arguments:
allow_unsupported (bool): If `True`, enabled but unsupported XBlocks will also be returned.
Note that unsupported XBlocks are not recommended for use in courses due to non-compliance
with one or more of the base requirements, such as testing, accessibility, internationalization,
and documentation. Default value is `False`.
name (str): If provided, filters the returned XBlocks to those with the provided name. This is
useful for XBlocks with lots of template types.
Returns:
QuerySet: If `XBlockStudioConfigurationFlag` is enabled, returns authorable XBlocks,
taking into account `support_level`, `enabled` and `name` (if specified).
If `XBlockStudioConfigurationFlag` is disabled, returns None.
"""
if
not
XBlockStudioConfigurationFlag
.
is_enabled
():
return
None
blocks
=
XBlockStudioConfiguration
.
objects
.
current_set
()
.
filter
(
enabled
=
True
)
if
not
allow_unsupported
:
blocks
=
blocks
.
exclude
(
support_level
=
XBlockStudioConfiguration
.
UNSUPPORTED
)
if
name
:
blocks
=
blocks
.
filter
(
name
=
name
)
return
blocks
common/djangoapps/xblock_django/migrations/0003_add_new_config_models.py
0 → 100644
View file @
39cf70ec
# -*- coding: utf-8 -*-
from
__future__
import
unicode_literals
from
django.db
import
migrations
,
models
import
django.db.models.deletion
from
django.conf
import
settings
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
migrations
.
swappable_dependency
(
settings
.
AUTH_USER_MODEL
),
(
'xblock_django'
,
'0002_auto_20160204_0809'
),
]
operations
=
[
migrations
.
CreateModel
(
name
=
'XBlockConfiguration'
,
fields
=
[
(
'id'
,
models
.
AutoField
(
verbose_name
=
'ID'
,
serialize
=
False
,
auto_created
=
True
,
primary_key
=
True
)),
(
'change_date'
,
models
.
DateTimeField
(
auto_now_add
=
True
,
verbose_name
=
'Change date'
)),
(
'enabled'
,
models
.
BooleanField
(
default
=
False
,
verbose_name
=
'Enabled'
)),
(
'name'
,
models
.
CharField
(
max_length
=
255
,
db_index
=
True
)),
(
'deprecated'
,
models
.
BooleanField
(
default
=
False
,
verbose_name
=
'show deprecation messaging in Studio'
)),
(
'changed_by'
,
models
.
ForeignKey
(
on_delete
=
django
.
db
.
models
.
deletion
.
PROTECT
,
editable
=
False
,
to
=
settings
.
AUTH_USER_MODEL
,
null
=
True
,
verbose_name
=
'Changed by'
)),
],
options
=
{
'ordering'
:
(
'-change_date'
,),
'abstract'
:
False
,
},
),
migrations
.
CreateModel
(
name
=
'XBlockStudioConfiguration'
,
fields
=
[
(
'id'
,
models
.
AutoField
(
verbose_name
=
'ID'
,
serialize
=
False
,
auto_created
=
True
,
primary_key
=
True
)),
(
'change_date'
,
models
.
DateTimeField
(
auto_now_add
=
True
,
verbose_name
=
'Change date'
)),
(
'enabled'
,
models
.
BooleanField
(
default
=
False
,
verbose_name
=
'Enabled'
)),
(
'name'
,
models
.
CharField
(
max_length
=
255
,
db_index
=
True
)),
(
'template'
,
models
.
CharField
(
default
=
b
''
,
max_length
=
255
,
blank
=
True
)),
(
'support_level'
,
models
.
CharField
(
default
=
b
'us'
,
max_length
=
2
,
choices
=
[(
b
'fs'
,
'Fully Supported'
),
(
b
'ps'
,
'Provisionally Supported'
),
(
b
'us'
,
'Unsupported'
)])),
(
'changed_by'
,
models
.
ForeignKey
(
on_delete
=
django
.
db
.
models
.
deletion
.
PROTECT
,
editable
=
False
,
to
=
settings
.
AUTH_USER_MODEL
,
null
=
True
,
verbose_name
=
'Changed by'
)),
],
),
migrations
.
CreateModel
(
name
=
'XBlockStudioConfigurationFlag'
,
fields
=
[
(
'id'
,
models
.
AutoField
(
verbose_name
=
'ID'
,
serialize
=
False
,
auto_created
=
True
,
primary_key
=
True
)),
(
'change_date'
,
models
.
DateTimeField
(
auto_now_add
=
True
,
verbose_name
=
'Change date'
)),
(
'enabled'
,
models
.
BooleanField
(
default
=
False
,
verbose_name
=
'Enabled'
)),
(
'changed_by'
,
models
.
ForeignKey
(
on_delete
=
django
.
db
.
models
.
deletion
.
PROTECT
,
editable
=
False
,
to
=
settings
.
AUTH_USER_MODEL
,
null
=
True
,
verbose_name
=
'Changed by'
)),
],
),
]
common/djangoapps/xblock_django/models.py
View file @
39cf70ec
...
@@ -6,6 +6,7 @@ from django.utils.translation import ugettext_lazy as _
...
@@ -6,6 +6,7 @@ from django.utils.translation import ugettext_lazy as _
from
django.conf
import
settings
from
django.conf
import
settings
from
django.db.models
import
TextField
from
django.db.models
import
TextField
from
django.db
import
models
from
config_models.models
import
ConfigurationModel
from
config_models.models
import
ConfigurationModel
...
@@ -72,3 +73,70 @@ class XBlockDisableConfig(ConfigurationModel):
...
@@ -72,3 +73,70 @@ class XBlockDisableConfig(ConfigurationModel):
disabled_xblocks
=
config
.
disabled_blocks
,
disabled_xblocks
=
config
.
disabled_blocks
,
disabled_create_block_types
=
config
.
disabled_create_block_types
disabled_create_block_types
=
config
.
disabled_create_block_types
)
)
class
XBlockConfiguration
(
ConfigurationModel
):
"""
XBlock configuration used by both LMS and Studio, and not specific to a particular template.
"""
KEY_FIELDS
=
(
'name'
,)
# xblock name is unique
class
Meta
(
ConfigurationModel
.
Meta
):
app_label
=
'xblock_django'
# boolean field 'enabled' inherited from parent ConfigurationModel
name
=
models
.
CharField
(
max_length
=
255
,
null
=
False
,
db_index
=
True
)
deprecated
=
models
.
BooleanField
(
default
=
False
,
verbose_name
=
_
(
'show deprecation messaging in Studio'
)
)
def
__unicode__
(
self
):
return
(
"XBlockConfiguration(name={}, enabled={}, deprecated={})"
)
.
format
(
self
.
name
,
self
.
enabled
,
self
.
deprecated
)
class
XBlockStudioConfigurationFlag
(
ConfigurationModel
):
"""
Enables site-wide Studio configuration for XBlocks.
"""
class
Meta
(
object
):
app_label
=
"xblock_django"
# boolean field 'enabled' inherited from parent ConfigurationModel
def
__unicode__
(
self
):
return
"XBlockStudioConfigurationFlag(enabled={})"
.
format
(
self
.
enabled
)
class
XBlockStudioConfiguration
(
ConfigurationModel
):
"""
Studio editing configuration for a specific XBlock/template combination.
"""
KEY_FIELDS
=
(
'name'
,
'template'
)
# xblock name/template combination is unique
FULL_SUPPORT
=
'fs'
PROVISIONAL_SUPPORT
=
'ps'
UNSUPPORTED
=
'us'
SUPPORT_CHOICES
=
(
(
FULL_SUPPORT
,
_
(
'Fully Supported'
)),
(
PROVISIONAL_SUPPORT
,
_
(
'Provisionally Supported'
)),
(
UNSUPPORTED
,
_
(
'Unsupported'
))
)
# boolean field 'enabled' inherited from parent ConfigurationModel
name
=
models
.
CharField
(
max_length
=
255
,
null
=
False
,
db_index
=
True
)
template
=
models
.
CharField
(
max_length
=
255
,
blank
=
True
,
default
=
''
)
support_level
=
models
.
CharField
(
max_length
=
2
,
choices
=
SUPPORT_CHOICES
,
default
=
UNSUPPORTED
)
class
Meta
(
object
):
app_label
=
"xblock_django"
def
__unicode__
(
self
):
return
(
"XBlockStudioConfiguration(name={}, template={}, enabled={}, support_level={})"
)
.
format
(
self
.
name
,
self
.
template
,
self
.
enabled
,
self
.
support_level
)
common/djangoapps/xblock_django/tests/test_api.py
0 → 100644
View file @
39cf70ec
"""
Tests related to XBlock support API.
"""
from
xblock_django.models
import
XBlockConfiguration
,
XBlockStudioConfiguration
,
XBlockStudioConfigurationFlag
from
xblock_django.api
import
deprecated_xblocks
,
disabled_xblocks
,
authorable_xblocks
from
openedx.core.djangolib.testing.utils
import
CacheIsolationTestCase
class
XBlockSupportTestCase
(
CacheIsolationTestCase
):
"""
Tests for XBlock Support methods.
"""
def
setUp
(
self
):
super
(
XBlockSupportTestCase
,
self
)
.
setUp
()
# Set up XBlockConfigurations for disabled and deprecated states
block_config
=
[
(
"poll"
,
True
,
True
),
(
"survey"
,
False
,
True
),
(
"done"
,
True
,
False
),
]
for
name
,
enabled
,
deprecated
in
block_config
:
XBlockConfiguration
(
name
=
name
,
enabled
=
enabled
,
deprecated
=
deprecated
)
.
save
()
# Set up XBlockStudioConfigurations for studio support level
studio_block_config
=
[
(
"poll"
,
""
,
False
,
XBlockStudioConfiguration
.
FULL_SUPPORT
),
# FULL_SUPPORT negated by enabled=False
(
"survey"
,
""
,
True
,
XBlockStudioConfiguration
.
UNSUPPORTED
),
(
"done"
,
""
,
True
,
XBlockStudioConfiguration
.
FULL_SUPPORT
),
(
"problem"
,
""
,
True
,
XBlockStudioConfiguration
.
FULL_SUPPORT
),
(
"problem"
,
"multiple_choice"
,
True
,
XBlockStudioConfiguration
.
FULL_SUPPORT
),
(
"problem"
,
"circuit_schematic_builder"
,
True
,
XBlockStudioConfiguration
.
UNSUPPORTED
),
(
"problem"
,
"ora1"
,
False
,
XBlockStudioConfiguration
.
FULL_SUPPORT
),
(
"html"
,
"zoom"
,
True
,
XBlockStudioConfiguration
.
PROVISIONAL_SUPPORT
),
(
"split_module"
,
""
,
True
,
XBlockStudioConfiguration
.
UNSUPPORTED
),
]
for
name
,
template
,
enabled
,
support_level
in
studio_block_config
:
XBlockStudioConfiguration
(
name
=
name
,
template
=
template
,
enabled
=
enabled
,
support_level
=
support_level
)
.
save
()
def
test_deprecated_blocks
(
self
):
""" Tests the deprecated_xblocks method """
deprecated_xblock_names
=
[
block
.
name
for
block
in
deprecated_xblocks
()]
self
.
assertItemsEqual
([
"poll"
,
"survey"
],
deprecated_xblock_names
)
XBlockConfiguration
(
name
=
"poll"
,
enabled
=
True
,
deprecated
=
False
)
.
save
()
deprecated_xblock_names
=
[
block
.
name
for
block
in
deprecated_xblocks
()]
self
.
assertItemsEqual
([
"survey"
],
deprecated_xblock_names
)
def
test_disabled_blocks
(
self
):
""" Tests the disabled_xblocks method """
disabled_xblock_names
=
[
block
.
name
for
block
in
disabled_xblocks
()]
self
.
assertItemsEqual
([
"survey"
],
disabled_xblock_names
)
XBlockConfiguration
(
name
=
"poll"
,
enabled
=
False
,
deprecated
=
True
)
.
save
()
disabled_xblock_names
=
[
block
.
name
for
block
in
disabled_xblocks
()]
self
.
assertItemsEqual
([
"survey"
,
"poll"
],
disabled_xblock_names
)
def
test_authorable_blocks_flag_disabled
(
self
):
"""
Tests authorable_xblocks returns None if the configuration flag is not enabled.
"""
self
.
assertFalse
(
XBlockStudioConfigurationFlag
.
is_enabled
())
self
.
assertIsNone
(
authorable_xblocks
())
def
test_authorable_blocks_empty_model
(
self
):
"""
Tests authorable_xblocks returns an empty list if the configuration flag is enabled but
the XBlockStudioConfiguration table is empty.
"""
XBlockStudioConfigurationFlag
(
enabled
=
True
)
.
save
()
XBlockStudioConfiguration
.
objects
.
all
()
.
delete
()
self
.
assertEqual
(
0
,
len
(
authorable_xblocks
(
allow_unsupported
=
True
)))
def
test_authorable_blocks
(
self
):
"""
Tests authorable_xblocks when configuration flag is enabled and name is not specified.
"""
XBlockStudioConfigurationFlag
(
enabled
=
True
)
.
save
()
authorable_xblock_names
=
[
block
.
name
for
block
in
authorable_xblocks
()]
self
.
assertItemsEqual
([
"done"
,
"problem"
,
"problem"
,
"html"
],
authorable_xblock_names
)
# Note that "survey" is disabled in XBlockConfiguration, but it is still returned by
# authorable_xblocks because it is marked as enabled and unsupported in XBlockStudioConfiguration.
# Since XBlockConfiguration is a blacklist and relates to xblock type, while XBlockStudioConfiguration
# is a whitelist and uses a combination of xblock type and template (and in addition has a global feature flag),
# it is expected that Studio code will need to filter by both disabled_xblocks and authorable_xblocks.
authorable_xblock_names
=
[
block
.
name
for
block
in
authorable_xblocks
(
allow_unsupported
=
True
)]
self
.
assertItemsEqual
(
[
"survey"
,
"done"
,
"problem"
,
"problem"
,
"problem"
,
"html"
,
"split_module"
],
authorable_xblock_names
)
def
test_authorable_blocks_by_name
(
self
):
"""
Tests authorable_xblocks when configuration flag is enabled and name is specified.
"""
def
verify_xblock_fields
(
name
,
template
,
support_level
,
block
):
"""
Verifies the returned xblock state.
"""
self
.
assertEqual
(
name
,
block
.
name
)
self
.
assertEqual
(
template
,
block
.
template
)
self
.
assertEqual
(
support_level
,
block
.
support_level
)
XBlockStudioConfigurationFlag
(
enabled
=
True
)
.
save
()
# There are no xblocks with name video.
authorable_blocks
=
authorable_xblocks
(
name
=
"video"
)
self
.
assertEqual
(
0
,
len
(
authorable_blocks
))
# There is only a single html xblock.
authorable_blocks
=
authorable_xblocks
(
name
=
"html"
)
self
.
assertEqual
(
1
,
len
(
authorable_blocks
))
verify_xblock_fields
(
"html"
,
"zoom"
,
XBlockStudioConfiguration
.
PROVISIONAL_SUPPORT
,
authorable_blocks
[
0
])
authorable_blocks
=
authorable_xblocks
(
name
=
"problem"
,
allow_unsupported
=
True
)
self
.
assertEqual
(
3
,
len
(
authorable_blocks
))
no_template
=
None
circuit
=
None
multiple_choice
=
None
for
block
in
authorable_blocks
:
if
block
.
template
==
''
:
no_template
=
block
elif
block
.
template
==
'circuit_schematic_builder'
:
circuit
=
block
elif
block
.
template
==
'multiple_choice'
:
multiple_choice
=
block
verify_xblock_fields
(
"problem"
,
""
,
XBlockStudioConfiguration
.
FULL_SUPPORT
,
no_template
)
verify_xblock_fields
(
"problem"
,
"circuit_schematic_builder"
,
XBlockStudioConfiguration
.
UNSUPPORTED
,
circuit
)
verify_xblock_fields
(
"problem"
,
"multiple_choice"
,
XBlockStudioConfiguration
.
FULL_SUPPORT
,
multiple_choice
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment