Commit 1a8532d8 by Calen Pennington

Make it possible to create, edit, and publish a draft

parent 8da37234
from django.conf import settings
from xmodule.modulestore import Location
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.draft import DRAFT
from xmodule.modulestore.exceptions import ItemNotFoundError
def get_course_location_for_item(location):
'''
......@@ -45,3 +48,22 @@ def get_lms_link_for_item(item):
return lms_link
def compute_unit_state(unit):
"""
Returns whether this unit is 'draft', 'public', or 'private'.
'draft' content is in the process of being edited, but still has a previous
version visible in the LMS
'public' content is locked and visible in the LMS
'private' content is editabled and not visible in the LMS
"""
if unit.location.revision == DRAFT:
try:
modulestore('direct').get_item(unit.location._replace(revision=None))
return 'draft'
except ItemNotFoundError:
return 'private'
else:
return 'public'
......@@ -43,7 +43,7 @@ from cache_toolbox.core import set_cached_content, get_cached_content, del_cache
from auth.authz import is_user_in_course_group_role, get_users_in_course_group_by_role
from auth.authz import get_user_by_email, add_user_to_course_group, remove_user_from_course_group
from auth.authz import INSTRUCTOR_ROLE_NAME, STAFF_ROLE_NAME
from .utils import get_course_location_for_item, get_lms_link_for_item
from .utils import get_course_location_for_item, get_lms_link_for_item, compute_unit_state
from xmodule.templates import all_templates
......@@ -210,6 +210,8 @@ def edit_unit(request, location):
containing_section_locs = modulestore().get_parent_locations(containing_subsection.location)
containing_section = modulestore().get_item(containing_section_locs[0])
unit_state = compute_unit_state(item)
return render_to_response('unit.html', {
'unit': item,
'components': components,
......@@ -217,7 +219,9 @@ 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')
'create_new_unit_template': Location('i4x', 'edx', 'templates', 'vertical', 'Empty'),
'unit_state': unit_state,
'release_date': None,
})
......@@ -235,7 +239,6 @@ def preview_component(request, location):
})
def user_author_string(user):
'''Get an author string for commits by this user. Format:
first last <email@email.com>.
......@@ -428,7 +431,7 @@ def delete_item(request):
item = modulestore().get_item(item_location)
_delete_item(item, delete_children)
return HttpResponse()
......@@ -481,6 +484,34 @@ def save_item(request):
@login_required
@expect_json
def create_draft(request):
location = request.POST['id']
# check permissions for this user within this course
if not has_access(request.user, location):
raise PermissionDenied()
# This clones the existing item location to a draft location (the draft is implicit,
# because modulestore is a Draft modulestore)
modulestore().clone_item(location, location)
return HttpResponse()
@login_required
@expect_json
def publish_draft(request):
location = request.POST['id']
# check permissions for this user within this course
if not has_access(request.user, location):
raise PermissionDenied()
modulestore().publish(location)
return HttpResponse()
@login_required
@expect_json
def clone_item(request):
parent_location = Location(request.POST['parent_location'])
template = Location(request.POST['template'])
......
......@@ -35,9 +35,9 @@ class CMS.Views.ModuleEdit extends Backbone.View
return _metadata
cloneTemplate: (template) ->
cloneTemplate: (parent, template) ->
$.post("/clone_item", {
parent_location: @$el.parent().data('id')
parent_location: parent
template: template
}, (data) =>
@model.set(id: data.id)
......
......@@ -5,7 +5,10 @@ class CMS.Views.UnitEdit extends Backbone.View
'click .new-component-templates .new-component-template a': 'saveNewComponent'
'click .new-component-templates .cancel-button': 'closeNewComponent'
'click .new-component-button': 'showNewComponentForm'
'click .unit-actions .save-button': 'save'
'click #save-draft': 'saveDraft'
'click #delete-draft': 'deleteDraft'
'click #create-draft': 'createDraft'
'click #publish-draft': 'publishDraft'
initialize: =>
@$newComponentItem = @$('.new-component-item')
......@@ -15,7 +18,6 @@ class CMS.Views.UnitEdit extends Backbone.View
@$('.components').sortable(
handle: '.drag-handle'
update: (event, ui) => @saveOrder()
)
@$('.component').each((idx, element) =>
......@@ -30,6 +32,7 @@ class CMS.Views.UnitEdit extends Backbone.View
@model.components = @components()
# New component creation
showNewComponentForm: (event) =>
event.preventDefault()
@$newComponentItem.addClass('adding')
......@@ -61,13 +64,16 @@ class CMS.Views.UnitEdit extends Backbone.View
@$newComponentItem.before(editor.$el)
editor.cloneTemplate($(event.currentTarget).data('location'))
editor.cloneTemplate(
@$el.data('id'),
$(event.currentTarget).data('location')
)
@closeNewComponent(event)
components: => @$('.component').map((idx, el) -> $(el).data('id')).get()
saveOrder: =>
saveDraft: =>
@model.save(
children: @components()
)
......@@ -81,3 +87,24 @@ class CMS.Views.UnitEdit extends Backbone.View
@saveOrder()
)
deleteDraft: (event) ->
$.post('/delete_item', {
id: @$el.data('id')
delete_children: true
}, =>
window.location.reload()
)
createDraft: (event) ->
$.post('/create_draft', {
id: @$el.data('id')
}, =>
@$el.toggleClass('edit-state-public edit-state-draft')
)
publishDraft: (event) ->
$.post('/publish_draft', {
id: @$el.data('id')
}, =>
@$el.toggleClass('edit-state-public edit-state-draft')
)
\ No newline at end of file
......@@ -394,3 +394,36 @@
}
}
}
.edit-state-draft {
.visibility {
display: none;
}
#create-draft {
display: none;
}
}
.edit-state-public {
#save-draft,
#delete-draft,
#publish-draft,
.component-actions,
.new-component-item {
display: none;
}
.drag-handle {
display: none !important;
}
}
.edit-state-private {
#save-draft,
#delete-draft,
#publish-draft,
#create-draft, {
display: none;
}
}
......@@ -14,12 +14,12 @@
</script>
</%block>
<%block name="content">
<div class="main-wrapper">
<div class="main-wrapper edit-state-${unit_state}" data-id="${unit.location.url()}">
<div class="inner-wrapper">
<div class="main-column">
<article class="unit-body window">
<p class="unit-name-input"><label>Display Name:</label><input type="text" value="${unit.display_name}" class="unit-display-name-input" /></p>
<ol class="components" data-id="${unit.location.url()}">
<ol class="components">
% for id in components:
<li class="component" data-id="${id}"/>
% endfor
......@@ -64,14 +64,6 @@
<div class="unit-properties window">
<h4>Unit Properties</h4>
<div class="window-contents">
<div class="due-date-input row">
<label>Due date:</label>
<a href="#" class="set-date">Set a due date</a>
<div class="date-setter">
<p class="date-description"><input type="text" value="10/20/2012" class="date-input" /> <input type="text" value="6:00 am" class="time-input" />
<a href="#" class="remove-date">Remove due date</a>
</div>
</div>
<div class="row visibility">
<label class="inline-label">Visibility:</label>
<select>
......@@ -79,11 +71,14 @@
<option>Private</option>
</select>
</div>
<a id="create-draft" href="#">This unit has been published. Click here to edit it.</a>
<a id="publish-draft" href="#">This unit has already been published. Click here to release your changes to it</a>
<div class="row status">
<p>This unit is scheduled to be released to <strong>students</strong> on <strong>10/12/2012</strong> with the subsection <a href="#">"Administrivia and Circuit Elements."</a></p>
<p>This unit is scheduled to be released to <strong>students</strong> on <strong>${release_date}</strong> with the subsection <a href="#">""</a></p>
</div>
<div class="row unit-actions">
<a href="#" class="save-button">Save</a>
<a id="save-draft" href="#" class="save-button">Save Draft</a>
<a id="delete-draft" href="#" class="save-button">Delete Draft</a>
<a href="${lms_link}" target="_blank" class="preview-button">Preview</a>
</div>
</div>
......@@ -114,4 +109,4 @@
</div>
</div>
</%block>
\ No newline at end of file
</%block>
......@@ -15,6 +15,8 @@ urlpatterns = ('',
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'^create_draft$', 'contentstore.views.create_draft', name='create_draft'),
url(r'^publish_draft$', 'contentstore.views.publish_draft', name='publish_draft'),
url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<name>[^/]+)$',
'contentstore.views.course_index', name='course_index'),
url(r'^github_service_hook$', 'github_sync.views.github_post_receive'),
......
......@@ -136,10 +136,12 @@ class DraftModuleStore(ModuleStoreBase):
"""
return super(DraftModuleStore, self).delete_item(Location(location)._replace(revision=DRAFT))
def get_parent_locations(self, location):
'''Find all locations that are the parents of this location. Needed
for path_to_location().
returns an iterable of things that can be passed to Location.
'''
return super(DraftModuleStore, self).get_parent_locations(Location(location)._replace(revision=DRAFT))
def publish(self, location):
"""
Save a current draft to the underlying modulestore
"""
draft = self.get_item(location)
super(DraftModuleStore, self).update_item(location, draft.definition.get('data', {}))
super(DraftModuleStore, self).update_children(location, draft.definition.get('children', []))
super(DraftModuleStore, self).update_metadata(location, draft.metadata)
self.delete_item(location)
......@@ -4,16 +4,22 @@ Settings for the LMS that runs alongside the CMS on AWS
from ..dev import *
modulestore_options = {
'default_class': 'xmodule.raw_module.RawDescriptor',
'host': 'localhost',
'db': 'xmodule',
'collection': 'modulestore',
'fs_root': DATA_DIR,
'render_template': 'mitxmako.shortcuts.render_to_string',
}
MODULESTORE = {
'default': {
'ENGINE': 'xmodule.modulestore.mongo.DraftMongoModuleStore',
'OPTIONS': modulestore_options
},
'direct': {
'ENGINE': 'xmodule.modulestore.mongo.MongoModuleStore',
'OPTIONS': {
'default_class': 'xmodule.raw_module.RawDescriptor',
'host': 'localhost',
'db': 'xmodule',
'collection': 'modulestore',
'fs_root': DATA_DIR,
'render_template': 'mitxmako.shortcuts.render_to_string',
}
'OPTIONS': modulestore_options
}
}
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