diff --git a/cms/static/client_templates/metadata_number_entry.html b/cms/static/client_templates/metadata_number_entry.html index 5ccea65d5d..040488cb39 100644 --- a/cms/static/client_templates/metadata_number_entry.html +++ b/cms/static/client_templates/metadata_number_entry.html @@ -1,6 +1,6 @@
- + diff --git a/cms/static/js/views/metadata_editor_view.js b/cms/static/js/views/metadata_editor_view.js index a051255dd2..9a1c324732 100644 --- a/cms/static/js/views/metadata_editor_view.js +++ b/cms/static/js/views/metadata_editor_view.js @@ -30,15 +30,15 @@ CMS.Views.Metadata.Editor = Backbone.View.extend({ el: self.$el.find('.metadata_entry')[counter++], model: new CMS.Models.Metadata(item) }; - if (item.options.length > 0) { - // Right now, all our option types only hold strings. Should really support - // any type though. + if (item.type === 'Select') { self.views.push(new CMS.Views.Metadata.Option(data)); } + else if (item.type === 'Integer' || item.type === 'Float') { + self.views.push(new CMS.Views.Metadata.Number(data)); + } else { self.views.push(new CMS.Views.Metadata.String(data)); } - }); } ); @@ -151,6 +151,28 @@ CMS.Views.Metadata.String = CMS.Views.Metadata.AbstractEditor.extend({ } }); +CMS.Views.Metadata.Number = CMS.Views.Metadata.AbstractEditor.extend({ + + events : { + "change input" : "updateModel", + "keypress .setting-input" : "showClearButton" , + "click .setting-clear" : "clear" + }, + + getTemplateName : function () { + return "metadata_number_entry"; + }, + + getValueFromEditor : function () { + return this.$el.find('#' + this.uniqueId).val(); + }, + + setValueInEditor : function (value) { + this.$el.find('input').val(value); + } +}); + + CMS.Views.Metadata.Option = CMS.Views.Metadata.AbstractEditor.extend({ events : { diff --git a/common/lib/xmodule/xmodule/capa_module.py b/common/lib/xmodule/xmodule/capa_module.py index 91deb45a46..b33cf5fb8c 100644 --- a/common/lib/xmodule/xmodule/capa_module.py +++ b/common/lib/xmodule/xmodule/capa_module.py @@ -63,7 +63,8 @@ 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(display_name="Maximum Attempts", - help="When set, this specifies the number of times the student can try to answer this problem.", scope=Scope.settings) + help="This specifies the number of times the student can try to answer this problem. If unset, infinite attempts are allowed.", + values = {"min" : 1 }, scope=Scope.settings) 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=Scope.settings) showanswer = String(display_name="Show Answer", @@ -87,7 +88,8 @@ 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(display_name="Problem Weight", - help="Specifies the number of points the problem is worth. By default, each response field in the problem is worth one point.", + help="Specifies the number of points the problem is worth. If unset, each response field in the problem is worth one point.", + values = {"min" : 0 }, scope=Scope.settings) 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.", diff --git a/common/lib/xmodule/xmodule/combined_open_ended_module.py b/common/lib/xmodule/xmodule/combined_open_ended_module.py index 1e965e6c6b..b170fa9a7d 100644 --- a/common/lib/xmodule/xmodule/combined_open_ended_module.py +++ b/common/lib/xmodule/xmodule/combined_open_ended_module.py @@ -57,7 +57,8 @@ class CombinedOpenEndedFields(object): ready_to_reset = Boolean(help="If the problem is ready to be reset or not.", default=False, scope=Scope.user_state) attempts = Integer(display_name="Maximum Attempts", - help="Specifies the number of times the student can try to answer this problem.", default=1, scope=Scope.settings) + help="Specifies the number of times the student can try to answer this problem.", default=1, + scope=Scope.settings) # TODO: move values to Boolean in xblock. is_graded = Boolean(display_name="Graded", help="Whether or not the problem is graded.", default=False, scope=Scope.settings, values=[{'display_name': "True", "value": True}, {'display_name': "False", "value": False}]) diff --git a/common/lib/xmodule/xmodule/x_module.py b/common/lib/xmodule/xmodule/x_module.py index 3b094e04a5..5c22106f75 100644 --- a/common/lib/xmodule/xmodule/x_module.py +++ b/common/lib/xmodule/xmodule/x_module.py @@ -9,7 +9,7 @@ from pkg_resources import resource_listdir, resource_string, resource_isdir from xmodule.modulestore import Location from xmodule.modulestore.exceptions import ItemNotFoundError -from xblock.core import XBlock, Scope, String +from xblock.core import XBlock, Scope, String, Integer, Float log = logging.getLogger(__name__) @@ -649,17 +649,29 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock): 'inheritable': inheritable, 'explicitly_set': explicitly_set} + # We support the following editors: + # 1. A select editor for fields with a list of possible values (includes Booleans). + # 2. Number editor for integers and floats. + # 3. A generic string editor for anything else (editing JSON representation of the value). + type = "Generic" + # TODO: test all this logic values = [] if field.values is None else field.values - for index, choice in enumerate(values): - json_choice = choice - # TODO: test this logic. - if hasattr(json_choice, 'value'): - json_choice['value'] = field.to_json(json_choice['value']) - else: - json_choice = field.to_json(json_choice) - values[index] = json_choice - + if isinstance(values, list): + if len(values) > 0: + type = "Select" + for index, choice in enumerate(values): + json_choice = choice + if hasattr(json_choice, 'value'): + json_choice['value'] = field.to_json(json_choice['value']) + else: + json_choice = field.to_json(json_choice) + values[index] = json_choice + elif isinstance(field, Integer): + type = "Integer" + elif isinstance(field, Float): + type = "Float" simple_metadata[field.name] = {'field_name' : field.name, + 'type' : type, 'display_name' : field.display_name, 'value': field.to_json(value), 'options' : values,