diff --git a/cms/djangoapps/contentstore/views.py b/cms/djangoapps/contentstore/views.py
index 34b97a1179..1df2c44ff7 100644
--- a/cms/djangoapps/contentstore/views.py
+++ b/cms/djangoapps/contentstore/views.py
@@ -383,6 +383,14 @@ def get_module_previews(request, descriptor):
return preview_html
+@login_required
+@expect_json
+def delete_item(request):
+ item_location = request.POST['id']
+ modulestore().delete_item(item_location)
+ return HttpResponse()
+
+
@login_required
@expect_json
def save_item(request):
diff --git a/cms/static/coffee/src/views/module_edit.coffee b/cms/static/coffee/src/views/module_edit.coffee
index b41cf10851..2326756dc8 100644
--- a/cms/static/coffee/src/views/module_edit.coffee
+++ b/cms/static/coffee/src/views/module_edit.coffee
@@ -6,10 +6,10 @@ class CMS.Views.ModuleEdit extends Backbone.View
"click .component-editor .cancel-button": 'clickCancelButton'
"click .component-editor .save-button": 'clickSaveButton'
"click .component-actions .edit-button": 'clickEditButton'
-
+ "click .component-actions .delete-button": 'onDelete'
initialize: ->
- @module = @options.module
+ @onDelete = @options.onDelete
@render()
$component_editor: => @$el.find('.component-editor')
diff --git a/cms/static/coffee/src/views/unit.coffee b/cms/static/coffee/src/views/unit.coffee
index df5e714d3a..1180b17471 100644
--- a/cms/static/coffee/src/views/unit.coffee
+++ b/cms/static/coffee/src/views/unit.coffee
@@ -18,9 +18,10 @@ class CMS.Views.UnitEdit extends Backbone.View
update: (event, ui) => @saveOrder()
)
- @$('.component').each((idx, element) ->
+ @$('.component').each((idx, element) =>
new CMS.Views.ModuleEdit(
el: element,
+ onDelete: @deleteComponent,
model: new CMS.Models.Module(
id: $(element).data('id'),
)
@@ -70,3 +71,13 @@ class CMS.Views.UnitEdit extends Backbone.View
@model.save(
children: @components()
)
+
+ deleteComponent: (event) =>
+ $component = $(event.currentTarget).parents('.component')
+ $.post('/delete_item', {
+ id: $component.data('id')
+ }, =>
+ $component.remove()
+ @saveOrder()
+ )
+
diff --git a/cms/templates/component.html b/cms/templates/component.html
index 76a121fec6..f0d266e2e1 100644
--- a/cms/templates/component.html
+++ b/cms/templates/component.html
@@ -1,7 +1,7 @@
${preview}
diff --git a/cms/urls.py b/cms/urls.py
index e35c756c74..7cbb912b06 100644
--- a/cms/urls.py
+++ b/cms/urls.py
@@ -14,6 +14,7 @@ urlpatterns = ('',
url(r'^delete/(?P.*?)$', 'contentstore.views.delete_unit', name='delete_unit'),
url(r'^preview_component/(?P.*?)$', 'contentstore.views.preview_component', name='preview_component'),
url(r'^save_item$', 'contentstore.views.save_item', name='save_item'),
+ url(r'^delete_item$', 'contentstore.views.delete_item', name='delete_item'),
url(r'^clone_item$', 'contentstore.views.clone_item', name='clone_item'),
url(r'^(?P[^/]+)/(?P[^/]+)/course/(?P[^/]+)$',
'contentstore.views.course_index', name='course_index'),
diff --git a/common/lib/xmodule/xmodule/modulestore/__init__.py b/common/lib/xmodule/xmodule/modulestore/__init__.py
index fa8cf8d3d7..880159d8ed 100644
--- a/common/lib/xmodule/xmodule/modulestore/__init__.py
+++ b/common/lib/xmodule/xmodule/modulestore/__init__.py
@@ -332,6 +332,14 @@ class ModuleStore(object):
"""
raise NotImplementedError
+ def delete_item(self, location):
+ """
+ Delete an item from this modulestore
+
+ location: Something that can be passed to Location
+ """
+ raise NotImplementedError
+
def get_courses(self):
'''
Returns a list containing the top level XModuleDescriptors of the courses
diff --git a/common/lib/xmodule/xmodule/modulestore/mongo.py b/common/lib/xmodule/xmodule/modulestore/mongo.py
index baa4e7870c..21e28e9d67 100644
--- a/common/lib/xmodule/xmodule/modulestore/mongo.py
+++ b/common/lib/xmodule/xmodule/modulestore/mongo.py
@@ -309,6 +309,14 @@ class MongoModuleStore(ModuleStoreBase):
self._update_single_item(location, {'metadata': metadata})
+ def delete_item(self, location):
+ """
+ Delete an item from this modulestore
+
+ location: Something that can be passed to Location
+ """
+ self.collection.remove({'_id': Location(location).dict()})
+
def get_parent_locations(self, location):
'''Find all locations that are the parents of this location. Needed
for path_to_location().
diff --git a/common/lib/xmodule/xmodule/x_module.py b/common/lib/xmodule/xmodule/x_module.py
index 43b7cec3af..fd209ee26f 100644
--- a/common/lib/xmodule/xmodule/x_module.py
+++ b/common/lib/xmodule/xmodule/x_module.py
@@ -13,6 +13,7 @@ from xmodule.modulestore import Location
from xmodule.timeparse import parse_time
from xmodule.contentstore.content import StaticContent, XASSET_SRCREF_PREFIX
+from xmodule.modulestore.exceptions import ItemNotFoundError
log = logging.getLogger('mitx.' + __name__)
@@ -531,7 +532,11 @@ class XModuleDescriptor(Plugin, HTMLSnippet, ResourceTemplates):
if self._child_instances is None:
self._child_instances = []
for child_loc in self.definition.get('children', []):
- child = self.system.load_item(child_loc)
+ try:
+ child = self.system.load_item(child_loc)
+ except ItemNotFoundError:
+ log.exception('Unable to load item {loc}, skipping'.format(loc=child_loc))
+ continue
# TODO (vshnayder): this should go away once we have
# proper inheritance support in mongo. The xml
# datastore does all inheritance on course load.