diff --git a/cms/djangoapps/contentstore/views.py b/cms/djangoapps/contentstore/views.py index be8ff491c8..b8da38f2c7 100644 --- a/cms/djangoapps/contentstore/views.py +++ b/cms/djangoapps/contentstore/views.py @@ -45,6 +45,8 @@ from auth.authz import get_user_by_email, add_user_to_course_group, remove_user_ from auth.authz import ADMIN_ROLE_NAME, EDITOR_ROLE_NAME from .utils import get_course_location_for_item +from xmodule.templates import all_templates + log = logging.getLogger(__name__) @@ -128,7 +130,8 @@ def course_index(request, org, course, name): return render_to_response('overview.html', { 'sections': sections, - 'upload_asset_callback_url': upload_asset_callback_url + 'upload_asset_callback_url': upload_asset_callback_url, + 'create_new_unit_template': Location('i4x', 'edx', 'templates', 'vertical', 'Empty') }) @@ -145,8 +148,9 @@ def edit_subsection(request, location): return HttpResponseBadRequest return render_to_response('edit_subsection.html', - {'subsection': item}) - + {'subsection': item, + 'create_new_unit_template': Location('i4x', 'edx', 'templates', 'vertical', 'Empty') + }) @login_required def edit_unit(request, location): @@ -205,6 +209,7 @@ def edit_unit(request, location): 'lms_link': lms_link, 'subsection': containing_subsection, 'section': containing_section, + 'create_new_unit_template': Location('i4x', 'edx', 'templates', 'vertical', 'Empty') }) @@ -222,10 +227,6 @@ def preview_component(request, location): }) -@login_required -def delete_unit(request, location): - pass - def user_author_string(user): '''Get an author string for commits by this user. Format: @@ -395,12 +396,31 @@ def get_module_previews(request, descriptor): preview_html.append(module.get_html()) return preview_html +def _delete_item(item, recurse=False): + if recurse: + children = item.get_children() + for child in children: + _delete_item(child, recurse) + + modulestore().delete_item(item.location); + @login_required @expect_json def delete_item(request): item_location = request.POST['id'] - modulestore().delete_item(item_location) + + # check permissions for this user within this course + if not has_access(request.user, item_location): + raise PermissionDenied() + + # optional parameter to delete all children (default False) + delete_children = request.POST.get('delete_children', False) + + item = modulestore().get_item(item_location) + + _delete_item(item, delete_children) + return HttpResponse() @@ -442,6 +462,8 @@ def save_item(request): def clone_item(request): parent_location = Location(request.POST['parent_location']) template = Location(request.POST['template']) + + display_name = request.POST.get('display_name') if not has_access(request.user, parent_location): raise PermissionDenied() @@ -453,6 +475,10 @@ def clone_item(request): # TODO: This needs to be deleted when we have proper storage for static content new_item.metadata['data_dir'] = parent.metadata['data_dir'] + + # replace the display name with an optional parameter passed in from the caller + if display_name is not None: + new_item.metadata['display_name'] = display_name modulestore().update_metadata(new_item.location.url(), new_item.own_metadata) modulestore().update_children(parent_location, parent.definition.get('children', []) + [new_item.location.url()]) diff --git a/cms/static/js/base.js b/cms/static/js/base.js index e02635ad42..66f76c265f 100644 --- a/cms/static/js/base.js +++ b/cms/static/js/base.js @@ -21,11 +21,104 @@ $(document).ready(function() { $('.unit-history ol a').bind('click', showHistoryModal); $modal.bind('click', hideModal); $modalCover.bind('click', hideHistoryModal); - $('.assets .upload-button').bind('click', showUploadModal); $('.upload-modal .close-button').bind('click', hideModal); + $('.unit .item-actions .delete-button').bind('click', deleteUnit); + $('.new-unit-item').bind('click', createNewUnit); + $('.save-subsection').bind('click', saveSubsection); + + // making the unit list sortable + $('.sortable-unit-list').sortable(); + $('.sortable-unit-list').disableSelection(); + $('.sortable-unit-list').bind('sortstop', onUnitReordered); }); +// This method only changes the ordering of the child objects in a subsection +function onUnitReordered() { + var subsection_id = $(this).data('subsection-id'); + + var _els = $(this).children('li:.leaf'); + + var children = new Array(); + for(var i=0;i<_els.length;i++) { + el = _els[i]; + children[i] = $(el).data('id'); + } + + // call into server to commit the new order + $.ajax({ + url: "/save_item", + type: "POST", + dataType: "json", + contentType: "application/json", + data:JSON.stringify({ 'id' : subsection_id, 'metadata' : null, 'data': null, 'children' : children}) + }); +} + +function saveSubsection(e) { + e.preventDefault(); + + var id = $(this).data('id'); + + // pull all metadata editable fields on page + var metadata_fields = $('input[data-metadata-name]'); + + metadata = {}; + for(var i=0; i< metadata_fields.length;i++) { + el = metadata_fields[i]; + metadata[$(el).data("metadata-name")] = el.value; + } + + children =[]; + + $.ajax({ + url: "/save_item", + type: "POST", + dataType: "json", + contentType: "application/json", + data:JSON.stringify({ 'id' : id, 'metadata' : metadata, 'data': null, 'children' : children}), + success: function() { + alert('Your changes have been saved.'); + }, + error: function() { + alert('There has been an error while saving your changes.'); + } + }); +} + +function createNewUnit(e) { + e.preventDefault(); + + parent = $(this).data('parent'); + template = $(this).data('template'); + + $.post('/clone_item', + {'parent_location' : parent, + 'template' : template, + 'display_name': 'New Unit', + }, + function(data) { + // redirect to the edit page + window.location = "/edit/" + data['id']; + }); +} + +function deleteUnit(e) { + e.preventDefault(); + + if(!confirm('Are you sure you wish to delete this item. It cannot be reversed!')) + return; + + var _li_el = $(this).parents('li.leaf'); + var id = _li_el.data('id'); + + $.post('/delete_item', + {'id': id, 'delete_children' : true}, + function(data) { + _li_el.remove(); + }); +} + function showUploadModal(e) { e.preventDefault(); $('.upload-modal').show(); diff --git a/cms/templates/edit_subsection.html b/cms/templates/edit_subsection.html index c9fe9fbda8..578e2a9ceb 100644 --- a/cms/templates/edit_subsection.html +++ b/cms/templates/edit_subsection.html @@ -12,11 +12,11 @@
- +
- +
@@ -55,7 +55,7 @@ hideshow
- Save + Save Preview
diff --git a/cms/templates/widgets/units.html b/cms/templates/widgets/units.html index 5bfafd46a0..8207b485a7 100644 --- a/cms/templates/widgets/units.html +++ b/cms/templates/widgets/units.html @@ -3,10 +3,10 @@ -<%def name="enum_units(subsection, actions=True, selected=None)"> -
    +<%def name="enum_units(subsection, actions=True, selected=None, sortable=True)"> +
      % for unit in subsection.get_children(): -
    1. +
    2. <% if unit.location == selected: selected_class = 'editing' @@ -21,17 +21,20 @@ This def will enumerate through a passed in subsection and list all of the units % if actions:
      - - + +
      % endif
    3. % endfor
    4. - + New Unit
    + + + diff --git a/cms/urls.py b/cms/urls.py index 2b6ee61cbd..44f42343f3 100644 --- a/cms/urls.py +++ b/cms/urls.py @@ -11,7 +11,6 @@ urlpatterns = ('', url(r'^$', 'contentstore.views.index', name='index'), url(r'^edit/(?P.*?)$', 'contentstore.views.edit_unit', name='edit_unit'), url(r'^subsection/(?P.*?)$', 'contentstore.views.edit_subsection', name='edit_subsection'), - 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'), diff --git a/common/lib/xmodule/xmodule/vertical_module.py b/common/lib/xmodule/xmodule/vertical_module.py index f0c26e045f..738ee9ac5f 100644 --- a/common/lib/xmodule/xmodule/vertical_module.py +++ b/common/lib/xmodule/xmodule/vertical_module.py @@ -45,5 +45,9 @@ class VerticalModule(XModule): class VerticalDescriptor(SequenceDescriptor): module_class = VerticalModule + # cdodge: override the SequenceDescript's template_dir_name to point to default template directory + template_dir_name = "default" + js = {'coffee': [resource_string(__name__, 'js/src/vertical/edit.coffee')]} js_module_name = "VerticalDescriptor" +