Commit 8a131446 by Calen Pennington

First pass at integrating the new unit editing wireframes with the CMS

parent 44805ca3
......@@ -118,7 +118,7 @@ def course_index(request, org, course, name):
@login_required
def edit_item(request):
def edit_item(request, location):
"""
Display an editing page for the specified module.
......@@ -127,11 +127,10 @@ def edit_item(request):
id: A Location URL
"""
# TODO (vshnayder): change name from id to location in coffee+html as well.
item_location = request.GET['id']
if not has_access(request.user, item_location):
if not has_access(request.user, location):
raise Http404 # TODO (vshnayder): better error
item = modulestore().get_item(item_location)
item = modulestore().get_item(location)
item.get_html = wrap_xmodule(item.get_html, item, "xmodule_edit.html")
if settings.LMS_BASE is not None:
......@@ -146,18 +145,17 @@ def edit_item(request):
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,
# TODO: It would be nice to able to use reverse here in some form, but we don't have the lms urls imported
'lms_link': lms_link,
'module': item,
'editable_preview': get_module_previews(request, item)[0],
})
@login_required
def delete_item(request, location):
pass
@login_required
def new_item(request):
"""
Display a page where the user can create a new item from a template
......@@ -277,6 +275,7 @@ def preview_module_system(request, preview_id, descriptor):
preview_id (str): An identifier specifying which preview this module is used for
descriptor: An XModuleDescriptor
"""
return ModuleSystem(
ajax_url=reverse('preview_dispatch', args=[preview_id, descriptor.location.url(), '']).rstrip('/'),
# TODO (cpennington): Do we want to track how instructors are using the preview problems?
......@@ -323,8 +322,10 @@ def load_preview_module(request, preview_id, descriptor, instance_state, shared_
error_msg=exc_info_to_str(sys.exc_info())
).xmodule_constructor(system)(None, None)
module.get_html = wrap_xmodule(module.get_html, module, "xmodule_display.html")
module.get_html = wrap_xmodule(module.get_html, module, "editable_preview.html")
module.get_html = replace_static_urls(
wrap_xmodule(module.get_html, module, "xmodule_display.html"),
module.get_html,
module.metadata.get('data_dir', module.location.course)
)
save_preview_state(request, preview_id, descriptor.location.url(),
......
......@@ -6,160 +6,162 @@ var $newComponentStep1;
var $newComponentStep2;
$(document).ready(function() {
$body = $('body');
$modal = $('.history-modal');
$modalCover = $('.modal-cover');
$newComponentItem = $('.new-component-item');
$newComponentStep1 = $('.new-component-step-1');
$newComponentStep2 = $('.new-component-step-2');
$newComponentButton = $('.new-component-button');
$('.expand-collapse-icon').bind('click', toggleSubmodules);
$('.visibility-options').bind('change', setVisibility);
$body.delegate('.components .edit-button', 'click', editComponent);
$body.delegate('.component-editor .save-button, .component-editor .cancel-button', 'click', closeComponentEditor);
$newComponentButton.bind('click', showNewComponentForm);
$newComponentStep1.find('.new-component-type a').bind('click', showNewComponentProperties);
$newComponentStep2.find('.save-button').bind('click', saveNewComponent);
$newComponentStep2.find('.cancel-button').bind('click', cancelNewComponent);
$('.unit-history ol a').bind('click', showHistoryModal);
$modal.bind('click', hideHistoryModal);
$modalCover.bind('click', hideHistoryModal);
$body = $('body');
$modal = $('.history-modal');
$modalCover = $('.modal-cover');
$newComponentItem = $('.new-component-item');
$newComponentStep1 = $('.new-component-step-1');
$newComponentStep2 = $('.new-component-step-2');
$newComponentButton = $('.new-component-button');
$('.expand-collapse-icon').bind('click', toggleSubmodules);
$('.visibility-options').bind('change', setVisibility);
$body.delegate('.components .edit-button', 'click', editComponent);
$body.delegate('.component-editor .save-button, .component-editor .cancel-button', 'click', closeComponentEditor);
$newComponentButton.bind('click', showNewComponentForm);
$newComponentStep1.find('.new-component-type a').bind('click', showNewComponentProperties);
$newComponentStep2.find('.save-button').bind('click', saveNewComponent);
$newComponentStep2.find('.cancel-button').bind('click', cancelNewComponent);
$('.unit-history ol a').bind('click', showHistoryModal);
$modal.bind('click', hideHistoryModal);
$modalCover.bind('click', hideHistoryModal);
XModule.loadModules('display');
});
function toggleSubmodules(e) {
e.preventDefault();
$(this).toggleClass('expand').toggleClass('collapse');
$(this).closest('.branch, .window').toggleClass('collapsed');
e.preventDefault();
$(this).toggleClass('expand').toggleClass('collapse');
$(this).closest('.branch, .window').toggleClass('collapsed');
}
function setVisibility(e) {
$(this).find('.checked').removeClass('checked');
$(e.target).closest('.option').addClass('checked');
$(this).find('.checked').removeClass('checked');
$(e.target).closest('.option').addClass('checked');
}
function editComponent(e) {
e.preventDefault();
$(this).closest('li').addClass('editing').find('.component-editor').slideDown(150);
e.preventDefault();
$(this).closest('li').addClass('editing').find('.component-editor').slideDown(150);
}
function closeComponentEditor(e) {
e.preventDefault();
$(this).closest('li').removeClass('editing').find('.component-editor').slideUp(150);
e.preventDefault();
$(this).closest('li').removeClass('editing').find('.component-editor').slideUp(150);
}
function showNewComponentForm(e) {
e.preventDefault();
$newComponentItem.addClass('adding');
$(this).slideUp(150);
$newComponentStep1.slideDown(150);
e.preventDefault();
$newComponentItem.addClass('adding');
$(this).slideUp(150);
$newComponentStep1.slideDown(150);
}
function showNewComponentProperties(e) {
e.preventDefault();
var displayName;
var componentSource;
var selectionRange;
var $renderedComponent;
switch($(this).attr('data-type')) {
case 'video':
displayName = 'Video';
componentSource = '<video youtube="1.50:___,1.25:___,1.0:___,0.75:___"/>';
selectionRange = [21, 24];
$renderedComponent = $('<div class="rendered-component"><div class="video-unit"><img src="images/video-module.png"></div></div>');
break;
case 'textbook':
displayName = 'Textbook';
componentSource = '<customtag page="___"><impl>book</impl></customtag>';
selectionRange = [17, 20];
$renderedComponent = $('<div class="rendered-component"><p><span class="textbook-icon"></span>More information given in the text.</p></div>');
break;
case 'slide':
displayName = 'Slide';
componentSource = '<customtag page="___"><customtag lecnum="___"><impl>slides</impl></customtag>';
selectionRange = [17, 20];
$renderedComponent = $('<div class="rendered-component"><p><span class="slides-icon"></span>Lecture Slides Handout [Clean] [Annotated]</p></div>');
break;
case 'discussion':
displayName = 'Discussion';
componentSource = '<discussion for="___" id="___" discussion_category="___"/>';
selectionRange = [17, 20];
$renderedComponent = $('<div class="rendered-component"><div class="discussion-unit"><img src="images/discussion-module.png"></div></div>');
break;
case 'problem':
displayName = 'Problem';
componentSource = '<problem>___</problem>';
selectionRange = [9, 12];
$renderedComponent = $('<div class="rendered-component"></div>');
break;
case 'freeform':
displayName = 'Freeform HTML';
componentSource = '';
selectionRange = [0, 0];
$renderedComponent = $('<div class="rendered-component"></div>');
break;
}
$newComponentItem.prepend($renderedComponent);
$renderedComponent.slideDown(250);
$newComponentStep2.find('h5').html('Edit ' + displayName + ' Component');
$newComponentStep2.find('textarea').html(componentSource);
setTimeout(function() {
$newComponentStep2.find('textarea').focus().get(0).setSelectionRange(selectionRange[0], selectionRange[1]);
}, 10);
$newComponentStep1.slideUp(250);
$newComponentStep2.slideDown(250);
e.preventDefault();
var displayName;
var componentSource;
var selectionRange;
var $renderedComponent;
switch($(this).attr('data-type')) {
case 'video':
displayName = 'Video';
componentSource = '<video youtube="1.50:___,1.25:___,1.0:___,0.75:___"/>';
selectionRange = [21, 24];
$renderedComponent = $('<div class="rendered-component"><div class="video-unit"><img src="images/video-module.png"></div></div>');
break;
case 'textbook':
displayName = 'Textbook';
componentSource = '<customtag page="___"><impl>book</impl></customtag>';
selectionRange = [17, 20];
$renderedComponent = $('<div class="rendered-component"><p><span class="textbook-icon"></span>More information given in the text.</p></div>');
break;
case 'slide':
displayName = 'Slide';
componentSource = '<customtag page="___"><customtag lecnum="___"><impl>slides</impl></customtag>';
selectionRange = [17, 20];
$renderedComponent = $('<div class="rendered-component"><p><span class="slides-icon"></span>Lecture Slides Handout [Clean] [Annotated]</p></div>');
break;
case 'discussion':
displayName = 'Discussion';
componentSource = '<discussion for="___" id="___" discussion_category="___"/>';
selectionRange = [17, 20];
$renderedComponent = $('<div class="rendered-component"><div class="discussion-unit"><img src="images/discussion-module.png"></div></div>');
break;
case 'problem':
displayName = 'Problem';
componentSource = '<problem>___</problem>';
selectionRange = [9, 12];
$renderedComponent = $('<div class="rendered-component"></div>');
break;
case 'freeform':
displayName = 'Freeform HTML';
componentSource = '';
selectionRange = [0, 0];
$renderedComponent = $('<div class="rendered-component"></div>');
break;
}
$newComponentItem.prepend($renderedComponent);
$renderedComponent.slideDown(250);
$newComponentStep2.find('h5').html('Edit ' + displayName + ' Component');
$newComponentStep2.find('textarea').html(componentSource);
setTimeout(function() {
$newComponentStep2.find('textarea').focus().get(0).setSelectionRange(selectionRange[0], selectionRange[1]);
}, 10);
$newComponentStep1.slideUp(250);
$newComponentStep2.slideDown(250);
}
function cancelNewComponent(e) {
e.preventDefault();
e.preventDefault();
$newComponentStep2.slideUp(250);
$newComponentButton.slideDown(250);
$newComponentItem.removeClass('adding');
$newComponentItem.find('.rendered-component').remove();
$newComponentStep2.slideUp(250);
$newComponentButton.slideDown(250);
$newComponentItem.removeClass('adding');
$newComponentItem.find('.rendered-component').remove();
}
function saveNewComponent(e) {
e.preventDefault();
var $newComponent = $newComponentItem.clone();
$newComponent.removeClass('adding').removeClass('new-component-item');
$newComponent.find('.new-component-step-2').removeClass('new-component-step-2').addClass('component-editor');
setTimeout(function() {
$newComponent.find('.component-editor').slideUp(250);
}, 10);
$newComponent.append('<div class="component-actions"><a href="#" class="edit-button"><span class="edit-icon white"></span>Edit</a><a href="#" class="delete-button"><span class="delete-icon white"></span>Delete</a> </div><a href="#" class="drag-handle"></a>');
$newComponent.find('.new-component-step-1').remove();
$newComponent.find('.new-component-button').remove();
$newComponentStep2.slideUp(250);
$newComponentButton.slideDown(250);
$newComponentItem.removeClass('adding');
$newComponentItem.find('.rendered-component').remove();
$newComponentItem.before($newComponent);
e.preventDefault();
var $newComponent = $newComponentItem.clone();
$newComponent.removeClass('adding').removeClass('new-component-item');
$newComponent.find('.new-component-step-2').removeClass('new-component-step-2').addClass('component-editor');
setTimeout(function() {
$newComponent.find('.component-editor').slideUp(250);
}, 10);
$newComponent.append('<div class="component-actions"><a href="#" class="edit-button"><span class="edit-icon white"></span>Edit</a><a href="#" class="delete-button"><span class="delete-icon white"></span>Delete</a> </div><a href="#" class="drag-handle"></a>');
$newComponent.find('.new-component-step-1').remove();
$newComponent.find('.new-component-button').remove();
$newComponentStep2.slideUp(250);
$newComponentButton.slideDown(250);
$newComponentItem.removeClass('adding');
$newComponentItem.find('.rendered-component').remove();
$newComponentItem.before($newComponent);
}
function showHistoryModal(e) {
e.preventDefault();
e.preventDefault();
$modal.show();
$modalCover.show();
$modal.show();
$modalCover.show();
}
function hideHistoryModal(e) {
e.preventDefault();
e.preventDefault();
$modal.hide();
$modalCover.hide();
$modal.hide();
$modalCover.hide();
}
......
<section class='editable-preview'>
${content}
<div class="component-actions">
<a href="#" class="edit-button"><span class="edit-icon white"></span>Edit</a>
<a href="#" class="delete-button"><span class="delete-icon white"></span>Delete</a>
</div>
<a href="#" class="drag-handle"></a>
<div class="component-editor">
<h5>Edit Video Component</h5>
<textarea class="component-source"><video youtube="1.50:q1xkuPsOY6Q,1.25:9WOY2dHz5i4,1.0:4rpg8Bq6hb4,0.75:KLim9Xkp7IY"/></textarea>
<a href="#" class="save-button">Save</a><a href="#" class="cancel-button">Cancel</a>
</div>
<section>
<%inherit file="base.html" />
<%! from django.core.urlresolvers import reverse %>
<%block name="title">CMS Courseware Overview</%block>
<%include file="widgets/header.html"/>
<%def name="branch(section)">
<li class="branch collapsed">
......@@ -11,54 +11,62 @@
<a href="#" class="draft-item">
<span class="${section.category}-icon"></span>${section.display_name} <span class="draft-tag">– draft</span>
</a>
<div class="item-actions">
<a href="#" class="edit-button"><span class="edit-icon"></span></a>
<a href="#" class="edit-button"><span class="delete-icon"></span></a>
<a href="#" class="drag-handle"></a>
</div>
${actions(section)}
</div>
<ol>
% for unit in section.get_children():
${branch(unit)}
% endfor
${new_unit()}
</ol>
</li>
</%def>
<%def name="actions(unit)">
<div class="item-actions">
<a href="${reverse('edit_item', args=[unit.location])}" class="edit-button"><span class="edit-icon"></span></a>
<a href="${reverse('delete_item', args=[unit.location])}" class="edit-button"><span class="delete-icon"></span></a>
<a href="#" class="drag-handle"></a>
</div>
</%def>
<%def name="new_unit()">
<li>
<a href="#" class="section-item new-unit-item">
<span class="plus-icon"></span>New Unit
</a>
</li>
</%def>
<%block name="content">
<div class="main-wrapper">
<div class="inner-wrapper">
<h1>Courseware</h1>
<input type="text" class="courseware-unit-search-input search" placeholder="search units" />
<article class="courseware-overview">
<a href="#" class="new-courseware-section-button"><span class="plus-icon"></span> New Section</a>
% for week in weeks:
<section class="courseware-section branch">
<header>
<a href="#" class="expand-collapse-icon collapse"></a>
<div class="item-details">
<h3>${week.display_name}</h3>
<h4><strong>Unscheduled:</strong> <a href="#">click here to set</a></h4>
</div>
<div class="item-actions">
<a href="#" class="edit-button"><span class="edit-icon"></span></a>
<a href="#" class="edit-button"><span class="delete-icon"></span></a>
<a href="#" class="drag-handle"></a>
</div>
</header>
<ol>
% for section in week.get_children():
${branch(section)}
% endfor
</ol>
</section>
% endfor
</article>
</div>
<div class="main-wrapper">
<div class="inner-wrapper">
<h1>Courseware</h1>
<input type="text" class="courseware-unit-search-input search" placeholder="search units" />
<article class="courseware-overview">
<a href="#" class="new-courseware-section-button"><span class="plus-icon"></span> New Section</a>
% for week in weeks:
<section class="courseware-section branch">
<header>
<a href="#" class="expand-collapse-icon collapse"></a>
<div class="item-details">
<h3>${week.display_name}</h3>
<h4><strong>Unscheduled:</strong> <a href="#">click here to set</a></h4>
</div>
${actions(week)}
</header>
<ol>
% for section in week.get_children():
${branch(section)}
% endfor
${new_unit()}
</ol>
</section>
% endfor
</article>
</div>
<%include file="widgets/upload_assets.html"/>
<footer></footer>
</div>
<footer></footer>
</%block>
<section id="unit-wrapper">
<header>
<section>
<h1 class="editable">${url_name}</h1>
<p class="${category}"><a href="#">${category}</a></p>
</section>
<%inherit file="base.html" />
<%! from django.core.urlresolvers import reverse %>
<%block name="title">CMS Unit</%block>
<%block name="content">
<div class="main-wrapper">
<div class="inner-wrapper">
<article class="unit-body window">
<nav class="breadcrumbs">
<ul>
<li><a href="#">Week 2</a></li>
<li><a href="#">Linearity and Superposition</a></li>
<li><span class="current-page">S3V2: Properties of Linearity</span></li>
</ul>
</nav>
${editable_preview}
</article>
<div class="actions">
<a href="#" class="cancel">Cancel</a>
<a href="" class="save-update">Save &amp; Update</a>
</div>
</header>
<section>
<section class="meta wip">
<section class="status-settings">
<ul>
<li><a href="#" class="current">Scrap</a></li>
<li><a href="#">Draft</a></li>
<li><a href="#">Proofed</a></li>
<li><a href="#">Published</a></li>
</ul>
<a href="#" class="settings">Settings</a>
</section>
<section class="author">
<dl>
<dt>Last modified:</dt>
<dd>mm/dd/yy</dd>
<dt>By</dt>
<dd>Anant Agarwal</dd>
</dl>
</section>
<section class="tags">
<div>
<h2>Tags:</h2>
<p class="editable">Click to edit</p>
<div class="sidebar">
<div class="unit-properties window">
<h4>Properties</h4>
<div class="window-contents">
<div class="row"><label>Display Name:</label><input type="text" value="${module.display_name}" /></div>
<div class="row">
<label>State:</label>
<div class="visibility-options">
<span class="option checked"><input type="radio" name="visibility" id="visibility-hidden" checked /><label for="visibility-hidden">Draft</label></span>
<span class="option"><input type="radio" name="visibility" id="visibility-visible" /><label for="visibility-visible">Final</label></span>
</div>
</div>
<div class="row">
<a href="#" class="save-button">Save</a>
</div>
</div>
</div>
<div>
<h2>Goal</h2>
<p class="editable">Click to edit</p>
<div class="unit-history window collapsed">
<h4><a href="#" class="expand-collapse-icon expand"></a>History</h4>
<div class="window-contents">
<ol>
</ol>
</div>
</div>
</section>
</section>
${contents}
% if lms_link is not None:
<a class="lms-link" href="${lms_link}">View in LMS</a>
% endif
<section class="previews">
% for preview in previews:
<section class="preview">
${preview}
</section>
% endfor
</section>
<div class="actions">
<a href="" class="save-update">Save &amp; Update</a>
<a href="#" class="cancel">Cancel</a>
</div>
</div>
</div>
<div class="history-modal">
<div class="modal-body">
<h2>S3V2: Properties of Linearity</h2>
<div class="video-unit"><img src="images/video-module.png"></div>
<p><span class="textbook-icon"></span>More information given in the text.</p>
<p><span class="slides-icon"></span>Lecture Slides Handout [Clean] [Annotated]</p>
</div>
<div class="modal-actions">
<a href="#" class="revert-button">Revert to this version</a>
<a href="#" class="close-button">Close</a>
</div>
<%include file="widgets/notes.html"/>
</section>
</section>
</div>
<div class="modal-cover"></div>
</%block>
......@@ -10,7 +10,8 @@ import django.contrib.auth.views
urlpatterns = ('',
url(r'^$', 'contentstore.views.index', name='index'),
url(r'^new_item$', 'contentstore.views.new_item', name='new_item'),
url(r'^edit_item$', 'contentstore.views.edit_item', name='edit_item'),
url(r'^edit/(?P<location>.*?)$', 'contentstore.views.edit_item', name='edit_item'),
url(r'^delete/(?P<location>.*?)$', 'contentstore.views.delete_item', name='delete_item'),
url(r'^save_item$', 'contentstore.views.save_item', name='save_item'),
url(r'^clone_item$', 'contentstore.views.clone_item', name='clone_item'),
url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<name>[^/]+)$',
......
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