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
d4cc7b8f
Commit
d4cc7b8f
authored
Jul 06, 2016
by
cahrens
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Support level support for Studio xblock creation.
TNL-4670
parent
71bebec5
Hide whitespace changes
Inline
Side-by-side
Showing
25 changed files
with
605 additions
and
243 deletions
+605
-243
cms/djangoapps/contentstore/tests/test_contentstore.py
+0
-1
cms/djangoapps/contentstore/tests/test_course_settings.py
+10
-0
cms/djangoapps/contentstore/views/component.py
+162
-45
cms/djangoapps/contentstore/views/tests/test_item.py
+126
-29
cms/djangoapps/models/settings/course_metadata.py
+7
-0
cms/envs/aws.py
+0
-3
cms/envs/common.py
+0
-13
cms/static/js/models/component_template.js
+3
-1
cms/static/js/spec/models/component_template_spec.js
+2
-1
cms/static/js/spec/views/pages/container_spec.js
+141
-6
cms/static/js/spec_helpers/edit_helpers.js
+9
-5
cms/static/js/views/components/add_xblock_menu.js
+14
-3
cms/static/sass/_build-v1.scss
+1
-1
cms/static/sass/elements/_modules.scss
+37
-3
cms/static/sass/partials/_variables.scss
+10
-6
cms/templates/js/add-xblock-component-menu-problem.underscore
+15
-11
cms/templates/js/add-xblock-component-menu.underscore
+9
-6
cms/templates/js/add-xblock-component-support-legend.underscore
+22
-0
cms/templates/js/add-xblock-component-support-level.underscore
+10
-0
common/djangoapps/xblock_django/api.py
+8
-11
common/djangoapps/xblock_django/models.py
+2
-19
common/djangoapps/xblock_django/tests/test_api.py
+7
-16
common/djangoapps/xblock_django/tests/test_models.py
+0
-58
common/lib/xmodule/xmodule/course_module.py
+10
-1
lms/envs/common.py
+0
-4
No files found.
cms/djangoapps/contentstore/tests/test_contentstore.py
View file @
d4cc7b8f
...
...
@@ -691,7 +691,6 @@ class MiscCourseTests(ContentStoreTestCase):
# Test that malicious code does not appear in html
self
.
assertNotIn
(
malicious_code
,
resp
.
content
)
@patch
(
'django.conf.settings.DEPRECATED_ADVANCED_COMPONENT_TYPES'
,
[])
def
test_advanced_components_in_edit_unit
(
self
):
# This could be made better, but for now let's just assert that we see the advanced modules mentioned in the page
# response HTML
...
...
cms/djangoapps/contentstore/tests/test_course_settings.py
View file @
d4cc7b8f
...
...
@@ -21,6 +21,7 @@ from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
from
openedx.core.djangoapps.models.course_details
import
CourseDetails
from
student.roles
import
CourseInstructorRole
,
CourseStaffRole
from
student.tests.factories
import
UserFactory
from
xblock_django.models
import
XBlockStudioConfigurationFlag
from
xmodule.fields
import
Date
from
xmodule.modulestore
import
ModuleStoreEnum
from
xmodule.modulestore.django
import
modulestore
...
...
@@ -784,6 +785,15 @@ class CourseMetadataEditingTest(CourseTestCase):
)
self
.
assertNotIn
(
'edxnotes'
,
test_model
)
def
test_allow_unsupported_xblocks
(
self
):
"""
allow_unsupported_xblocks is only shown in Advanced Settings if
XBlockStudioConfigurationFlag is enabled.
"""
self
.
assertNotIn
(
'allow_unsupported_xblocks'
,
CourseMetadata
.
fetch
(
self
.
fullcourse
))
XBlockStudioConfigurationFlag
(
enabled
=
True
)
.
save
()
self
.
assertIn
(
'allow_unsupported_xblocks'
,
CourseMetadata
.
fetch
(
self
.
fullcourse
))
def
test_validate_from_json_correct_inputs
(
self
):
is_valid
,
errors
,
test_model
=
CourseMetadata
.
validate_and_update_from_json
(
self
.
course
,
...
...
cms/djangoapps/contentstore/views/component.py
View file @
d4cc7b8f
...
...
@@ -27,7 +27,10 @@ from opaque_keys.edx.keys import UsageKey
from
student.auth
import
has_course_author_access
from
django.utils.translation
import
ugettext
as
_
from
xblock_django.models
import
XBlockDisableConfig
from
xblock_django.api
import
disabled_xblocks
,
authorable_xblocks
from
xblock_django.models
import
XBlockStudioConfigurationFlag
__all__
=
[
'container_handler'
,
...
...
@@ -47,17 +50,41 @@ CONTAINER_TEMPLATES = [
"basic-modal"
,
"modal-button"
,
"edit-xblock-modal"
,
"editor-mode-button"
,
"upload-dialog"
,
"add-xblock-component"
,
"add-xblock-component-button"
,
"add-xblock-component-menu"
,
"add-xblock-component-menu-problem"
,
"xblock-string-field-editor"
,
"publish-xblock"
,
"publish-history"
,
"add-xblock-component-support-legend"
,
"add-xblock-component-support-level"
,
"add-xblock-component-menu-problem"
,
"xblock-string-field-editor"
,
"publish-xblock"
,
"publish-history"
,
"unit-outline"
,
"container-message"
,
"license-selector"
,
]
def
_advanced_component_types
():
def
_advanced_component_types
(
show_unsupported
):
"""
Return advanced component types which can be created.
Args:
show_unsupported: if True, unsupported XBlocks may be included in the return value
Returns:
A dict of authorable XBlock types and their support levels (see XBlockStudioConfiguration). For example:
{
"done": "us", # unsupported
"discussion: "fs" # fully supported
}
Note that the support level will be "True" for all XBlocks if XBlockStudioConfigurationFlag
is not enabled.
"""
disabled_create_block_types
=
XBlockDisableConfig
.
disabled_create_block_types
()
return
[
c_type
for
c_type
in
ADVANCED_COMPONENT_TYPES
if
c_type
not
in
disabled_create_block_types
]
enabled_block_types
=
_filter_disabled_blocks
(
ADVANCED_COMPONENT_TYPES
)
if
XBlockStudioConfigurationFlag
.
is_enabled
():
authorable_blocks
=
authorable_xblocks
(
allow_unsupported
=
show_unsupported
)
filtered_blocks
=
{}
for
block
in
authorable_blocks
:
if
block
.
name
in
enabled_block_types
:
filtered_blocks
[
block
.
name
]
=
block
.
support_level
return
filtered_blocks
else
:
all_blocks
=
{}
for
block_name
in
enabled_block_types
:
all_blocks
[
block_name
]
=
True
return
all_blocks
def
_load_mixed_class
(
category
):
...
...
@@ -152,13 +179,14 @@ def get_component_templates(courselike, library=False):
"""
Returns the applicable component templates that can be used by the specified course or library.
"""
def
create_template_dict
(
name
,
cat
,
boilerplate_name
=
None
,
tab
=
"common"
,
hinted
=
False
):
def
create_template_dict
(
name
,
cat
egory
,
support_level
,
boilerplate_name
=
None
,
tab
=
"common"
,
hinted
=
False
):
"""
Creates a component template dict.
Parameters
display_name: the user-visible name of the component
category: the type of component (problem, html, etc.)
support_level: the support level of this component
boilerplate_name: name of boilerplate for filling in default values. May be None.
hinted: True if hinted problem else False
tab: common(default)/advanced, which tab it goes in
...
...
@@ -166,10 +194,50 @@ def get_component_templates(courselike, library=False):
"""
return
{
"display_name"
:
name
,
"category"
:
cat
,
"category"
:
cat
egory
,
"boilerplate_name"
:
boilerplate_name
,
"hinted"
:
hinted
,
"tab"
:
tab
"tab"
:
tab
,
"support_level"
:
support_level
}
def
component_support_level
(
editable_types
,
name
,
template
=
None
):
"""
Returns the support level for the given xblock name/template combination.
Args:
editable_types: a QuerySet of xblocks with their support levels
name: the name of the xblock
template: optional template for the xblock
Returns:
If XBlockStudioConfigurationFlag is enabled, returns the support level
(see XBlockStudioConfiguration) or False if this xblock name/template combination
has no Studio support at all. If XBlockStudioConfigurationFlag is disabled,
simply returns True.
"""
# If the Studio support feature is disabled, return True for all.
if
not
XBlockStudioConfigurationFlag
.
is_enabled
():
return
True
if
template
is
None
:
template
=
""
extension_index
=
template
.
rfind
(
".yaml"
)
if
extension_index
>=
0
:
template
=
template
[
0
:
extension_index
]
for
block
in
editable_types
:
if
block
.
name
==
name
and
block
.
template
==
template
:
return
block
.
support_level
return
False
def
create_support_legend_dict
():
"""
Returns a dict of settings information for the display of the support level legend.
"""
return
{
"show_legend"
:
XBlockStudioConfigurationFlag
.
is_enabled
(),
"allow_unsupported_xblocks"
:
allow_unsupported
,
"documentation_label"
:
_
(
"{platform_name} Support Levels:"
)
.
format
(
platform_name
=
settings
.
PLATFORM_NAME
)
}
component_display_names
=
{
...
...
@@ -189,57 +257,92 @@ def get_component_templates(courselike, library=False):
if
library
:
component_types
=
[
component
for
component
in
component_types
if
component
!=
'discussion'
]
component_types
=
_filter_disabled_blocks
(
component_types
)
# Content Libraries currently don't allow opting in to unsupported xblocks/problem types.
allow_unsupported
=
getattr
(
courselike
,
"allow_unsupported_xblocks"
,
False
)
for
category
in
component_types
:
authorable_variations
=
authorable_xblocks
(
allow_unsupported
=
allow_unsupported
,
name
=
category
)
support_level_without_template
=
component_support_level
(
authorable_variations
,
category
)
templates_for_category
=
[]
component_class
=
_load_mixed_class
(
category
)
# add the default template with localized display name
# TODO: Once mixins are defined per-application, rather than per-runtime,
# this should use a cms mixed-in class. (cpennington)
display_name
=
xblock_type_display_name
(
category
,
_
(
'Blank'
))
# this is the Blank Advanced problem
templates_for_category
.
append
(
create_template_dict
(
display_name
,
category
,
None
,
'advanced'
))
categories
.
add
(
category
)
if
support_level_without_template
:
# add the default template with localized display name
# TODO: Once mixins are defined per-application, rather than per-runtime,
# this should use a cms mixed-in class. (cpennington)
display_name
=
xblock_type_display_name
(
category
,
_
(
'Blank'
))
# this is the Blank Advanced problem
templates_for_category
.
append
(
create_template_dict
(
display_name
,
category
,
support_level_without_template
,
None
,
'advanced'
)
)
categories
.
add
(
category
)
# add boilerplates
if
hasattr
(
component_class
,
'templates'
):
for
template
in
component_class
.
templates
():
filter_templates
=
getattr
(
component_class
,
'filter_templates'
,
None
)
if
not
filter_templates
or
filter_templates
(
template
,
courselike
):
# Tab can be 'common' 'advanced'
# Default setting is common/advanced depending on the presence of markdown
tab
=
'common'
if
template
[
'metadata'
]
.
get
(
'markdown'
)
is
None
:
tab
=
'advanced'
hinted
=
template
.
get
(
'hinted'
,
False
)
templates_for_category
.
append
(
create_template_dict
(
_
(
template
[
'metadata'
]
.
get
(
'display_name'
)),
# pylint: disable=translation-of-non-string
category
,
template
.
get
(
'template_id'
),
tab
,
hinted
,
)
template_id
=
template
.
get
(
'template_id'
)
support_level_with_template
=
component_support_level
(
authorable_variations
,
category
,
template_id
)
if
support_level_with_template
:
# Tab can be 'common' 'advanced'
# Default setting is common/advanced depending on the presence of markdown
tab
=
'common'
if
template
[
'metadata'
]
.
get
(
'markdown'
)
is
None
:
tab
=
'advanced'
hinted
=
template
.
get
(
'hinted'
,
False
)
templates_for_category
.
append
(
create_template_dict
(
_
(
template
[
'metadata'
]
.
get
(
'display_name'
)),
# pylint: disable=translation-of-non-string
category
,
support_level_with_template
,
template_id
,
tab
,
hinted
,
)
)
# Add any advanced problem types
# Add any advanced problem types
. Note that these are different xblocks being stored as Advanced Problems.
if
category
==
'problem'
:
for
advanced_problem_type
in
ADVANCED_PROBLEM_TYPES
:
disabled_block_names
=
[
block
.
name
for
block
in
disabled_xblocks
()]
advanced_problem_types
=
[
advanced_problem_type
for
advanced_problem_type
in
ADVANCED_PROBLEM_TYPES
if
advanced_problem_type
[
'component'
]
not
in
disabled_block_names
]
for
advanced_problem_type
in
advanced_problem_types
:
component
=
advanced_problem_type
[
'component'
]
boilerplate_name
=
advanced_problem_type
[
'boilerplate_name'
]
try
:
component_display_name
=
xblock_type_display_name
(
component
)
except
PluginMissingError
:
log
.
warning
(
'Unable to load xblock type
%
s to read display_name'
,
component
,
exc_info
=
True
)
else
:
templates_for_category
.
append
(
create_template_dict
(
component_display_name
,
component
,
boilerplate_name
,
'advanced'
)
)
categories
.
add
(
component
)
authorable_advanced_component_variations
=
authorable_xblocks
(
allow_unsupported
=
allow_unsupported
,
name
=
component
)
advanced_component_support_level
=
component_support_level
(
authorable_advanced_component_variations
,
component
,
boilerplate_name
)
if
advanced_component_support_level
:
try
:
component_display_name
=
xblock_type_display_name
(
component
)
except
PluginMissingError
:
log
.
warning
(
'Unable to load xblock type
%
s to read display_name'
,
component
,
exc_info
=
True
)
else
:
templates_for_category
.
append
(
create_template_dict
(
component_display_name
,
component
,
advanced_component_support_level
,
boilerplate_name
,
'advanced'
)
)
categories
.
add
(
component
)
component_templates
.
append
({
"type"
:
category
,
"templates"
:
templates_for_category
,
"display_name"
:
component_display_names
[
category
]
"display_name"
:
component_display_names
[
category
],
"support_legend"
:
create_support_legend_dict
()
})
# Libraries do not support advanced components at this time.
...
...
@@ -251,19 +354,25 @@ def get_component_templates(courselike, library=False):
# are the names of the modules in ADVANCED_COMPONENT_TYPES that should be
# enabled for the course.
course_advanced_keys
=
courselike
.
advanced_modules
advanced_component_templates
=
{
"type"
:
"advanced"
,
"templates"
:
[],
"display_name"
:
_
(
"Advanced"
)}
advanced_component_types
=
_advanced_component_types
()
advanced_component_templates
=
{
"type"
:
"advanced"
,
"templates"
:
[],
"display_name"
:
_
(
"Advanced"
),
"support_legend"
:
create_support_legend_dict
()
}
advanced_component_types
=
_advanced_component_types
(
allow_unsupported
)
# Set component types according to course policy file
if
isinstance
(
course_advanced_keys
,
list
):
for
category
in
course_advanced_keys
:
if
category
in
advanced_component_types
and
category
not
in
categories
:
if
category
in
advanced_component_types
.
keys
()
and
category
not
in
categories
:
# boilerplates not supported for advanced components
try
:
component_display_name
=
xblock_type_display_name
(
category
,
default_display_name
=
category
)
advanced_component_templates
[
'templates'
]
.
append
(
create_template_dict
(
component_display_name
,
category
category
,
advanced_component_types
[
category
]
)
)
categories
.
add
(
category
)
...
...
@@ -288,6 +397,14 @@ def get_component_templates(courselike, library=False):
return
component_templates
def
_filter_disabled_blocks
(
all_blocks
):
"""
Filter out disabled xblocks from the provided list of xblock names.
"""
disabled_block_names
=
[
block
.
name
for
block
in
disabled_xblocks
()]
return
[
block_name
for
block_name
in
all_blocks
if
block_name
not
in
disabled_block_names
]
@login_required
def
_get_item_in_course
(
request
,
usage_key
):
"""
...
...
cms/djangoapps/contentstore/views/tests/test_item.py
View file @
d4cc7b8f
...
...
@@ -24,7 +24,7 @@ from contentstore.views.item import (
)
from
contentstore.tests.utils
import
CourseTestCase
from
student.tests.factories
import
UserFactory
from
xblock_django.models
import
XBlock
DisableConfi
g
from
xblock_django.models
import
XBlock
Configuration
,
XBlockStudioConfiguration
,
XBlockStudioConfigurationFla
g
from
xmodule.capa_module
import
CapaDescriptor
from
xmodule.modulestore
import
ModuleStoreEnum
from
xmodule.modulestore.django
import
modulestore
...
...
@@ -1457,12 +1457,19 @@ class TestComponentTemplates(CourseTestCase):
def
setUp
(
self
):
super
(
TestComponentTemplates
,
self
)
.
setUp
()
self
.
templates
=
get_component_templates
(
self
.
course
)
# Advanced Module support levels.
XBlockStudioConfiguration
.
objects
.
create
(
name
=
'poll'
,
enabled
=
True
,
support_level
=
"fs"
)
XBlockStudioConfiguration
.
objects
.
create
(
name
=
'survey'
,
enabled
=
True
,
support_level
=
"ps"
)
XBlockStudioConfiguration
.
objects
.
create
(
name
=
'annotatable'
,
enabled
=
True
,
support_level
=
"us"
)
# Basic component support levels.
XBlockStudioConfiguration
.
objects
.
create
(
name
=
'html'
,
enabled
=
True
,
support_level
=
"fs"
)
XBlockStudioConfiguration
.
objects
.
create
(
name
=
'discussion'
,
enabled
=
True
,
support_level
=
"ps"
)
XBlockStudioConfiguration
.
objects
.
create
(
name
=
'problem'
,
enabled
=
True
,
support_level
=
"us"
)
XBlockStudioConfiguration
.
objects
.
create
(
name
=
'video'
,
enabled
=
True
,
support_level
=
"us"
)
# XBlock masquerading as a problem
XBlockStudioConfiguration
.
objects
.
create
(
name
=
'openassessment'
,
enabled
=
True
,
support_level
=
"us"
)
# Initialize the deprecated modules settings with empty list
XBlockDisableConfig
.
objects
.
create
(
disabled_create_blocks
=
''
,
enabled
=
True
)
self
.
templates
=
get_component_templates
(
self
.
course
)
def
get_templates_of_type
(
self
,
template_type
):
"""
...
...
@@ -1481,12 +1488,40 @@ class TestComponentTemplates(CourseTestCase):
"""
Test the handling of the basic component templates.
"""
self
.
assertIsNotNone
(
self
.
get_templates_of_type
(
'discussion'
)
)
self
.
assertIsNotNone
(
self
.
get_templates_of_type
(
'html'
)
)
self
.
assert
IsNotNone
(
self
.
get_templates_of_type
(
'problem'
)
)
self
.
assert
IsNotNone
(
self
.
get_templates_of_type
(
'video'
)
)
self
.
_verify_basic_component
(
"discussion"
,
"Discussion"
)
self
.
_verify_basic_component
(
"video"
,
"Video"
)
self
.
assert
Greater
(
self
.
get_templates_of_type
(
'html'
),
0
)
self
.
assert
Greater
(
self
.
get_templates_of_type
(
'problem'
),
0
)
self
.
assertIsNone
(
self
.
get_templates_of_type
(
'advanced'
))
# Now fully disable video through XBlockConfiguration
XBlockConfiguration
.
objects
.
create
(
name
=
'video'
,
enabled
=
False
)
self
.
templates
=
get_component_templates
(
self
.
course
)
self
.
assertIsNone
(
self
.
get_templates_of_type
(
'video'
))
def
test_basic_components_support_levels
(
self
):
"""
Test that support levels can be set on basic component templates.
"""
XBlockStudioConfigurationFlag
.
objects
.
create
(
enabled
=
True
)
self
.
templates
=
get_component_templates
(
self
.
course
)
self
.
_verify_basic_component
(
"discussion"
,
"Discussion"
,
"ps"
)
self
.
assertEqual
([],
self
.
get_templates_of_type
(
"video"
))
self
.
assertEqual
([],
self
.
get_templates_of_type
(
"problem"
))
self
.
course
.
allow_unsupported_xblocks
=
True
self
.
templates
=
get_component_templates
(
self
.
course
)
self
.
_verify_basic_component
(
"video"
,
"Video"
,
"us"
)
problem_templates
=
self
.
get_templates_of_type
(
'problem'
)
problem_no_boilerplate
=
self
.
get_template
(
problem_templates
,
u'Blank Advanced Problem'
)
self
.
assertIsNotNone
(
problem_no_boilerplate
)
self
.
assertEqual
(
'us'
,
problem_no_boilerplate
[
'support_level'
])
# Now fully disable video through XBlockConfiguration
XBlockConfiguration
.
objects
.
create
(
name
=
'video'
,
enabled
=
False
)
self
.
templates
=
get_component_templates
(
self
.
course
)
self
.
assertIsNone
(
self
.
get_templates_of_type
(
'video'
))
def
test_advanced_components
(
self
):
"""
Test the handling of advanced component templates.
...
...
@@ -1510,6 +1545,11 @@ class TestComponentTemplates(CourseTestCase):
self
.
assertNotEqual
(
only_template
.
get
(
'category'
),
'video'
)
self
.
assertNotEqual
(
only_template
.
get
(
'category'
),
'openassessment'
)
# Now fully disable word_cloud through XBlockConfiguration
XBlockConfiguration
.
objects
.
create
(
name
=
'word_cloud'
,
enabled
=
False
)
self
.
templates
=
get_component_templates
(
self
.
course
)
self
.
assertIsNone
(
self
.
get_templates_of_type
(
'advanced'
))
def
test_advanced_problems
(
self
):
"""
Test the handling of advanced problem templates.
...
...
@@ -1520,44 +1560,101 @@ class TestComponentTemplates(CourseTestCase):
self
.
assertEqual
(
circuit_template
.
get
(
'category'
),
'problem'
)
self
.
assertEqual
(
circuit_template
.
get
(
'boilerplate_name'
),
'circuitschematic.yaml'
)
@patch
(
'django.conf.settings.DEPRECATED_ADVANCED_COMPONENT_TYPES'
,
[])
def
test_deprecated_no_advance_component_button
(
self
):
"""
Test that there will be no `Advanced` button on unit page if
units are
deprecated provided
that they are the only modules in `Advanced Module List`
Test that there will be no `Advanced` button on unit page if
xblocks have disabled
Studio support given
that they are the only modules in `Advanced Module List`
"""
XBlockDisableConfig
.
objects
.
create
(
disabled_create_blocks
=
'poll survey'
,
enabled
=
True
)
# Update poll and survey to have "enabled=False".
XBlockStudioConfiguration
.
objects
.
create
(
name
=
'poll'
,
enabled
=
False
,
support_level
=
"fs"
)
XBlockStudioConfiguration
.
objects
.
create
(
name
=
'survey'
,
enabled
=
False
,
support_level
=
"fs"
)
XBlockStudioConfigurationFlag
.
objects
.
create
(
enabled
=
True
)
self
.
course
.
advanced_modules
.
extend
([
'poll'
,
'survey'
])
templates
=
get_component_templates
(
self
.
course
)
button_names
=
[
template
[
'display_name'
]
for
template
in
templates
]
self
.
assertNotIn
(
'Advanced'
,
button_names
)
@patch
(
'django.conf.settings.DEPRECATED_ADVANCED_COMPONENT_TYPES'
,
[])
def
test_cannot_create_deprecated_problems
(
self
):
"""
Test that
we can't create problems if they are deprecated
Test that
xblocks that have Studio support disabled do not show on the "new component" menu.
"""
XBlockDisableConfig
.
objects
.
create
(
disabled_create_blocks
=
'poll survey'
,
enabled
=
True
)
# Update poll to have "enabled=False".
XBlockStudioConfiguration
.
objects
.
create
(
name
=
'poll'
,
enabled
=
False
,
support_level
=
"fs"
)
XBlockStudioConfigurationFlag
.
objects
.
create
(
enabled
=
True
)
self
.
course
.
advanced_modules
.
extend
([
'annotatable'
,
'poll'
,
'survey'
])
templates
=
get_component_templates
(
self
.
course
)
button_names
=
[
template
[
'display_name'
]
for
template
in
templates
]
self
.
assertIn
(
'Advanced'
,
button_names
)
self
.
assertEqual
(
len
(
templates
[
0
][
'templates'
]),
1
)
template_display_names
=
[
template
[
'display_name'
]
for
template
in
templates
[
0
][
'templates'
]]
self
.
assertEqual
(
template_display_names
,
[
'Annotation
'
])
# Annotatable doesn't show up because it is unsupported (in test setUp).
self
.
_verify_advanced_xblocks
([
'Survey'
],
[
'ps'
])
# Now enable unsupported components.
self
.
course
.
allow_unsupported_xblocks
=
True
self
.
_verify_advanced_xblocks
([
'Annotation'
,
'Survey'
],
[
'us'
,
'ps
'
])
@patch
(
'django.conf.settings.DEPRECATED_ADVANCED_COMPONENT_TYPES'
,
[
'poll'
])
def
test_create_non_deprecated_problems
(
self
):
# Now disable Annotatable completely through XBlockConfiguration
XBlockConfiguration
.
objects
.
create
(
name
=
'annotatable'
,
enabled
=
False
)
self
.
_verify_advanced_xblocks
([
'Survey'
],
[
'ps'
])
def
test_create_support_level_flag_off
(
self
):
"""
Test that we can create problems if they are not deprecated
Test that we can create any advanced xblock (that isn't completely disabled through
XBlockConfiguration) if XBlockStudioConfigurationFlag is False.
"""
XBlockStudioConfigurationFlag
.
objects
.
create
(
enabled
=
False
)
self
.
course
.
advanced_modules
.
extend
([
'annotatable'
,
'survey'
])
self
.
_verify_advanced_xblocks
([
'Annotation'
,
'Survey'
],
[
True
,
True
])
def
test_xblock_masquerading_as_problem
(
self
):
"""
Test the integration of xblocks masquerading as problems.
"""
def
get_openassessment
():
""" Helper method to return the openassessment template from problem list """
self
.
templates
=
get_component_templates
(
self
.
course
)
problem_templates
=
self
.
get_templates_of_type
(
'problem'
)
return
self
.
get_template
(
problem_templates
,
u'Peer Assessment'
)
def
verify_openassessment_present
(
support_level
):
""" Helper method to verify that openassessment template is present """
openassessment
=
get_openassessment
()
self
.
assertIsNotNone
(
openassessment
)
self
.
assertEqual
(
openassessment
.
get
(
'category'
),
'openassessment'
)
self
.
assertEqual
(
openassessment
.
get
(
'support_level'
),
support_level
)
verify_openassessment_present
(
True
)
# Now enable XBlockStudioConfigurationFlag. The openassessment block is marked
# unsupported, so will no longer show up.
XBlockStudioConfigurationFlag
.
objects
.
create
(
enabled
=
True
)
self
.
assertIsNone
(
get_openassessment
())
# Now allow unsupported components.
self
.
course
.
allow_unsupported_xblocks
=
True
verify_openassessment_present
(
'us'
)
# Now disable openassessment completely through XBlockConfiguration
XBlockConfiguration
.
objects
.
create
(
name
=
'openassessment'
,
enabled
=
False
)
self
.
assertIsNone
(
get_openassessment
())
def
_verify_advanced_xblocks
(
self
,
expected_xblocks
,
expected_support_levels
):
"""
Verify the names of the advanced xblocks showing in the "new component" menu.
"""
self
.
course
.
advanced_modules
.
extend
([
'annotatable'
,
'poll'
,
'survey'
])
templates
=
get_component_templates
(
self
.
course
)
button_names
=
[
template
[
'display_name'
]
for
template
in
templates
]
self
.
assertIn
(
'Advanced'
,
button_names
)
self
.
assertEqual
(
len
(
templates
[
0
][
'templates'
]),
2
)
self
.
assertEqual
(
len
(
templates
[
0
][
'templates'
]),
len
(
expected_xblocks
)
)
template_display_names
=
[
template
[
'display_name'
]
for
template
in
templates
[
0
][
'templates'
]]
self
.
assertEqual
(
template_display_names
,
[
'Annotation'
,
'Survey'
])
self
.
assertEqual
(
template_display_names
,
expected_xblocks
)
template_support_levels
=
[
template
[
'support_level'
]
for
template
in
templates
[
0
][
'templates'
]]
self
.
assertEqual
(
template_support_levels
,
expected_support_levels
)
def
_verify_basic_component
(
self
,
component_type
,
display_name
,
support_level
=
True
):
"""
Verify the display name and support level of basic components (that have no boilerplates).
"""
templates
=
self
.
get_templates_of_type
(
component_type
)
self
.
assertEqual
(
1
,
len
(
templates
))
self
.
assertEqual
(
display_name
,
templates
[
0
][
'display_name'
])
self
.
assertEqual
(
support_level
,
templates
[
0
][
'support_level'
])
@ddt.ddt
...
...
cms/djangoapps/models/settings/course_metadata.py
View file @
d4cc7b8f
...
...
@@ -2,7 +2,9 @@
Django module for Course Metadata class -- manages advanced settings and related parameters
"""
from
xblock.fields
import
Scope
from
xblock_django.models
import
XBlockStudioConfigurationFlag
from
xmodule.modulestore.django
import
modulestore
from
django.utils.translation
import
ugettext
as
_
from
django.conf
import
settings
...
...
@@ -93,6 +95,11 @@ class CourseMetadata(object):
filtered_list
.
append
(
'enable_ccx'
)
filtered_list
.
append
(
'ccx_connector'
)
# If the XBlockStudioConfiguration table is not being used, there is no need to
# display the "Allow Unsupported XBlocks" setting.
if
not
XBlockStudioConfigurationFlag
.
is_enabled
():
filtered_list
.
append
(
'allow_unsupported_xblocks'
)
return
filtered_list
@classmethod
...
...
cms/envs/aws.py
View file @
d4cc7b8f
...
...
@@ -398,9 +398,6 @@ ADVANCED_SECURITY_CONFIG = ENV_TOKENS.get('ADVANCED_SECURITY_CONFIG', {})
################ ADVANCED COMPONENT/PROBLEM TYPES ###############
ADVANCED_PROBLEM_TYPES
=
ENV_TOKENS
.
get
(
'ADVANCED_PROBLEM_TYPES'
,
ADVANCED_PROBLEM_TYPES
)
DEPRECATED_ADVANCED_COMPONENT_TYPES
=
ENV_TOKENS
.
get
(
'DEPRECATED_ADVANCED_COMPONENT_TYPES'
,
DEPRECATED_ADVANCED_COMPONENT_TYPES
)
################ VIDEO UPLOAD PIPELINE ###############
...
...
cms/envs/common.py
View file @
d4cc7b8f
...
...
@@ -1132,19 +1132,6 @@ XBLOCK_SETTINGS = {
}
}
################################ XBlock Deprecation ################################
# The following settings are used for deprecating XBlocks.
# Adding components in this list will disable the creation of new problems for
# those advanced components in Studio. Existing problems will work fine
# and one can edit them in Studio.
# DEPRECATED. Please use /admin/xblock_django/xblockdisableconfig instead.
DEPRECATED_ADVANCED_COMPONENT_TYPES
=
[]
# XBlocks can be disabled from rendering in LMS Courseware by adding them to
# /admin/xblock_django/xblockdisableconfig/.
################################ Settings for Credit Course Requirements ################################
# Initial delay used for retrying tasks.
# Additional retries use longer delays.
...
...
cms/static/js/models/component_template.js
View file @
d4cc7b8f
...
...
@@ -10,7 +10,8 @@ define(["backbone"], function (Backbone) {
// category (may or may not match "type")
// boilerplate_name (may be null)
// is_common (only used for problems)
templates
:
[]
templates
:
[],
support_legend
:
{}
},
parse
:
function
(
response
)
{
// Returns true only for templates that both have no boilerplate and are of
...
...
@@ -24,6 +25,7 @@ define(["backbone"], function (Backbone) {
this
.
type
=
response
.
type
;
this
.
templates
=
response
.
templates
;
this
.
display_name
=
response
.
display_name
;
this
.
support_legend
=
response
.
support_legend
;
// Sort the templates.
this
.
templates
.
sort
(
function
(
a
,
b
)
{
...
...
cms/static/js/spec/models/component_template_spec.js
View file @
d4cc7b8f
...
...
@@ -49,7 +49,8 @@ define(["js/models/component_template"],
"boilerplate_name"
:
"alternate_word_cloud.yaml"
,
"display_name"
:
"Word Cloud"
}],
"type"
:
"problem"
"type"
:
"problem"
,
"support_legend"
:
{
"show_legend"
:
false
}
};
it
(
'orders templates correctly'
,
function
()
{
...
...
cms/static/js/spec/views/pages/container_spec.js
View file @
d4cc7b8f
define
([
"jquery"
,
"underscore"
,
"underscore.string"
,
"edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers"
,
"common/js/spec_helpers/template_helpers"
,
"js/spec_helpers/edit_helpers"
,
"js/views/pages/container"
,
"js/views/pages/paged_container"
,
"js/models/xblock_info"
,
"jquery.simulate"
],
function
(
$
,
_
,
str
,
AjaxHelpers
,
TemplateHelpers
,
EditHelpers
,
ContainerPage
,
PagedContainerPage
,
XBlockInfo
)
{
"js/views/pages/container"
,
"js/views/pages/paged_container"
,
"js/models/xblock_info"
,
"js/collections/component_template"
,
"jquery.simulate"
],
function
(
$
,
_
,
str
,
AjaxHelpers
,
TemplateHelpers
,
EditHelpers
,
ContainerPage
,
PagedContainerPage
,
XBlockInfo
,
ComponentTemplates
)
{
'use strict'
;
function
parameterized_suite
(
label
,
globalPageOptions
)
{
...
...
@@ -55,18 +57,19 @@ define(["jquery", "underscore", "underscore.string", "edx-ui-toolkit/js/utils/sp
);
};
getContainerPage
=
function
(
options
)
{
getContainerPage
=
function
(
options
,
componentTemplates
)
{
var
default_options
=
{
model
:
model
,
templates
:
EditHelpers
.
mockComponentTemplates
,
templates
:
componentTemplates
===
undefined
?
EditHelpers
.
mockComponentTemplates
:
componentTemplates
,
el
:
$
(
'#content'
)
};
return
new
PageClass
(
_
.
extend
(
options
||
{},
globalPageOptions
,
default_options
));
};
renderContainerPage
=
function
(
test
,
html
,
options
)
{
renderContainerPage
=
function
(
test
,
html
,
options
,
componentTemplates
)
{
requests
=
AjaxHelpers
.
requests
(
test
);
containerPage
=
getContainerPage
(
options
);
containerPage
=
getContainerPage
(
options
,
componentTemplates
);
containerPage
.
render
();
respondWithHtml
(
html
);
AjaxHelpers
.
expectJsonRequest
(
requests
,
'GET'
,
'/xblock/locator-container'
);
...
...
@@ -652,6 +655,138 @@ define(["jquery", "underscore", "underscore.string", "edx-ui-toolkit/js/utils/sp
"parent_locator"
:
"locator-group-A"
});
});
it
(
'does not show the support legend if show_legend is false'
,
function
()
{
// By default, show_legend is false in the mock component Templates.
renderContainerPage
(
this
,
mockContainerXBlockHtml
);
showTemplatePicker
();
expect
(
containerPage
.
$
(
'.support-documentation'
).
length
).
toBe
(
0
);
});
it
(
'does show the support legend if show_legend is true'
,
function
()
{
var
templates
=
new
ComponentTemplates
([
{
"templates"
:
[
{
"category"
:
"html"
,
"boilerplate_name"
:
null
,
"display_name"
:
"Text"
},
{
"category"
:
"html"
,
"boilerplate_name"
:
"announcement.yaml"
,
"display_name"
:
"Announcement"
},
{
"category"
:
"html"
,
"boilerplate_name"
:
"raw.yaml"
,
"display_name"
:
"Raw HTML"
}],
"type"
:
"html"
,
"support_legend"
:
{
"show_legend"
:
true
,
"documentation_label"
:
"Documentation Label:"
,
"allow_unsupported_xblocks"
:
false
}
}],
{
parse
:
true
}),
supportDocumentation
;
renderContainerPage
(
this
,
mockContainerXBlockHtml
,
{},
templates
);
showTemplatePicker
();
supportDocumentation
=
containerPage
.
$
(
'.support-documentation'
);
// On this page, groups are being shown, each of which has a new component menu.
expect
(
supportDocumentation
.
length
).
toBeGreaterThan
(
0
);
// check that the documentation label is displayed
expect
(
$
(
supportDocumentation
[
0
]).
find
(
'.support-documentation-link'
).
text
().
trim
())
.
toBe
(
'Documentation Label:'
);
// show_unsupported_xblocks is false, so only 2 support levels should be shown
expect
(
$
(
supportDocumentation
[
0
]).
find
(
'.support-documentation-level'
).
length
).
toBe
(
2
);
});
it
(
'does show unsupported level if enabled'
,
function
()
{
var
templates
=
new
ComponentTemplates
([
{
"templates"
:
[
{
"category"
:
"html"
,
"boilerplate_name"
:
null
,
"display_name"
:
"Text"
},
{
"category"
:
"html"
,
"boilerplate_name"
:
"announcement.yaml"
,
"display_name"
:
"Announcement"
},
{
"category"
:
"html"
,
"boilerplate_name"
:
"raw.yaml"
,
"display_name"
:
"Raw HTML"
}],
"type"
:
"html"
,
"support_legend"
:
{
"show_legend"
:
true
,
"documentation_label"
:
"Documentation Label:"
,
"allow_unsupported_xblocks"
:
true
}
}],
{
parse
:
true
}),
supportDocumentation
;
renderContainerPage
(
this
,
mockContainerXBlockHtml
,
{},
templates
);
showTemplatePicker
();
supportDocumentation
=
containerPage
.
$
(
'.support-documentation'
);
// show_unsupported_xblocks is true, so 3 support levels should be shown
expect
(
$
(
supportDocumentation
[
0
]).
find
(
'.support-documentation-level'
).
length
).
toBe
(
3
);
// verify only one has the unsupported item
expect
(
$
(
supportDocumentation
[
0
]).
find
(
'.fa-circle-o'
).
length
).
toBe
(
1
);
});
it
(
'does render support level indicators if present in JSON'
,
function
()
{
var
templates
=
new
ComponentTemplates
([
{
"templates"
:
[
{
"category"
:
"html"
,
"boilerplate_name"
:
null
,
"display_name"
:
"Text"
,
"support_level"
:
"fs"
},
{
"category"
:
"html"
,
"boilerplate_name"
:
"announcement.yaml"
,
"display_name"
:
"Announcement"
,
"support_level"
:
"ps"
},
{
"category"
:
"html"
,
"boilerplate_name"
:
"raw.yaml"
,
"display_name"
:
"Raw HTML"
,
"support_level"
:
"us"
}],
"type"
:
"html"
,
"support_legend"
:
{
"show_legend"
:
true
,
"documentation_label"
:
"Documentation Label:"
,
"allow_unsupported_xblocks"
:
true
}
}],
{
parse
:
true
}),
supportLevelIndicators
,
getScreenReaderText
;
renderContainerPage
(
this
,
mockContainerXBlockHtml
,
{},
templates
);
showTemplatePicker
();
supportLevelIndicators
=
$
(
containerPage
.
$
(
'.new-component-template'
)[
0
])
.
find
(
'.support-level'
);
expect
(
supportLevelIndicators
.
length
).
toBe
(
3
);
getScreenReaderText
=
function
(
index
){
return
$
(
$
(
supportLevelIndicators
[
index
]).
siblings
()[
0
]).
text
().
trim
();
};
// Verify one level of each type was rendered.
expect
(
getScreenReaderText
(
0
)).
toBe
(
'Fully Supported'
);
expect
(
getScreenReaderText
(
1
)).
toBe
(
'Provisionally Supported'
);
expect
(
getScreenReaderText
(
2
)).
toBe
(
'Not Supported'
);
});
});
});
});
...
...
cms/static/js/spec_helpers/edit_helpers.js
View file @
d4cc7b8f
...
...
@@ -41,12 +41,13 @@ define(["jquery", "underscore", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
mockComponentTemplates
=
new
ComponentTemplates
([
{
templates
:
[
"templates"
:
[
{
category
:
'discussion'
,
display_name
:
'Discussion'
"category"
:
"discussion"
,
"display_name"
:
"Discussion"
}],
type
:
'discussion'
"type"
:
"discussion"
,
"support_legend"
:
{
"show_legend"
:
false
}
},
{
"templates"
:
[
{
...
...
@@ -62,7 +63,8 @@ define(["jquery", "underscore", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
"boilerplate_name"
:
"raw.yaml"
,
"display_name"
:
"Raw HTML"
}],
"type"
:
"html"
"type"
:
"html"
,
"support_legend"
:
{
"show_legend"
:
false
}
}],
{
parse
:
true
...
...
@@ -76,6 +78,8 @@ define(["jquery", "underscore", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
TemplateHelpers
.
installTemplate
(
'add-xblock-component-button'
);
TemplateHelpers
.
installTemplate
(
'add-xblock-component-menu'
);
TemplateHelpers
.
installTemplate
(
'add-xblock-component-menu-problem'
);
TemplateHelpers
.
installTemplate
(
'add-xblock-component-support-legend'
);
TemplateHelpers
.
installTemplate
(
'add-xblock-component-support-level'
);
// Add templates needed by the edit XBlock modal
TemplateHelpers
.
installTemplate
(
'edit-xblock-modal'
);
...
...
cms/static/js/views/components/add_xblock_menu.js
View file @
d4cc7b8f
define
([
"jquery"
,
"js/views/baseview"
],
function
(
$
,
BaseView
)
{
define
([
"jquery"
,
"js/views/baseview"
,
'edx-ui-toolkit/js/utils/html-utils'
],
function
(
$
,
BaseView
,
HtmlUtils
)
{
return
BaseView
.
extend
({
className
:
function
()
{
...
...
@@ -9,8 +9,19 @@ define(["jquery", "js/views/baseview"],
BaseView
.
prototype
.
initialize
.
call
(
this
);
var
template_name
=
this
.
model
.
type
===
"problem"
?
"add-xblock-component-menu-problem"
:
"add-xblock-component-menu"
;
var
support_indicator_template
=
this
.
loadTemplate
(
"add-xblock-component-support-level"
);
var
support_legend_template
=
this
.
loadTemplate
(
"add-xblock-component-support-legend"
);
this
.
template
=
this
.
loadTemplate
(
template_name
);
this
.
$el
.
html
(
this
.
template
({
type
:
this
.
model
.
type
,
templates
:
this
.
model
.
templates
}));
HtmlUtils
.
setHtml
(
this
.
$el
,
HtmlUtils
.
HTML
(
this
.
template
({
type
:
this
.
model
.
type
,
templates
:
this
.
model
.
templates
,
support_legend
:
this
.
model
.
support_legend
,
support_indicator_template
:
support_indicator_template
,
support_legend_template
:
support_legend_template
,
HtmlUtils
:
HtmlUtils
}))
);
// Make the tabs on problems into "real tabs"
this
.
$
(
'.tab-group'
).
tabs
();
}
...
...
cms/static/sass/_build-v1.scss
View file @
d4cc7b8f
...
...
@@ -10,7 +10,7 @@
// +Base - Utilities
// ====================
@import
'variables'
;
@import
'
partials/
variables'
;
@import
'mixins'
;
@import
'mixins-inherited'
;
...
...
cms/static/sass/elements/_modules.scss
View file @
d4cc7b8f
...
...
@@ -168,18 +168,47 @@
// specific menu types
&
.new-component-problem
{
padding-bottom
:
(
$baseline
/
2
);
.problem-type-tabs
{
display
:
inline-block
;
}
}
.support-documentation
{
float
:
right
;
@include
margin
(
$baseline
,
0
,
(
$baseline
/
2
)
,
(
$baseline
/
2
));
@include
font-size
(
14
);
.support-documentation-level
{
padding-right
:
(
$baseline
/
2
);
}
.support-documentation-link
{
// Override JQuery ui-widget-content link color (black) with our usual link color and hover action.
color
:
$uxpl-blue-base
;
text-decoration
:
none
;
padding-right
:
(
$baseline
/
2
);
&
:hover
{
color
:
$uxpl-blue-hover-active
;
text-decoration
:
underline
;
}
}
}
.support-level
{
padding-right
:
(
$baseline
/
2
);
}
.icon
{
color
:
$uxpl-primary-accent
;
}
}
// individual menus
// --------------------
.new-component-template
{
@include
clearfix
();
margin-bottom
:
0
;
li
{
border
:
none
;
...
...
@@ -190,7 +219,7 @@
}
}
.button-component
{
.button-component
{
@include
clearfix
();
@include
transition
(
none
);
@extend
%t-demi-strong
;
...
...
@@ -201,11 +230,16 @@
background
:
$white
;
color
:
$gray-d3
;
text-align
:
left
;
font-family
:
$f-sans-serif
;
&
:hover
{
@include
transition
(
background-color
$tmg-f2
linear
0s
);
background
:
tint
(
$green
,
30%
);
color
:
$white
;
.icon
{
color
:
$white
;
}
}
}
}
...
...
cms/static/sass/partials/_variables.scss
View file @
d4cc7b8f
...
...
@@ -39,6 +39,16 @@ $f-monospace: 'Bitstream Vera Sans Mono', Consolas, Courier, monospace;
// ====================
$transparent
:
rgba
(
0
,
0
,
0
,
0
);
// used when color value is needed for UI width/transitions but element is transparent
// +Colors - UXPL new pattern library colors
// ====================
$uxpl-blue-base
:
rgba
(
0
,
116
,
180
,
1
);
// wcag2a compliant
$uxpl-blue-hover-active
:
lighten
(
$uxpl-blue-base
,
7%
);
// wcag2a compliant
$uxpl-green-base
:
rgba
(
0
,
129
,
0
,
1
);
// wcag2a compliant
$uxpl-green-hover-active
:
lighten
(
$uxpl-green-base
,
7%
);
// wcag2a compliant
$uxpl-primary-accent
:
rgb
(
14
,
166
,
236
);
// +Colors - Primary
// ====================
$black
:
rgb
(
0
,
0
,
0
);
...
...
@@ -87,12 +97,6 @@ $blue-t1: rgba($blue, 0.25);
$blue-t2
:
rgba
(
$blue
,
0
.50
);
$blue-t3
:
rgba
(
$blue
,
0
.75
);
$uxpl-blue-base
:
rgba
(
0
,
116
,
180
,
1
);
// wcag2a compliant
$uxpl-blue-hover-active
:
lighten
(
$uxpl-blue-base
,
7%
);
// wcag2a compliant
$uxpl-green-base
:
rgba
(
0
,
129
,
0
,
1
);
// wcag2a compliant
$uxpl-green-hover-active
:
lighten
(
$uxpl-green-base
,
7%
);
// wcag2a compliant
$pink
:
rgb
(
183
,
37
,
103
);
// #b72567;
$pink-l1
:
tint
(
$pink
,
20%
);
$pink-l2
:
tint
(
$pink
,
40%
);
...
...
cms/templates/js/add-xblock-component-menu-problem.underscore
View file @
d4cc7b8f
...
...
@@ -7,10 +7,10 @@
%>">
<ul class="problem-type-tabs nav-tabs" tabindex='-1'>
<li class="current">
<a class="link-tab" href="#tab1"><%
=
gettext("Common Problem Types") %></a>
<a class="link-tab" href="#tab1"><%
-
gettext("Common Problem Types") %></a>
</li>
<li>
<a class="link-tab" href="#tab2"><%
=
gettext("Advanced") %></a>
<a class="link-tab" href="#tab2"><%
-
gettext("Advanced") %></a>
</li>
</ul>
<div class="tab current" id="tab1">
...
...
@@ -19,15 +19,17 @@
<% if (templates[i].tab == "common") { %>
<% if (!templates[i].boilerplate_name) { %>
<li class="editor-md empty">
<button type="button" class="button-component" data-category="<%= templates[i].category %>">
<span class="name"><%= templates[i].display_name %></span>
<button type="button" class="button-component" data-category="<%- templates[i].category %>">
<%= HtmlUtils.HTML(support_indicator_template({support_level: templates[i].support_level})) %>
<span class="name"><%- templates[i].display_name %></span>
</button>
</li>
<% } else { %>
<li class="editor-md">
<button type="button" class="button-component" data-category="<%= templates[i].category %>"
data-boilerplate="<%= templates[i].boilerplate_name %>">
<span class="name"><%= templates[i].display_name %></span>
<button type="button" class="button-component" data-category="<%- templates[i].category %>"
data-boilerplate="<%- templates[i].boilerplate_name %>">
<%= HtmlUtils.HTML(support_indicator_template({support_level: templates[i].support_level})) %>
<span class="name"><%- templates[i].display_name %></span>
</button>
</li>
<% } %>
...
...
@@ -40,14 +42,16 @@
<% for (var i = 0; i < templates.length; i++) { %>
<% if (templates[i].tab == "advanced") { %>
<li class="editor-manual">
<button type="button" class="button-component" data-category="<%= templates[i].category %>"
data-boilerplate="<%= templates[i].boilerplate_name %>">
<span class="name"><%= templates[i].display_name %></span>
<button type="button" class="button-component" data-category="<%- templates[i].category %>"
data-boilerplate="<%- templates[i].boilerplate_name %>">
<%= HtmlUtils.HTML(support_indicator_template({support_level: templates[i].support_level})) %>
<span class="name"><%- templates[i].display_name %></span>
</button>
</li>
<% } %>
<% } %>
</ul>
</div>
<button class="cancel-button" data-type="<%= type %>"><%= gettext("Cancel") %></button>
<button class="cancel-button" data-type="<%- type %>"><%- gettext("Cancel") %></button>
<%= HtmlUtils.HTML(support_legend_template({support_legend: support_legend})) %>
</div>
cms/templates/js/add-xblock-component-menu.underscore
View file @
d4cc7b8f
...
...
@@ -10,20 +10,23 @@
<% for (var i = 0; i < templates.length; i++) { %>
<% if (!templates[i].boilerplate_name) { %>
<li class="editor-md empty">
<button type="button" class="button-component" data-category="<%= templates[i].category %>">
<span class="name"><%= templates[i].display_name %></span>
<button type="button" class="button-component" data-category="<%- templates[i].category %>">
<%= HtmlUtils.HTML(support_indicator_template({support_level: templates[i].support_level})) %>
<span class="name"><%- templates[i].display_name %></span>
</button>
</li>
<% } else { %>
<li class="editor-md">
<button type="button" class="button-component" data-category="<%= templates[i].category %>"
data-boilerplate="<%= templates[i].boilerplate_name %>">
<span class="name"><%= templates[i].display_name %></span>
<button type="button" class="button-component" data-category="<%- templates[i].category %>"
data-boilerplate="<%- templates[i].boilerplate_name %>">
<%= HtmlUtils.HTML(support_indicator_template({support_level: templates[i].support_level})) %>
<span class="name"><%- templates[i].display_name %></span>
</button>
</li>
<% } %>
<% } %>
</ul>
<button class="cancel-button" data-type="<%= type %>"><%= gettext("Cancel") %></button>
<button class="cancel-button" data-type="<%- type %>"><%- gettext("Cancel") %></button>
<%= HtmlUtils.HTML(support_legend_template({support_legend: support_legend})) %>
</div>
<% } %>
cms/templates/js/add-xblock-component-support-legend.underscore
0 → 100644
View file @
d4cc7b8f
<% if (support_legend.show_legend) { %>
<span class="support-documentation">
<a class="support-documentation-link"
href="http://edx.readthedocs.io/projects/edx-partner-course-staff/en/latest/exercises_tools/create_exercises_and_tools.html#levels-of-support-for-tools" target="_blank">
<%- support_legend.documentation_label %>
</a>
<span class="support-documentation-level">
<span class="icon fa fa-circle" aria-hidden="true"></span>
<span><%- gettext('Supported') %></span>
</span>
<span class="support-documentation-level">
<span class="icon fa fa-adjust" aria-hidden="true"></span>
<span><%- gettext('Provisional') %></span>
</span>
<% if (support_legend.allow_unsupported_xblocks) { %>
<span class="support-documentation-level">
<span class="icon fa fa-circle-o" aria-hidden="true"></span>
<span><%- gettext('Not Supported') %></span>
</span>
<% } %>
</span>
<% } %>
cms/templates/js/add-xblock-component-support-level.underscore
0 → 100644
View file @
d4cc7b8f
<% if (support_level === "fs"){ %>
<span class="icon support-level fa fa-circle" aria-hidden="true"></span>
<span class="sr"><%- gettext('Fully Supported') %></span>
<% } else if (support_level === "ps"){ %>
<span class="icon support-level fa fa-adjust" aria-hidden="true"></span>
<span class="sr"><%- gettext('Provisionally Supported') %></span>
<% } else if (support_level === "us"){ %>
<span class="icon support-level fa fa-circle-o" aria-hidden="true"></span>
<span class="sr"><%- gettext('Not Supported') %></span>
<% } %>
common/djangoapps/xblock_django/api.py
View file @
d4cc7b8f
...
...
@@ -22,11 +22,11 @@ def disabled_xblocks():
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`).
This method returns the QuerySet of XBlocks that can be created in Studio (by default, only fully supported
and provisionally supported XBlocks), as stored in `XBlockStudioConfiguration`.
Note that this method does NOT check the value `XBlockStudioConfigurationFlag`, nor does it 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.
...
...
@@ -36,13 +36,10 @@ def authorable_xblocks(allow_unsupported=False, name=None):
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
.
QuerySet:
Returns authorable XBlocks, taking into account `support_level`, `enabled` and `name`
(if specified) as specified by `XBlockStudioConfiguration`. Does not take into account whether or not
`XBlockStudioConfigurationFlag` is enabled
.
"""
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
)
...
...
common/djangoapps/xblock_django/models.py
View file @
d4cc7b8f
...
...
@@ -41,27 +41,10 @@ class XBlockDisableConfig(ConfigurationModel):
return
block_type
in
config
.
disabled_blocks
.
split
()
@classmethod
def
disabled_create_block_types
(
cls
):
""" Return list of deprecated XBlock types. Merges types in settings file and field. """
config
=
cls
.
current
()
xblock_types
=
config
.
disabled_create_blocks
.
split
()
if
config
.
enabled
else
[]
# Merge settings list with one in the admin config;
if
hasattr
(
settings
,
'DEPRECATED_ADVANCED_COMPONENT_TYPES'
):
xblock_types
.
extend
(
xblock_type
for
xblock_type
in
settings
.
DEPRECATED_ADVANCED_COMPONENT_TYPES
if
xblock_type
not
in
xblock_types
)
return
xblock_types
def
__unicode__
(
self
):
config
=
XBlockDisableConfig
.
current
()
return
u"Disabled xblocks = {disabled_xblocks}
\n
Deprecated xblocks = {disabled_create_block_types}"
.
format
(
disabled_xblocks
=
config
.
disabled_blocks
,
disabled_create_block_types
=
config
.
disabled_create_block_types
return
u"Disabled xblocks = {disabled_xblocks}"
.
format
(
disabled_xblocks
=
config
.
disabled_blocks
)
...
...
common/djangoapps/xblock_django/tests/test_api.py
View file @
d4cc7b8f
...
...
@@ -61,28 +61,21 @@ class XBlockSupportTestCase(CacheIsolationTestCase):
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
.
Tests authorable_xblocks returns an empty list if
XBlockStudioConfiguration table is empty, regardless
of whether or not XBlockStudioConfigurationFlag is enabled
.
"""
XBlockStudioConfigurationFlag
(
enabled
=
True
)
.
save
()
XBlockStudioConfiguration
.
objects
.
all
()
.
delete
()
self
.
assertFalse
(
XBlockStudioConfigurationFlag
.
is_enabled
())
self
.
assertEqual
(
0
,
len
(
authorable_xblocks
(
allow_unsupported
=
True
)))
XBlockStudioConfigurationFlag
(
enabled
=
True
)
.
save
()
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.
Tests authorable_xblocks when 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
)
...
...
@@ -99,7 +92,7 @@ class XBlockSupportTestCase(CacheIsolationTestCase):
def
test_authorable_blocks_by_name
(
self
):
"""
Tests authorable_xblocks when
configuration flag is enabled and
name is specified.
Tests authorable_xblocks when name is specified.
"""
def
verify_xblock_fields
(
name
,
template
,
support_level
,
block
):
"""
...
...
@@ -109,8 +102,6 @@ class XBlockSupportTestCase(CacheIsolationTestCase):
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
))
...
...
common/djangoapps/xblock_django/tests/test_models.py
deleted
100644 → 0
View file @
71bebec5
"""
Tests for deprecated xblocks in XBlockDisableConfig.
"""
import
ddt
from
mock
import
patch
from
django.test
import
TestCase
from
xblock_django.models
import
XBlockDisableConfig
@ddt.ddt
class
XBlockDisableConfigTestCase
(
TestCase
):
"""
Tests for the DjangoXBlockUserService.
"""
def
setUp
(
self
):
super
(
XBlockDisableConfigTestCase
,
self
)
.
setUp
()
# Initialize the deprecated modules settings with empty list
XBlockDisableConfig
.
objects
.
create
(
disabled_blocks
=
''
,
enabled
=
True
)
@ddt.data
(
(
'poll'
,
[
'poll'
]),
(
'poll survey annotatable textannotation'
,
[
'poll'
,
'survey'
,
'annotatable'
,
'textannotation'
]),
(
''
,
[])
)
@ddt.unpack
def
test_deprecated_blocks_splitting
(
self
,
xblocks
,
expected_result
):
"""
Tests that it correctly splits the xblocks defined in field.
"""
XBlockDisableConfig
.
objects
.
create
(
disabled_create_blocks
=
xblocks
,
enabled
=
True
)
self
.
assertEqual
(
XBlockDisableConfig
.
disabled_create_block_types
(),
expected_result
)
@patch
(
'django.conf.settings.DEPRECATED_ADVANCED_COMPONENT_TYPES'
,
[
'poll'
,
'survey'
])
def
test_deprecated_blocks_file
(
self
):
"""
Tests that deprecated modules contain entries from settings file DEPRECATED_ADVANCED_COMPONENT_TYPES
"""
self
.
assertEqual
(
XBlockDisableConfig
.
disabled_create_block_types
(),
[
'poll'
,
'survey'
])
@patch
(
'django.conf.settings.DEPRECATED_ADVANCED_COMPONENT_TYPES'
,
[
'poll'
,
'survey'
])
def
test_deprecated_blocks_file_and_config
(
self
):
"""
Tests that deprecated types defined in both settings and config model are read.
"""
XBlockDisableConfig
.
objects
.
create
(
disabled_create_blocks
=
'annotatable'
,
enabled
=
True
)
self
.
assertEqual
(
XBlockDisableConfig
.
disabled_create_block_types
(),
[
'annotatable'
,
'poll'
,
'survey'
])
common/lib/xmodule/xmodule/course_module.py
View file @
d4cc7b8f
...
...
@@ -408,7 +408,7 @@ class CourseFields(object):
)
advanced_modules
=
List
(
display_name
=
_
(
"Advanced Module List"
),
help
=
_
(
"Enter the names of the advanced
component
s to use in your course."
),
help
=
_
(
"Enter the names of the advanced
module
s to use in your course."
),
scope
=
Scope
.
settings
)
has_children
=
True
...
...
@@ -830,6 +830,15 @@ class CourseFields(object):
},
scope
=
Scope
.
settings
)
allow_unsupported_xblocks
=
Boolean
(
display_name
=
_
(
"Add Unsupported Problems and Tools"
),
help
=
_
(
"Enter true or false. If true, you can add unsupported problems and tools to your course in Studio. "
"Unsupported problems and tools 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."
),
scope
=
Scope
.
settings
,
default
=
False
)
class
CourseModule
(
CourseFields
,
SequenceModule
):
# pylint: disable=abstract-method
...
...
lms/envs/common.py
View file @
d4cc7b8f
...
...
@@ -2905,10 +2905,6 @@ APP_UPGRADE_CACHE_TIMEOUT = 3600
# if you want to avoid an overlap in ids while searching for history across the two tables.
STUDENTMODULEHISTORYEXTENDED_OFFSET
=
10000
# Deprecated xblock types
DEPRECATED_ADVANCED_COMPONENT_TYPES
=
[]
# Cutoff date for granting audit certificates
AUDIT_CERT_CUTOFF_DATE
=
None
...
...
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