Hopefully the course-info changes I had made w/o link destruction
This commit is contained in:
@@ -33,6 +33,31 @@ def get_course_location_for_item(location):
|
||||
|
||||
return location
|
||||
|
||||
def get_course_for_item(location):
|
||||
'''
|
||||
cdodge: for a given Xmodule, return the course that it belongs to
|
||||
NOTE: This makes a lot of assumptions about the format of the course location
|
||||
Also we have to assert that this module maps to only one course item - it'll throw an
|
||||
assert if not
|
||||
'''
|
||||
item_loc = Location(location)
|
||||
|
||||
# @hack! We need to find the course location however, we don't
|
||||
# know the 'name' parameter in this context, so we have
|
||||
# to assume there's only one item in this query even though we are not specifying a name
|
||||
course_search_location = ['i4x', item_loc.org, item_loc.course, 'course', None]
|
||||
courses = modulestore().get_items(course_search_location)
|
||||
|
||||
# make sure we found exactly one match on this above course search
|
||||
found_cnt = len(courses)
|
||||
if found_cnt == 0:
|
||||
raise BaseException('Could not find course at {0}'.format(course_search_location))
|
||||
|
||||
if found_cnt > 1:
|
||||
raise BaseException('Found more than one course at {0}. There should only be one!!! Dump = {1}'.format(course_search_location, courses))
|
||||
|
||||
return courses[0]
|
||||
|
||||
|
||||
def get_lms_link_for_item(location, preview=False):
|
||||
location = Location(location)
|
||||
|
||||
@@ -42,6 +42,8 @@ import shutil
|
||||
import sys
|
||||
import tarfile
|
||||
import time
|
||||
from contentstore.course_info_model import get_course_updates,\
|
||||
update_course_updates, delete_course_update
|
||||
|
||||
# to install PIL on MacOSX: 'easy_install http://dist.repoze.org/PIL-1.1.6.tar.gz'
|
||||
|
||||
@@ -336,7 +338,7 @@ def edit_unit(request, location):
|
||||
def preview_component(request, location):
|
||||
# TODO (vshnayder): change name from id to location in coffee+html as well.
|
||||
if not has_access(request.user, location):
|
||||
raise Http404 # TODO (vshnayder): better error
|
||||
raise HttpResponseForbidden()
|
||||
|
||||
component = modulestore().get_item(location)
|
||||
|
||||
@@ -869,6 +871,9 @@ def edit_static(request, org, course, coursename):
|
||||
return render_to_response('edit-static-page.html', {})
|
||||
|
||||
|
||||
def settings(request, org, course, coursename):
|
||||
return render_to_response('settings.html', {})
|
||||
|
||||
def edit_tabs(request, org, course, coursename):
|
||||
location = ['i4x', org, course, 'course', coursename]
|
||||
course_item = modulestore().get_item(location)
|
||||
@@ -896,6 +901,61 @@ def server_error(request):
|
||||
return render_to_response('error.html', {'error': '500'})
|
||||
|
||||
|
||||
@login_required
|
||||
@ensure_csrf_cookie
|
||||
def course_info(request, org, course, name, provided_id=None):
|
||||
"""
|
||||
Send models and views as well as html for editing the course info to the client.
|
||||
|
||||
org, course, name: Attributes of the Location for the item to edit
|
||||
"""
|
||||
location = ['i4x', org, course, 'course', name]
|
||||
|
||||
# check that logged in user has permissions to this item
|
||||
if not has_access(request.user, location):
|
||||
raise PermissionDenied()
|
||||
|
||||
course_module = modulestore().get_item(location)
|
||||
|
||||
# get current updates
|
||||
location = ['i4x', org, course, 'course_info', "updates"]
|
||||
|
||||
return render_to_response('course_info.html', {
|
||||
'active_tab': 'courseinfo-tab',
|
||||
'context_course': course_module,
|
||||
'url_base' : "/" + org + "/" + course + "/",
|
||||
'course_updates' : json.dumps(get_course_updates(location))
|
||||
})
|
||||
|
||||
@expect_json
|
||||
@login_required
|
||||
@ensure_csrf_cookie
|
||||
def course_info_updates(request, org, course, provided_id=None):
|
||||
"""
|
||||
restful CRUD operations on course_info updates.
|
||||
|
||||
org, course: Attributes of the Location for the item to edit
|
||||
provided_id should be none if it's new (create) and a composite of the update db id + index otherwise.
|
||||
"""
|
||||
# ??? No way to check for access permission afaik
|
||||
# get current updates
|
||||
location = ['i4x', org, course, 'course_info', "updates"]
|
||||
# NB: we're setting Backbone.emulateHTTP to true on the client so everything comes as a post!!!
|
||||
if request.method == 'POST' and 'HTTP_X_HTTP_METHOD_OVERRIDE' in request.META:
|
||||
real_method = request.META['HTTP_X_HTTP_METHOD_OVERRIDE']
|
||||
else:
|
||||
real_method = request.method
|
||||
|
||||
if request.method == 'GET':
|
||||
return HttpResponse(json.dumps(get_course_updates(location)), mimetype="application/json")
|
||||
elif real_method == 'POST':
|
||||
# new instance (unless django makes PUT a POST): updates are coming as POST. Not sure why.
|
||||
return HttpResponse(json.dumps(update_course_updates(location, request.POST, provided_id)), mimetype="application/json")
|
||||
elif real_method == 'PUT':
|
||||
return HttpResponse(json.dumps(update_course_updates(location, request.POST, provided_id)), mimetype="application/json")
|
||||
elif real_method == 'DELETE': # coming as POST need to pull from Request Header X-HTTP-Method-Override DELETE
|
||||
return HttpResponse(json.dumps(delete_course_update(location, request.POST, provided_id)), mimetype="application/json")
|
||||
|
||||
@login_required
|
||||
@ensure_csrf_cookie
|
||||
def asset_index(request, org, course, name):
|
||||
|
||||
63
cms/static/coffee/src/views/course_info_edit.coffee
Normal file
63
cms/static/coffee/src/views/course_info_edit.coffee
Normal file
@@ -0,0 +1,63 @@
|
||||
## Derived from and should inherit from a common ancestor w/ ModuleEdit
|
||||
class CMS.Views.CourseInfoEdit extends Backbone.View
|
||||
tagName: 'div'
|
||||
className: 'component'
|
||||
|
||||
events:
|
||||
"click .component-editor .cancel-button": 'clickCancelButton'
|
||||
"click .component-editor .save-button": 'clickSaveButton'
|
||||
"click .component-actions .edit-button": 'clickEditButton'
|
||||
"click .component-actions .delete-button": 'onDelete'
|
||||
|
||||
initialize: ->
|
||||
@render()
|
||||
|
||||
$component_editor: => @$el.find('.component-editor')
|
||||
|
||||
loadDisplay: ->
|
||||
XModule.loadModule(@$el.find('.xmodule_display'))
|
||||
|
||||
loadEdit: ->
|
||||
if not @module
|
||||
@module = XModule.loadModule(@$el.find('.xmodule_edit'))
|
||||
|
||||
# don't show metadata (deprecated for course_info)
|
||||
render: ->
|
||||
if @model.id
|
||||
@$el.load("/preview_component/#{@model.id}", =>
|
||||
@loadDisplay()
|
||||
@delegateEvents()
|
||||
)
|
||||
|
||||
clickSaveButton: (event) =>
|
||||
event.preventDefault()
|
||||
data = @module.save()
|
||||
@model.save(data).done( =>
|
||||
# # showToastMessage("Your changes have been saved.", null, 3)
|
||||
@module = null
|
||||
@render()
|
||||
@$el.removeClass('editing')
|
||||
).fail( ->
|
||||
showToastMessage("There was an error saving your changes. Please try again.", null, 3)
|
||||
)
|
||||
|
||||
clickCancelButton: (event) ->
|
||||
event.preventDefault()
|
||||
@$el.removeClass('editing')
|
||||
@$component_editor().slideUp(150)
|
||||
|
||||
clickEditButton: (event) ->
|
||||
event.preventDefault()
|
||||
@$el.addClass('editing')
|
||||
@$component_editor().slideDown(150)
|
||||
@loadEdit()
|
||||
|
||||
onDelete: (event) ->
|
||||
# clear contents, don't delete
|
||||
@model.definition.data = "<ol></ol>"
|
||||
# TODO change label to 'clear'
|
||||
|
||||
onNew: (event) ->
|
||||
ele = $(@model.definition.data).find("ol")
|
||||
if (ele)
|
||||
ele = $(ele).first().prepend("<li><h2>" + $.datepicker.formatDate('MM d', new Date()) + "</h2>/n</li>");
|
||||
33
cms/templates/course_info.html
Normal file
33
cms/templates/course_info.html
Normal file
@@ -0,0 +1,33 @@
|
||||
<%inherit file="base.html" />
|
||||
<!-- TODO decode course # from context_course into title -->
|
||||
<%block name="title">Course Info</%block>
|
||||
|
||||
<%block name="jsextra">
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
$(document).ready(function(){
|
||||
editor = new CMS.Views.CourseInfoEdit({
|
||||
el: $('.course-updates'),
|
||||
model : new CMS.Models.Module({id : '${course_updates.location.url()}'})
|
||||
});
|
||||
$(".new-update-button").bind('click', editor.onNew);
|
||||
});
|
||||
</script>
|
||||
</%block>
|
||||
|
||||
<%block name="content">
|
||||
<div class="main-wrapper">
|
||||
<div class="inner-wrapper">
|
||||
<h1>Course Info</h1>
|
||||
<div class="main-column">
|
||||
<div class="window">
|
||||
<h2>Updates</h2>
|
||||
<a href="#" class="new-update-button">New Update</a>
|
||||
<div class="course-updates"></div>
|
||||
<!-- probably replace w/ a vertical where each element of the vertical is a separate update w/ a date and html field -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="sidebar window">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</%block>
|
||||
@@ -10,6 +10,7 @@
|
||||
<a href="${reverse('course_index', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, name=ctx_loc.name))}" class="class-name">${context_course.display_name}</a>
|
||||
<ul class="class-nav">
|
||||
<li><a href="${reverse('course_index', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, name=ctx_loc.name))}" id='courseware-tab'>Courseware</a></li>
|
||||
<li><a href="${reverse('course_info', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, name=ctx_loc.name))}" id='courseinfo-tab'>Course Info</a></li>
|
||||
<li><a href="${reverse('edit_tabs', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, coursename=ctx_loc.name))}" id='pages-tab'>Tabs</a></li>
|
||||
<li><a href="${reverse('asset_index', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, name=ctx_loc.name))}" id='assets-tab'>Assets</a></li>
|
||||
<li><a href="${reverse('manage_users', kwargs=dict(location=ctx_loc))}" id='users-tab'>Users</a></li>
|
||||
|
||||
@@ -34,7 +34,12 @@ urlpatterns = ('',
|
||||
'contentstore.views.remove_user', name='remove_user'),
|
||||
url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<name>[^/]+)/remove_user$',
|
||||
'contentstore.views.remove_user', name='remove_user'),
|
||||
url(r'^pages/(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<coursename>[^/]+)$', 'contentstore.views.static_pages', name='static_pages'),
|
||||
url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/info/(?P<name>[^/]+)$', 'contentstore.views.course_info', name='course_info'),
|
||||
# ??? Is the following necessary or will the one below work w/ id=None if not sent?
|
||||
# url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/course_info/updates$', 'contentstore.views.course_info_updates', name='course_info'),
|
||||
url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/course_info/updates/(?P<provided_id>.*)$', 'contentstore.views.course_info_updates', name='course_info'),
|
||||
url(r'^pages/(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<coursename>[^/]+)$', 'contentstore.views.static_pages',
|
||||
name='static_pages'),
|
||||
url(r'^edit_static/(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<coursename>[^/]+)$', 'contentstore.views.edit_static', name='edit_static'),
|
||||
url(r'^edit_tabs/(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<coursename>[^/]+)$', 'contentstore.views.edit_tabs', name='edit_tabs'),
|
||||
url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/assets/(?P<name>[^/]+)$', 'contentstore.views.asset_index', name='asset_index'),
|
||||
|
||||
Reference in New Issue
Block a user