Commit 580164e1 by Calen Pennington

Merge pull request #826 from MITx/feature/cdodge/subsection-edit-page

Feature/cdodge/subsection edit page
parents ab7377dc fe42f6fd
......@@ -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()])
......
......@@ -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();
......
......@@ -12,11 +12,11 @@
<article class="subsection-body window">
<div class="subsection-name-input">
<label>Display Name:</label>
<input type="text" value="Welcome to 6.002x" class="subsection-display-name-input" />
<input type="text" value="${subsection.metadata['display_name']}" class="subsection-display-name-input" data-metadata-name="display_name"/>
</div>
<div>
<label>Subtitle:</label>
<input type="text" value="Lecture Sequence" class="unit-subtitle" />
<input type="text" value="${subsection.metadata['subtitle'] if 'subtitle' in subsection.metadata else ''}" class="unit-subtitle" data-metadata-name="subtitle"/>
</div>
<div class="unit-list">
<label>Units:</label>
......@@ -55,7 +55,7 @@
<a href="#" class="toggle-off">hide</a><a href="#" class="large-toggle"></a><a href="#" class="toggle-on">show</a>
</div>
<div class="row unit-actions">
<a href="#" class="save-button">Save</a>
<a href="#" class="save-button save-subsection" data-id="${subsection.location}">Save</a>
<a href="preview.html" target="_blank" class="preview-button">Preview</a>
</div>
</div>
......
......@@ -3,10 +3,10 @@
<!--
This def will enumerate through a passed in subsection and list all of the units
-->
<%def name="enum_units(subsection, actions=True, selected=None)">
<ol>
<%def name="enum_units(subsection, actions=True, selected=None, sortable=True)">
<ol ${'class="sortable-unit-list"' if sortable else ''} data-subsection-id="${subsection.location}">
% for unit in subsection.get_children():
<li class="leaf">
<li class="leaf unit" data-id="${unit.location}">
<%
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
</a>
% if actions:
<div class="item-actions">
<a href="${reverse('delete_unit', args=[unit.location])}" classs="edit-button wip"><span class="delete-icon"></span></a>
<a href="#" class="drag-handle wip"></a>
<a href="#" class="delete-button" data-id="${unit.location}"><span class="delete-icon"></span></a>
<a href="#" class="drag-handle"></a>
</div>
% endif
</div>
</li>
% endfor
<li>
<a href="#" class="new-unit-item wip">
<a href="#" class="new-unit-item" data-template="${create_new_unit_template}" data-parent="${subsection.location}">
<span class="new-unit-icon"></span>New Unit
</a>
</li>
</ol>
</%def>
......@@ -11,7 +11,6 @@ urlpatterns = ('',
url(r'^$', 'contentstore.views.index', name='index'),
url(r'^edit/(?P<location>.*?)$', 'contentstore.views.edit_unit', name='edit_unit'),
url(r'^subsection/(?P<location>.*?)$', 'contentstore.views.edit_subsection', name='edit_subsection'),
url(r'^delete/(?P<location>.*?)$', 'contentstore.views.delete_unit', name='delete_unit'),
url(r'^preview_component/(?P<location>.*?)$', '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'),
......
......@@ -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"
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment