Commit d4cc7b8f by cahrens

Support level support for Studio xblock creation.

TNL-4670
parent 71bebec5
...@@ -691,7 +691,6 @@ class MiscCourseTests(ContentStoreTestCase): ...@@ -691,7 +691,6 @@ class MiscCourseTests(ContentStoreTestCase):
# Test that malicious code does not appear in html # Test that malicious code does not appear in html
self.assertNotIn(malicious_code, resp.content) self.assertNotIn(malicious_code, resp.content)
@patch('django.conf.settings.DEPRECATED_ADVANCED_COMPONENT_TYPES', [])
def test_advanced_components_in_edit_unit(self): 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 # This could be made better, but for now let's just assert that we see the advanced modules mentioned in the page
# response HTML # response HTML
......
...@@ -21,6 +21,7 @@ from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration ...@@ -21,6 +21,7 @@ from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
from openedx.core.djangoapps.models.course_details import CourseDetails from openedx.core.djangoapps.models.course_details import CourseDetails
from student.roles import CourseInstructorRole, CourseStaffRole from student.roles import CourseInstructorRole, CourseStaffRole
from student.tests.factories import UserFactory from student.tests.factories import UserFactory
from xblock_django.models import XBlockStudioConfigurationFlag
from xmodule.fields import Date from xmodule.fields import Date
from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
...@@ -784,6 +785,15 @@ class CourseMetadataEditingTest(CourseTestCase): ...@@ -784,6 +785,15 @@ class CourseMetadataEditingTest(CourseTestCase):
) )
self.assertNotIn('edxnotes', test_model) 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): def test_validate_from_json_correct_inputs(self):
is_valid, errors, test_model = CourseMetadata.validate_and_update_from_json( is_valid, errors, test_model = CourseMetadata.validate_and_update_from_json(
self.course, self.course,
......
...@@ -2,7 +2,9 @@ ...@@ -2,7 +2,9 @@
Django module for Course Metadata class -- manages advanced settings and related parameters Django module for Course Metadata class -- manages advanced settings and related parameters
""" """
from xblock.fields import Scope from xblock.fields import Scope
from xblock_django.models import XBlockStudioConfigurationFlag
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.conf import settings from django.conf import settings
...@@ -93,6 +95,11 @@ class CourseMetadata(object): ...@@ -93,6 +95,11 @@ class CourseMetadata(object):
filtered_list.append('enable_ccx') filtered_list.append('enable_ccx')
filtered_list.append('ccx_connector') 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 return filtered_list
@classmethod @classmethod
......
...@@ -398,9 +398,6 @@ ADVANCED_SECURITY_CONFIG = ENV_TOKENS.get('ADVANCED_SECURITY_CONFIG', {}) ...@@ -398,9 +398,6 @@ ADVANCED_SECURITY_CONFIG = ENV_TOKENS.get('ADVANCED_SECURITY_CONFIG', {})
################ ADVANCED COMPONENT/PROBLEM TYPES ############### ################ ADVANCED COMPONENT/PROBLEM TYPES ###############
ADVANCED_PROBLEM_TYPES = ENV_TOKENS.get('ADVANCED_PROBLEM_TYPES', ADVANCED_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 ############### ################ VIDEO UPLOAD PIPELINE ###############
......
...@@ -1132,19 +1132,6 @@ XBLOCK_SETTINGS = { ...@@ -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 ################################ ################################ Settings for Credit Course Requirements ################################
# Initial delay used for retrying tasks. # Initial delay used for retrying tasks.
# Additional retries use longer delays. # Additional retries use longer delays.
......
...@@ -10,7 +10,8 @@ define(["backbone"], function (Backbone) { ...@@ -10,7 +10,8 @@ define(["backbone"], function (Backbone) {
// category (may or may not match "type") // category (may or may not match "type")
// boilerplate_name (may be null) // boilerplate_name (may be null)
// is_common (only used for problems) // is_common (only used for problems)
templates: [] templates: [],
support_legend: {}
}, },
parse: function (response) { parse: function (response) {
// Returns true only for templates that both have no boilerplate and are of // Returns true only for templates that both have no boilerplate and are of
...@@ -24,6 +25,7 @@ define(["backbone"], function (Backbone) { ...@@ -24,6 +25,7 @@ define(["backbone"], function (Backbone) {
this.type = response.type; this.type = response.type;
this.templates = response.templates; this.templates = response.templates;
this.display_name = response.display_name; this.display_name = response.display_name;
this.support_legend = response.support_legend;
// Sort the templates. // Sort the templates.
this.templates.sort(function (a, b) { this.templates.sort(function (a, b) {
......
...@@ -49,7 +49,8 @@ define(["js/models/component_template"], ...@@ -49,7 +49,8 @@ define(["js/models/component_template"],
"boilerplate_name": "alternate_word_cloud.yaml", "boilerplate_name": "alternate_word_cloud.yaml",
"display_name": "Word Cloud" "display_name": "Word Cloud"
}], }],
"type": "problem" "type": "problem",
"support_legend": {"show_legend": false}
}; };
it('orders templates correctly', function () { it('orders templates correctly', function () {
......
...@@ -41,12 +41,13 @@ define(["jquery", "underscore", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe ...@@ -41,12 +41,13 @@ define(["jquery", "underscore", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
mockComponentTemplates = new ComponentTemplates([ mockComponentTemplates = new ComponentTemplates([
{ {
templates: [ "templates": [
{ {
category: 'discussion', "category": "discussion",
display_name: 'Discussion' "display_name": "Discussion"
}], }],
type: 'discussion' "type": "discussion",
"support_legend": {"show_legend": false}
}, { }, {
"templates": [ "templates": [
{ {
...@@ -62,7 +63,8 @@ define(["jquery", "underscore", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe ...@@ -62,7 +63,8 @@ define(["jquery", "underscore", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
"boilerplate_name": "raw.yaml", "boilerplate_name": "raw.yaml",
"display_name": "Raw HTML" "display_name": "Raw HTML"
}], }],
"type": "html" "type": "html",
"support_legend": {"show_legend": false}
}], }],
{ {
parse: true parse: true
...@@ -76,6 +78,8 @@ define(["jquery", "underscore", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe ...@@ -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-button');
TemplateHelpers.installTemplate('add-xblock-component-menu'); TemplateHelpers.installTemplate('add-xblock-component-menu');
TemplateHelpers.installTemplate('add-xblock-component-menu-problem'); 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 // Add templates needed by the edit XBlock modal
TemplateHelpers.installTemplate('edit-xblock-modal'); TemplateHelpers.installTemplate('edit-xblock-modal');
......
define(["jquery", "js/views/baseview"], define(["jquery", "js/views/baseview", 'edx-ui-toolkit/js/utils/html-utils'],
function ($, BaseView) { function ($, BaseView, HtmlUtils) {
return BaseView.extend({ return BaseView.extend({
className: function () { className: function () {
...@@ -9,8 +9,19 @@ define(["jquery", "js/views/baseview"], ...@@ -9,8 +9,19 @@ define(["jquery", "js/views/baseview"],
BaseView.prototype.initialize.call(this); BaseView.prototype.initialize.call(this);
var template_name = this.model.type === "problem" ? "add-xblock-component-menu-problem" : var template_name = this.model.type === "problem" ? "add-xblock-component-menu-problem" :
"add-xblock-component-menu"; "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.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" // Make the tabs on problems into "real tabs"
this.$('.tab-group').tabs(); this.$('.tab-group').tabs();
} }
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
// +Base - Utilities // +Base - Utilities
// ==================== // ====================
@import 'variables'; @import 'partials/variables';
@import 'mixins'; @import 'mixins';
@import 'mixins-inherited'; @import 'mixins-inherited';
......
...@@ -168,18 +168,47 @@ ...@@ -168,18 +168,47 @@
// specific menu types // specific menu types
&.new-component-problem { &.new-component-problem {
padding-bottom: ($baseline/2);
.problem-type-tabs { .problem-type-tabs {
display: inline-block; 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 // individual menus
// -------------------- // --------------------
.new-component-template { .new-component-template {
@include clearfix(); @include clearfix();
margin-bottom: 0;
li { li {
border: none; border: none;
...@@ -190,7 +219,7 @@ ...@@ -190,7 +219,7 @@
} }
} }
.button-component { .button-component {
@include clearfix(); @include clearfix();
@include transition(none); @include transition(none);
@extend %t-demi-strong; @extend %t-demi-strong;
...@@ -201,11 +230,16 @@ ...@@ -201,11 +230,16 @@
background: $white; background: $white;
color: $gray-d3; color: $gray-d3;
text-align: left; text-align: left;
font-family: $f-sans-serif;
&:hover { &:hover {
@include transition(background-color $tmg-f2 linear 0s); @include transition(background-color $tmg-f2 linear 0s);
background: tint($green,30%); background: tint($green,30%);
color: $white; color: $white;
.icon {
color: $white;
}
} }
} }
} }
......
...@@ -39,6 +39,16 @@ $f-monospace: 'Bitstream Vera Sans Mono', Consolas, Courier, monospace; ...@@ -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 $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 // +Colors - Primary
// ==================== // ====================
$black: rgb(0,0,0); $black: rgb(0,0,0);
...@@ -87,12 +97,6 @@ $blue-t1: rgba($blue, 0.25); ...@@ -87,12 +97,6 @@ $blue-t1: rgba($blue, 0.25);
$blue-t2: rgba($blue, 0.50); $blue-t2: rgba($blue, 0.50);
$blue-t3: rgba($blue, 0.75); $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: rgb(183, 37, 103); // #b72567;
$pink-l1: tint($pink,20%); $pink-l1: tint($pink,20%);
$pink-l2: tint($pink,40%); $pink-l2: tint($pink,40%);
......
...@@ -7,10 +7,10 @@ ...@@ -7,10 +7,10 @@
%>"> %>">
<ul class="problem-type-tabs nav-tabs" tabindex='-1'> <ul class="problem-type-tabs nav-tabs" tabindex='-1'>
<li class="current"> <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>
<li> <li>
<a class="link-tab" href="#tab2"><%= gettext("Advanced") %></a> <a class="link-tab" href="#tab2"><%- gettext("Advanced") %></a>
</li> </li>
</ul> </ul>
<div class="tab current" id="tab1"> <div class="tab current" id="tab1">
...@@ -19,15 +19,17 @@ ...@@ -19,15 +19,17 @@
<% if (templates[i].tab == "common") { %> <% if (templates[i].tab == "common") { %>
<% if (!templates[i].boilerplate_name) { %> <% if (!templates[i].boilerplate_name) { %>
<li class="editor-md empty"> <li class="editor-md empty">
<button type="button" class="button-component" data-category="<%= templates[i].category %>"> <button type="button" class="button-component" data-category="<%- templates[i].category %>">
<span class="name"><%= templates[i].display_name %></span> <%= HtmlUtils.HTML(support_indicator_template({support_level: templates[i].support_level})) %>
<span class="name"><%- templates[i].display_name %></span>
</button> </button>
</li> </li>
<% } else { %> <% } else { %>
<li class="editor-md"> <li class="editor-md">
<button type="button" class="button-component" data-category="<%= templates[i].category %>" <button type="button" class="button-component" data-category="<%- templates[i].category %>"
data-boilerplate="<%= templates[i].boilerplate_name %>"> data-boilerplate="<%- templates[i].boilerplate_name %>">
<span class="name"><%= templates[i].display_name %></span> <%= HtmlUtils.HTML(support_indicator_template({support_level: templates[i].support_level})) %>
<span class="name"><%- templates[i].display_name %></span>
</button> </button>
</li> </li>
<% } %> <% } %>
...@@ -40,14 +42,16 @@ ...@@ -40,14 +42,16 @@
<% for (var i = 0; i < templates.length; i++) { %> <% for (var i = 0; i < templates.length; i++) { %>
<% if (templates[i].tab == "advanced") { %> <% if (templates[i].tab == "advanced") { %>
<li class="editor-manual"> <li class="editor-manual">
<button type="button" class="button-component" data-category="<%= templates[i].category %>" <button type="button" class="button-component" data-category="<%- templates[i].category %>"
data-boilerplate="<%= templates[i].boilerplate_name %>"> data-boilerplate="<%- templates[i].boilerplate_name %>">
<span class="name"><%= templates[i].display_name %></span> <%= HtmlUtils.HTML(support_indicator_template({support_level: templates[i].support_level})) %>
<span class="name"><%- templates[i].display_name %></span>
</button> </button>
</li> </li>
<% } %> <% } %>
<% } %> <% } %>
</ul> </ul>
</div> </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> </div>
...@@ -10,20 +10,23 @@ ...@@ -10,20 +10,23 @@
<% for (var i = 0; i < templates.length; i++) { %> <% for (var i = 0; i < templates.length; i++) { %>
<% if (!templates[i].boilerplate_name) { %> <% if (!templates[i].boilerplate_name) { %>
<li class="editor-md empty"> <li class="editor-md empty">
<button type="button" class="button-component" data-category="<%= templates[i].category %>"> <button type="button" class="button-component" data-category="<%- templates[i].category %>">
<span class="name"><%= templates[i].display_name %></span> <%= HtmlUtils.HTML(support_indicator_template({support_level: templates[i].support_level})) %>
<span class="name"><%- templates[i].display_name %></span>
</button> </button>
</li> </li>
<% } else { %> <% } else { %>
<li class="editor-md"> <li class="editor-md">
<button type="button" class="button-component" data-category="<%= templates[i].category %>" <button type="button" class="button-component" data-category="<%- templates[i].category %>"
data-boilerplate="<%= templates[i].boilerplate_name %>"> data-boilerplate="<%- templates[i].boilerplate_name %>">
<span class="name"><%= templates[i].display_name %></span> <%= HtmlUtils.HTML(support_indicator_template({support_level: templates[i].support_level})) %>
<span class="name"><%- templates[i].display_name %></span>
</button> </button>
</li> </li>
<% } %> <% } %>
<% } %> <% } %>
</ul> </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> </div>
<% } %> <% } %>
<% 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>
<% } %>
<% 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>
<% } %>
...@@ -22,11 +22,11 @@ def disabled_xblocks(): ...@@ -22,11 +22,11 @@ def disabled_xblocks():
def authorable_xblocks(allow_unsupported=False, name=None): def authorable_xblocks(allow_unsupported=False, name=None):
""" """
If Studio XBlock support state is enabled (via `XBlockStudioConfigurationFlag`), this method returns This method returns the QuerySet of XBlocks that can be created in Studio (by default, only fully supported
the QuerySet of XBlocks that can be created in Studio (by default, only fully supported and provisionally and provisionally supported XBlocks), as stored in `XBlockStudioConfiguration`.
supported). If `XBlockStudioConfigurationFlag` is not enabled, this method returns None. Note that this method does NOT check the value `XBlockStudioConfigurationFlag`, nor does it take into account
Note that this method does not take into account fully disabled xblocks (as returned fully disabled xblocks (as returned by `disabled_xblocks`) or deprecated xblocks
by `disabled_xblocks`) or deprecated xblocks (as returned by `deprecated_xblocks`). (as returned by `deprecated_xblocks`).
Arguments: Arguments:
allow_unsupported (bool): If `True`, enabled but unsupported XBlocks will also be returned. 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): ...@@ -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 name (str): If provided, filters the returned XBlocks to those with the provided name. This is
useful for XBlocks with lots of template types. useful for XBlocks with lots of template types.
Returns: Returns:
QuerySet: If `XBlockStudioConfigurationFlag` is enabled, returns authorable XBlocks, QuerySet: Returns authorable XBlocks, taking into account `support_level`, `enabled` and `name`
taking into account `support_level`, `enabled` and `name` (if specified). (if specified) as specified by `XBlockStudioConfiguration`. Does not take into account whether or not
If `XBlockStudioConfigurationFlag` is disabled, returns None. `XBlockStudioConfigurationFlag` is enabled.
""" """
if not XBlockStudioConfigurationFlag.is_enabled():
return None
blocks = XBlockStudioConfiguration.objects.current_set().filter(enabled=True) blocks = XBlockStudioConfiguration.objects.current_set().filter(enabled=True)
if not allow_unsupported: if not allow_unsupported:
blocks = blocks.exclude(support_level=XBlockStudioConfiguration.UNSUPPORTED) blocks = blocks.exclude(support_level=XBlockStudioConfiguration.UNSUPPORTED)
......
...@@ -41,27 +41,10 @@ class XBlockDisableConfig(ConfigurationModel): ...@@ -41,27 +41,10 @@ class XBlockDisableConfig(ConfigurationModel):
return block_type in config.disabled_blocks.split() 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): def __unicode__(self):
config = XBlockDisableConfig.current() config = XBlockDisableConfig.current()
return u"Disabled xblocks = {disabled_xblocks}\nDeprecated xblocks = {disabled_create_block_types}".format( return u"Disabled xblocks = {disabled_xblocks}".format(
disabled_xblocks=config.disabled_blocks, disabled_xblocks=config.disabled_blocks
disabled_create_block_types=config.disabled_create_block_types
) )
......
...@@ -61,28 +61,21 @@ class XBlockSupportTestCase(CacheIsolationTestCase): ...@@ -61,28 +61,21 @@ class XBlockSupportTestCase(CacheIsolationTestCase):
disabled_xblock_names = [block.name for block in disabled_xblocks()] disabled_xblock_names = [block.name for block in disabled_xblocks()]
self.assertItemsEqual(["survey", "poll"], disabled_xblock_names) 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): def test_authorable_blocks_empty_model(self):
""" """
Tests authorable_xblocks returns an empty list if the configuration flag is enabled but Tests authorable_xblocks returns an empty list if XBlockStudioConfiguration table is empty, regardless
the XBlockStudioConfiguration table is empty. of whether or not XBlockStudioConfigurationFlag is enabled.
""" """
XBlockStudioConfigurationFlag(enabled=True).save()
XBlockStudioConfiguration.objects.all().delete() 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))) self.assertEqual(0, len(authorable_xblocks(allow_unsupported=True)))
def test_authorable_blocks(self): 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()] authorable_xblock_names = [block.name for block in authorable_xblocks()]
self.assertItemsEqual(["done", "problem", "problem", "html"], authorable_xblock_names) self.assertItemsEqual(["done", "problem", "problem", "html"], authorable_xblock_names)
...@@ -99,7 +92,7 @@ class XBlockSupportTestCase(CacheIsolationTestCase): ...@@ -99,7 +92,7 @@ class XBlockSupportTestCase(CacheIsolationTestCase):
def test_authorable_blocks_by_name(self): 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): def verify_xblock_fields(name, template, support_level, block):
""" """
...@@ -109,8 +102,6 @@ class XBlockSupportTestCase(CacheIsolationTestCase): ...@@ -109,8 +102,6 @@ class XBlockSupportTestCase(CacheIsolationTestCase):
self.assertEqual(template, block.template) self.assertEqual(template, block.template)
self.assertEqual(support_level, block.support_level) self.assertEqual(support_level, block.support_level)
XBlockStudioConfigurationFlag(enabled=True).save()
# There are no xblocks with name video. # There are no xblocks with name video.
authorable_blocks = authorable_xblocks(name="video") authorable_blocks = authorable_xblocks(name="video")
self.assertEqual(0, len(authorable_blocks)) self.assertEqual(0, len(authorable_blocks))
......
"""
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'])
...@@ -408,7 +408,7 @@ class CourseFields(object): ...@@ -408,7 +408,7 @@ class CourseFields(object):
) )
advanced_modules = List( advanced_modules = List(
display_name=_("Advanced Module List"), display_name=_("Advanced Module List"),
help=_("Enter the names of the advanced components to use in your course."), help=_("Enter the names of the advanced modules to use in your course."),
scope=Scope.settings scope=Scope.settings
) )
has_children = True has_children = True
...@@ -830,6 +830,15 @@ class CourseFields(object): ...@@ -830,6 +830,15 @@ class CourseFields(object):
}, },
scope=Scope.settings 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 class CourseModule(CourseFields, SequenceModule): # pylint: disable=abstract-method
......
...@@ -2905,10 +2905,6 @@ APP_UPGRADE_CACHE_TIMEOUT = 3600 ...@@ -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. # if you want to avoid an overlap in ids while searching for history across the two tables.
STUDENTMODULEHISTORYEXTENDED_OFFSET = 10000 STUDENTMODULEHISTORYEXTENDED_OFFSET = 10000
# Deprecated xblock types
DEPRECATED_ADVANCED_COMPONENT_TYPES = []
# Cutoff date for granting audit certificates # Cutoff date for granting audit certificates
AUDIT_CERT_CUTOFF_DATE = None AUDIT_CERT_CUTOFF_DATE = None
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment