diff --git a/cms/djangoapps/contentstore/views.py b/cms/djangoapps/contentstore/views.py index 6f428ee3e8..74ff6657ea 100644 --- a/cms/djangoapps/contentstore/views.py +++ b/cms/djangoapps/contentstore/views.py @@ -104,12 +104,14 @@ def edit_item(request): item = modulestore().get_item(item_location) item.get_html = wrap_xmodule(item.get_html, item, "xmodule_edit.html") + return render_to_response('unit.html', { 'contents': item.get_html(), 'js_module': item.js_module_name, 'category': item.category, 'url_name': item.url_name, 'previews': get_module_previews(request, item), + 'metadata': item.metadata }) @@ -284,6 +286,19 @@ def save_item(request): children = request.POST['children'] modulestore().update_children(item_location, children) + # cdodge: also commit any metadata which might have been passed along in the + # POST from the client, if it is there + # note, that the postback is not the complete metadata, as there's system metadata which is + # not presented to the end-user for editing. So let's fetch the original and + # 'apply' the submitted metadata, so we don't end up deleting system metadata + if request.POST['metadata']: + posted_metadata = request.POST['metadata'] + # fetch original + existing_item = modulestore().get_item(item_location) + # update existing metadata with submitted metadata (which can be partial) + existing_item.metadata.update(posted_metadata) + modulestore().update_metadata(item_location, existing_item.metadata) + # Export the course back to github # This uses wildcarding to find the course, which requires handling # multiple courses returned, but there should only ever be one diff --git a/cms/static/coffee/src/models/module.coffee b/cms/static/coffee/src/models/module.coffee index e2f911e9bd..52357795ed 100644 --- a/cms/static/coffee/src/models/module.coffee +++ b/cms/static/coffee/src/models/module.coffee @@ -3,14 +3,26 @@ class CMS.Models.Module extends Backbone.Model defaults: data: '' children: '' + metadata: {} loadModule: (element) -> elt = $(element).find('.xmodule_edit').first() @module = XModule.loadModule(elt) + # find the metadata edit region which should be setup server side, + # so that we can wire up posting back those changes + @metadata_elt = $(element).find('.metadata_edit') editUrl: -> "/edit_item?#{$.param(id: @get('id'))}" save: (args...) -> @set(data: @module.save()) if @module + # cdodge: package up metadata which is separated into a number of input fields + # there's probably a better way to do this, but at least this lets me continue to move onwards + if @metadata_elt + _metadata = {} + # walk through the set of elments which have the 'xmetadata_name' attribute and + # build up a object to pass back to the server on the subsequent POST + _metadata[$(el).data("metadata-name")]=el.value for el in $('[data-metadata-name]', @metadata_elt) + @set(metadata: _metadata) super(args...) diff --git a/cms/templates/widgets/html-edit.html b/cms/templates/widgets/html-edit.html index 1e86c6c734..9f7196b6e4 100644 --- a/cms/templates/widgets/html-edit.html +++ b/cms/templates/widgets/html-edit.html @@ -1,3 +1,4 @@ +<%include file="metadata-edit.html" />
diff --git a/cms/templates/widgets/metadata-edit.html b/cms/templates/widgets/metadata-edit.html new file mode 100644 index 0000000000..62d5563047 --- /dev/null +++ b/cms/templates/widgets/metadata-edit.html @@ -0,0 +1,10 @@ +% if metadata: +
+

Metadata

+ +
+% endif diff --git a/cms/templates/widgets/raw-edit.html b/cms/templates/widgets/raw-edit.html index fbd1757be5..9488552be5 100644 --- a/cms/templates/widgets/raw-edit.html +++ b/cms/templates/widgets/raw-edit.html @@ -1,3 +1,4 @@ +<%include file="metadata-edit.html" />
diff --git a/cms/templates/widgets/sequence-edit.html b/cms/templates/widgets/sequence-edit.html index 242bfeae21..e9d796784d 100644 --- a/cms/templates/widgets/sequence-edit.html +++ b/cms/templates/widgets/sequence-edit.html @@ -29,6 +29,7 @@ + <%include file="metadata-edit.html" />
    diff --git a/common/djangoapps/util/json_request.py b/common/djangoapps/util/json_request.py index 391905e574..169a7e3fb4 100644 --- a/common/djangoapps/util/json_request.py +++ b/common/djangoapps/util/json_request.py @@ -6,7 +6,9 @@ import json def expect_json(view_function): @wraps(view_function) def expect_json_with_cloned_request(request, *args, **kwargs): - if request.META['CONTENT_TYPE'] == "application/json": + # cdodge: fix postback errors in CMS. The POST 'content-type' header can include additional information + # e.g. 'charset', so we can't do a direct string compare + if request.META['CONTENT_TYPE'].lower().startswith("application/json"): cloned_request = copy.copy(request) cloned_request.POST = cloned_request.POST.copy() cloned_request.POST.update(json.loads(request.body)) diff --git a/common/lib/xmodule/xmodule/editing_module.py b/common/lib/xmodule/xmodule/editing_module.py index 67a4d66dad..833d994b99 100644 --- a/common/lib/xmodule/xmodule/editing_module.py +++ b/common/lib/xmodule/xmodule/editing_module.py @@ -17,13 +17,13 @@ class EditingDescriptor(MakoModuleDescriptor): js = {'coffee': [resource_string(__name__, 'js/src/raw/edit.coffee')]} js_module_name = "RawDescriptor" - def get_context(self): - return { - 'module': self, - 'data': self.definition.get('data', ''), - # TODO (vshnayder): allow children and metadata to be edited. - #'children' : self.definition.get('children, ''), - # TODO: show both own metadata and inherited? - #'metadata' : self.own_metadata, - } + # cdodge: a little refactoring here, since we're basically doing the same thing + # here as with our parent class, let's call into it to get the basic fields + # set and then add our additional fields. Trying to keep it DRY. + def get_context(self): + _context = MakoModuleDescriptor.get_context(self) + # Add our specific template information (the raw data body) + _context.update({ 'data' : self.definition.get('data','') }) + return _context + diff --git a/common/lib/xmodule/xmodule/mako_module.py b/common/lib/xmodule/xmodule/mako_module.py index eedac99aa8..f5f2fae23b 100644 --- a/common/lib/xmodule/xmodule/mako_module.py +++ b/common/lib/xmodule/xmodule/mako_module.py @@ -1,4 +1,5 @@ from x_module import XModuleDescriptor, DescriptorSystem +import logging class MakoDescriptorSystem(DescriptorSystem): @@ -31,8 +32,18 @@ class MakoModuleDescriptor(XModuleDescriptor): """ Return the context to render the mako template with """ - return {'module': self} + return {'module': self, + 'metadata': self.metadata, + 'editable_metadata_fields' : self.editable_metadata_fields + } def get_html(self): return self.system.render_template( self.mako_template, self.get_context()) + + # cdodge: encapsulate a means to expose "editable" metadata fields (i.e. not internal system metadata) + @property + def editable_metadata_fields(self): + subset = [name for name in self.metadata.keys() if name not in self.system_metadata_fields] + return subset + diff --git a/common/lib/xmodule/xmodule/seq_module.py b/common/lib/xmodule/xmodule/seq_module.py index 841936cf17..8592d4b38c 100644 --- a/common/lib/xmodule/xmodule/seq_module.py +++ b/common/lib/xmodule/xmodule/seq_module.py @@ -9,6 +9,7 @@ from xmodule.x_module import XModule from xmodule.progress import Progress from xmodule.exceptions import NotFoundError from pkg_resources import resource_string +from .editing_module import EditingDescriptor log = logging.getLogger("mitx.common.lib.seq_module") @@ -95,7 +96,8 @@ class SequenceModule(XModule): 'element_id': self.location.html_id(), 'item_id': self.id, 'position': self.position, - 'tag': self.location.category} + 'tag': self.location.category + } self.content = self.system.render_template('seq_module.html', params) self.rendered = True diff --git a/common/lib/xmodule/xmodule/x_module.py b/common/lib/xmodule/xmodule/x_module.py index 8846926e05..90982d1955 100644 --- a/common/lib/xmodule/xmodule/x_module.py +++ b/common/lib/xmodule/xmodule/x_module.py @@ -352,6 +352,10 @@ class XModuleDescriptor(Plugin, HTMLSnippet): 'data_dir' ) + # cdodge: this is a list of metadata names which are 'system' metadata + # and should not be edited by an end-user + system_metadata_fields = [ 'data_dir' ] + # A list of descriptor attributes that must be equal for the descriptors to # be equal equality_attributes = ('definition', 'metadata', 'location',