diff --git a/cms/djangoapps/contentstore/views/component.py b/cms/djangoapps/contentstore/views/component.py index 8a1c9a9fc7..ff1acf3600 100644 --- a/cms/djangoapps/contentstore/views/component.py +++ b/cms/djangoapps/contentstore/views/component.py @@ -29,6 +29,7 @@ from opaque_keys.edx.keys import UsageKey from student.auth import has_course_author_access from django.utils.translation import ugettext as _ from models.settings.course_grading import CourseGradingModel +from xblock_django.models import XBlockDisableConfig __all__ = [ 'container_handler', @@ -57,7 +58,8 @@ def _advanced_component_types(): """ Return advanced component types which can be created. """ - return [c_type for c_type in ADVANCED_COMPONENT_TYPES if c_type not in settings.DEPRECATED_ADVANCED_COMPONENT_TYPES] + 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] def _load_mixed_class(category): diff --git a/cms/djangoapps/contentstore/views/tests/test_item.py b/cms/djangoapps/contentstore/views/tests/test_item.py index c17917f326..558bdeeae2 100644 --- a/cms/djangoapps/contentstore/views/tests/test_item.py +++ b/cms/djangoapps/contentstore/views/tests/test_item.py @@ -23,6 +23,7 @@ from contentstore.views.item import ( ) from contentstore.tests.utils import CourseTestCase from student.tests.factories import UserFactory +from xblock_django.models import XBlockDisableConfig from xmodule.capa_module import CapaDescriptor from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.django import modulestore @@ -1328,6 +1329,11 @@ class TestComponentTemplates(CourseTestCase): super(TestComponentTemplates, self).setUp() self.templates = get_component_templates(self.course) + # Initialize the deprecated modules settings with empty list + XBlockDisableConfig.objects.create( + disabled_create_blocks='', enabled=True + ) + def get_templates_of_type(self, template_type): """ Returns the templates for the specified type, or None if none is found. @@ -1384,22 +1390,24 @@ 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', ["poll", "survey"]) + @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` """ + XBlockDisableConfig.objects.create(disabled_create_blocks='poll survey', 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', ["poll", "survey"]) + @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 """ + XBlockDisableConfig.objects.create(disabled_create_blocks='poll survey', 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] @@ -1408,7 +1416,7 @@ class TestComponentTemplates(CourseTestCase): template_display_names = [template['display_name'] for template in templates[0]['templates']] self.assertEqual(template_display_names, ['Annotation']) - @patch('django.conf.settings.DEPRECATED_ADVANCED_COMPONENT_TYPES', []) + @patch('django.conf.settings.DEPRECATED_ADVANCED_COMPONENT_TYPES', ['poll']) def test_create_non_deprecated_problems(self): """ Test that we can create problems if they are not deprecated @@ -1417,9 +1425,9 @@ class TestComponentTemplates(CourseTestCase): 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']), 3) + self.assertEqual(len(templates[0]['templates']), 2) template_display_names = [template['display_name'] for template in templates[0]['templates']] - self.assertEqual(template_display_names, ['Annotation', 'Poll', 'Survey']) + self.assertEqual(template_display_names, ['Annotation', 'Survey']) @ddt.ddt diff --git a/cms/envs/common.py b/cms/envs/common.py index 997a8b8381..6b79e4dea2 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -999,11 +999,6 @@ ENTRANCE_EXAM_MIN_SCORE_PCT = 50 ### Default language for a new course DEFAULT_COURSE_LANGUAGE = "en" -# Adding components in this list will disable the creation of new problem for -# those components in Studio. Existing problems will work fine and one can edit -# them in Studio. -DEPRECATED_ADVANCED_COMPONENT_TYPES = [] - # Specify XBlocks that should be treated as advanced problems. Each entry is a # dict: # 'component': the entry-point name of the XBlock. @@ -1055,6 +1050,30 @@ XBLOCK_SETTINGS = { } } +################################ XBlock Deprecation ################################ + +# The following settings are used for deprecating XBlocks. + +# Adding an XBlock to this list does the following: +# 1. Shows a warning on the course outline if the XBlock is listed in +# "Advanced Module List" in "Advanced Settings" page. +# 2. List all instances of that XBlock on the top of the course outline page asking +# course authors to delete or replace the instances. +DEPRECATED_BLOCK_TYPES = [ + 'peergrading', + 'combinedopenended', + 'graphical_slider_tool', +] + +# 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. @@ -1070,16 +1089,6 @@ CREDIT_TASK_MAX_RETRIES = 5 # or denied for credit. CREDIT_PROVIDER_TIMESTAMP_EXPIRATION = 15 * 60 - -################################ Deprecated Blocks Info ################################ - -DEPRECATED_BLOCK_TYPES = [ - 'peergrading', - 'combinedopenended', - 'graphical_slider_tool', -] - - ################################ Settings for Microsites ################################ ### Select an implementation for the microsite backend @@ -1094,8 +1103,7 @@ MICROSITE_TEMPLATE_BACKEND = 'microsite_configuration.backends.filebased.Filebas # TTL for microsite database template cache MICROSITE_DATABASE_TEMPLATE_CACHE_TTL = 5 * 60 -#### PROCTORING CONFIGURATION DEFAULTS - +############################### PROCTORING CONFIGURATION DEFAULTS ############## PROCTORING_BACKEND_PROVIDER = { 'class': 'edx_proctoring.backends.null.NullBackendProvider', 'options': {}, diff --git a/common/djangoapps/xblock_django/migrations/0002_auto_20160204_0809.py b/common/djangoapps/xblock_django/migrations/0002_auto_20160204_0809.py new file mode 100644 index 0000000000..602d87f484 --- /dev/null +++ b/common/djangoapps/xblock_django/migrations/0002_auto_20160204_0809.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('xblock_django', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='xblockdisableconfig', + name='disabled_create_blocks', + field=models.TextField(default=b'', help_text='Space-separated list of XBlock types whose creation to disable in Studio.', blank=True), + ), + ] diff --git a/common/djangoapps/xblock_django/models.py b/common/djangoapps/xblock_django/models.py index c406c2573d..35cfb52802 100644 --- a/common/djangoapps/xblock_django/models.py +++ b/common/djangoapps/xblock_django/models.py @@ -3,6 +3,8 @@ Models. """ from django.utils.translation import ugettext_lazy as _ +from django.conf import settings + from django.db.models import TextField from config_models.models import ConfigurationModel @@ -10,7 +12,7 @@ from config_models.models import ConfigurationModel class XBlockDisableConfig(ConfigurationModel): """ - Configuration for disabling XBlocks. + Configuration for disabling and deprecating XBlocks. """ class Meta(ConfigurationModel.Meta): @@ -21,6 +23,13 @@ class XBlockDisableConfig(ConfigurationModel): help_text=_('Space-separated list of XBlocks which should not render.') ) + disabled_create_blocks = TextField( + default='', blank=True, + help_text=_( + "Space-separated list of XBlock types whose creation to disable in Studio." + ) + ) + @classmethod def is_block_type_disabled(cls, block_type): """ Return True if block_type is disabled. """ @@ -40,3 +49,26 @@ class XBlockDisableConfig(ConfigurationModel): return () return 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}\nDeprecated xblocks = {disabled_create_block_types}".format( + disabled_xblocks=config.disabled_blocks, + disabled_create_block_types=config.disabled_create_block_types + ) diff --git a/common/djangoapps/xblock_django/tests/test_models.py b/common/djangoapps/xblock_django/tests/test_models.py new file mode 100644 index 0000000000..72b73f642a --- /dev/null +++ b/common/djangoapps/xblock_django/tests/test_models.py @@ -0,0 +1,58 @@ +""" +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']) diff --git a/lms/envs/common.py b/lms/envs/common.py index be786340a2..291099f246 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -2751,6 +2751,9 @@ MOBILE_APP_USER_AGENT_REGEXES = [ r'edX/org.edx.mobile', ] +# Deprecated xblock types +DEPRECATED_ADVANCED_COMPONENT_TYPES = [] + ################################ Settings for Credentials Service ################################