Make it possible to create, edit, and publish a draft
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
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()
|
||||
|
||||
|
||||
@@ -479,6 +482,34 @@ def save_item(request):
|
||||
return HttpResponse()
|
||||
|
||||
|
||||
@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):
|
||||
|
||||
@@ -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')
|
||||
)
|
||||
@@ -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>
|
||||
</%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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user