Merge pull request #826 from MITx/feature/cdodge/subsection-edit-page
Feature/cdodge/subsection edit page
This commit is contained in:
@@ -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"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user