diff --git a/common/lib/xmodule/xmodule/capa_module.py b/common/lib/xmodule/xmodule/capa_module.py index d4db445ea8..daaaba07cd 100644 --- a/common/lib/xmodule/xmodule/capa_module.py +++ b/common/lib/xmodule/xmodule/capa_module.py @@ -17,7 +17,7 @@ from xmodule.x_module import XModule from xmodule.raw_module import RawDescriptor from xmodule.exceptions import NotFoundError, ProcessingError from xblock.core import Scope, String, Boolean, Object -from .fields import Timedelta, Date, StringyInteger, StringyFloat, NON_EDITABLE_SETTINGS_SCOPE +from .fields import Timedelta, Date, StringyInteger, StringyFloat from xmodule.util.date_utils import time_to_datetime log = logging.getLogger("mitx.courseware") @@ -63,13 +63,13 @@ class ComplexEncoder(json.JSONEncoder): class CapaFields(object): attempts = StringyInteger(help="Number of attempts taken by the student on this problem", default=0, scope=Scope.user_state) max_attempts = StringyInteger(help="Maximum number of attempts that a student is allowed", scope=Scope.settings) - due = Date(help="Date that this problem is due by", scope=NON_EDITABLE_SETTINGS_SCOPE) + due = Date(help="Date that this problem is due by", scope=Scope.settings) graceperiod = Timedelta(help="Amount of time after the due date that submissions will be accepted", - scope=NON_EDITABLE_SETTINGS_SCOPE) + scope=Scope.settings) showanswer = String(help="When to show the problem answer to the student", scope=Scope.settings, default="closed", values=["answered", "always", "attempted", "closed", "never"]) force_save_button = Boolean(help="Whether to force the save button to appear on the page", - scope=NON_EDITABLE_SETTINGS_SCOPE, default=False) + scope=Scope.settings, default=False) rerandomize = Randomization(help="When to rerandomize the problem", default="always", scope=Scope.settings) data = String(help="XML data for the problem", scope=Scope.content) correct_map = Object(help="Dictionary with the correctness of current student answers", scope=Scope.user_state, default={}) @@ -78,7 +78,7 @@ class CapaFields(object): done = Boolean(help="Whether the student has answered the problem", scope=Scope.user_state) seed = StringyInteger(help="Random seed for this student", scope=Scope.user_state) weight = StringyFloat(help="How much to weight this problem by", scope=Scope.settings) - markdown = String(help="Markdown source of this module", scope=NON_EDITABLE_SETTINGS_SCOPE) + markdown = String(help="Markdown source of this module", scope=Scope.settings) source_code = String(help="Source code for LaTeX and Word problems. This feature is not well-supported.", scope=Scope.settings) @@ -894,3 +894,10 @@ class CapaDescriptor(CapaFields, RawDescriptor): 'problems/' + path[8:], path[8:], ] + + @property + def non_editable_metadata_fields(self): + non_editable_fields = super(CapaDescriptor, self).non_editable_metadata_fields + non_editable_fields.extend([CapaDescriptor.due, CapaDescriptor.graceperiod, + CapaDescriptor.force_save_button, CapaDescriptor.markdown]) + return non_editable_fields \ No newline at end of file diff --git a/common/lib/xmodule/xmodule/discussion_module.py b/common/lib/xmodule/xmodule/discussion_module.py index b4409f5216..3ee2f2bae6 100644 --- a/common/lib/xmodule/xmodule/discussion_module.py +++ b/common/lib/xmodule/xmodule/discussion_module.py @@ -1,6 +1,5 @@ from pkg_resources import resource_string -from .fields import NON_EDITABLE_SETTINGS_SCOPE from xmodule.x_module import XModule from xmodule.raw_module import RawDescriptor from xmodule.editing_module import MetadataOnlyEditingDescriptor @@ -8,11 +7,10 @@ from xblock.core import String, Scope class DiscussionFields(object): - discussion_id = String(scope=NON_EDITABLE_SETTINGS_SCOPE) + discussion_id = String(scope=Scope.settings) discussion_category = String(scope=Scope.settings) discussion_target = String(scope=Scope.settings) - # We may choose to enable this in the future, but while Kevin is investigating.... - sort_key = String(scope=NON_EDITABLE_SETTINGS_SCOPE) + sort_key = String(scope=Scope.settings) class DiscussionModule(DiscussionFields, XModule): @@ -39,3 +37,10 @@ class DiscussionDescriptor(DiscussionFields, MetadataOnlyEditingDescriptor, RawD metadata_translations = dict(RawDescriptor.metadata_translations) metadata_translations['id'] = 'discussion_id' metadata_translations['for'] = 'discussion_target' + + @property + def non_editable_metadata_fields(self): + non_editable_fields = super(DiscussionDescriptor, self).non_editable_metadata_fields + # We may choose to enable sort_keys in the future, but while Kevin is investigating.... + non_editable_fields.extend([DiscussionDescriptor.discussion_id, DiscussionDescriptor.sort_key]) + return non_editable_fields \ No newline at end of file diff --git a/common/lib/xmodule/xmodule/fields.py b/common/lib/xmodule/xmodule/fields.py index b79accb2b3..3d56b7941e 100644 --- a/common/lib/xmodule/xmodule/fields.py +++ b/common/lib/xmodule/xmodule/fields.py @@ -7,18 +7,11 @@ from xblock.core import ModelType import datetime import dateutil.parser -from xblock.core import Integer, Float, Boolean, Scope +from xblock.core import Integer, Float, Boolean log = logging.getLogger(__name__) -class NonEditableSettingsScope(Scope): - pass - -# Same scope as Settings.scope, but not intended to be edited by users (in Studio). -NON_EDITABLE_SETTINGS_SCOPE = NonEditableSettingsScope(user=Scope.settings.user, block=Scope.settings.block) - - class Date(ModelType): ''' Date fields know how to parse and produce json (iso) compatible formats. diff --git a/common/lib/xmodule/xmodule/mako_module.py b/common/lib/xmodule/xmodule/mako_module.py index 02db0c602f..8abb1d7777 100644 --- a/common/lib/xmodule/xmodule/mako_module.py +++ b/common/lib/xmodule/xmodule/mako_module.py @@ -1,7 +1,4 @@ from .x_module import XModuleDescriptor, DescriptorSystem -from .fields import NonEditableSettingsScope -from xblock.core import Scope -from xblock.core import XBlock class MakoDescriptorSystem(DescriptorSystem): @@ -43,33 +40,3 @@ class MakoModuleDescriptor(XModuleDescriptor): return self.system.render_template( self.mako_template, self.get_context()) - @property - def editable_metadata_fields(self): - inherited_metadata = getattr(self, '_inherited_metadata', {}) - metadata = {} - for field in self.fields: - - if field.scope != Scope.settings or isinstance(field.scope, NonEditableSettingsScope): - continue - - # We are not allowing editing of xblock tag and name fields at this time (for any component). - if field == XBlock.tags or field == XBlock.name: - continue - - inherited = False - default = False - value = getattr(self, field.name) - if field.name in self._model_data: - default = False - if field.name in inherited_metadata and self._model_data.get(field.name) == inherited_metadata.get( - field.name): - inherited = True - else: - default = True - - metadata[field.name] = {'field' : field, - 'value': value, - 'is_inherited': inherited, - 'is_default': default } - - return metadata diff --git a/common/lib/xmodule/xmodule/tests/test_mako_module.py b/common/lib/xmodule/xmodule/tests/test_xml_module.py similarity index 73% rename from common/lib/xmodule/xmodule/tests/test_mako_module.py rename to common/lib/xmodule/xmodule/tests/test_xml_module.py index 7686e2a69e..06d5b0b0a3 100644 --- a/common/lib/xmodule/xmodule/tests/test_mako_module.py +++ b/common/lib/xmodule/xmodule/tests/test_xml_module.py @@ -1,17 +1,17 @@ from xmodule.x_module import XModuleFields from xblock.core import Scope, String, Object -from xmodule.fields import Date, StringyInteger, NON_EDITABLE_SETTINGS_SCOPE -from xmodule.mako_module import MakoModuleDescriptor +from xmodule.fields import Date, StringyInteger +from xmodule.xml_module import XmlDescriptor import unittest from . import test_system from mock import Mock class TestFields(object): - # Will be returned by editable_metadata_fields because Scope.settings. + # Will be returned by editable_metadata_fields. max_attempts = StringyInteger(scope=Scope.settings) - # Will not be returned by editable_metadata_fields because declared as non-editable Scope.settings. - due = Date(scope=NON_EDITABLE_SETTINGS_SCOPE) + # Will not be returned by editable_metadata_fields because filtered out by non_editable_metadata_fields. + due = Date(scope=Scope.settings) # Will not be returned by editable_metadata_fields because is not Scope.settings. student_answers = Object(scope=Scope.user_state) # Will be returned, and can override the inherited value from XModule. @@ -21,14 +21,15 @@ class TestFields(object): class EditableMetadataFieldsTest(unittest.TestCase): def test_display_name_field(self): - editable_fields = self.get_mako_editable_fields({}) + editable_fields = self.get_xml_editable_fields({}) # Tests that the xblock fields (currently tags and name) get filtered out. - self.assertEqual(1, len(editable_fields), "Expected only 1 editable field for mako descriptor.") + # Also tests that xml_attributes is filtered out of XmlDescriptor. + self.assertEqual(1, len(editable_fields), "Expected only 1 editable field for xml descriptor.") self.assert_display_name_default(editable_fields) def test_override_default(self): # Tests that is_default is correct when a value overrides the default. - editable_fields = self.get_mako_editable_fields({'display_name': 'foo'}) + editable_fields = self.get_xml_editable_fields({'display_name': 'foo'}) display_name = editable_fields['display_name'] self.assertFalse(display_name['is_default']) self.assertEqual('foo', display_name['value']) @@ -47,14 +48,19 @@ class EditableMetadataFieldsTest(unittest.TestCase): self.assert_field_values(editable_fields, 'display_name', XModuleFields.display_name, False, True, 'inherited') # Start of helper methods - def get_mako_editable_fields(self, model_data): + def get_xml_editable_fields(self, model_data): system = test_system() system.render_template = Mock(return_value="