diff --git a/cms/djangoapps/contentstore/views.py b/cms/djangoapps/contentstore/views.py index 7ad349cf65..c810dc7df7 100644 --- a/cms/djangoapps/contentstore/views.py +++ b/cms/djangoapps/contentstore/views.py @@ -44,6 +44,8 @@ import sys import tarfile import time from contentstore import course_info_model +from models.settings.course_details import CourseDetails +from models.settings.course_details import CourseDetailsEncoder # to install PIL on MacOSX: 'easy_install http://dist.repoze.org/PIL-1.1.6.tar.gz' @@ -871,9 +873,6 @@ 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) @@ -949,13 +948,58 @@ def course_info_updates(request, org, course, provided_id=None): if request.method == 'GET': return HttpResponse(json.dumps(course_info_model.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(course_info_model.update_course_updates(location, request.POST, provided_id)), mimetype="application/json") elif real_method == 'PUT': return HttpResponse(json.dumps(course_info_model.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 + elif real_method == 'DELETE': return HttpResponse(json.dumps(course_info_model.delete_course_update(location, request.POST, provided_id)), mimetype="application/json") +@login_required +@ensure_csrf_cookie +def get_course_settings(request, org, course, name): + """ + Send models and views as well as html for editing the course settings 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) + + return render_to_response('settings.html', { + 'active_tab': 'settings-tab', + 'context_course': course_module, + 'course_details' : json.dumps(CourseDetails.fetch(location), cls=CourseDetailsEncoder) + }) + +@expect_json +@login_required +@ensure_csrf_cookie +def course_settings_updates(request, org, course, name, section): + """ + restful CRUD operations on course_info updates. This differs from get_course_settings by communicating purely + through json (not rendering any html) and handles section level operations rather than whole page. + + org, course: Attributes of the Location for the item to edit + section: one of details, faculty, grading, problems, discussions + """ + if section == 'details': + manager = CourseDetails + else: return + + if request.method == 'GET': + # Cannot just do a get w/o knowing the course name :-( + return HttpResponse(json.dumps(manager.fetch(Location(['i4x', org, course, 'course',name])), cls=CourseDetailsEncoder), + mimetype="application/json") + elif request.method == 'POST': # post or put, doesn't matter. + return HttpResponse(json.dumps(manager.update_from_json(request.POST), cls=CourseDetailsEncoder), + mimetype="application/json") + + @login_required @ensure_csrf_cookie def asset_index(request, org, course, name): diff --git a/cms/static/js/models/settings/course_detais.js b/cms/static/js/models/settings/course_detais.js index 8869280792..c725ed46a2 100644 --- a/cms/static/js/models/settings/course_detais.js +++ b/cms/static/js/models/settings/course_detais.js @@ -1,8 +1,8 @@ CMS.Models.Settings.CourseDetails = Backbone.Models.extend({ defaults: { - location : null, # a Location model, required - start_date: null, # maps to 'start' - end_date: null, # maps to 'end' + location : null, // the course's Location model, required + start_date: null, // maps to 'start' + end_date: null, // maps to 'end' enrollment_start: null, enrollment_end: null, syllabus: null, @@ -19,6 +19,7 @@ CMS.Models.Settings.CourseDetails = Backbone.Models.extend({ }, urlRoot: function() { - // TODO impl + var location = this.get('location'); + return '/' + location.get('org') + "/" + location.get('course') + '/settings/' + location.get('name') + '/section/details'; } }); diff --git a/cms/static/js/views/settings/main_settings_view.js b/cms/static/js/views/settings/main_settings_view.js index 91bc7134a4..5a862dd5ef 100644 --- a/cms/static/js/views/settings/main_settings_view.js +++ b/cms/static/js/views/settings/main_settings_view.js @@ -3,7 +3,6 @@ CMS.Views.Settings.Main = Backbone.View.extend({ // allow navigation between the tabs events: { 'click .settings-page-menu a': "showSettingsTab", - 'blur input' : 'updateModel' }, currentTab: null, @@ -72,19 +71,50 @@ CMS.Views.Settings.Main = Backbone.View.extend({ CMS.Views.Settings.Details = Backbone.View.extend({ // Model class is CMS.Models.Settings.CourseDetails events : { - "blur input" : "updateModel" + "blur input" : "updateModel", + 'click .remove-course-syllabus' : "removeSyllabus", + 'click .new-course-syllabus' : 'assetSyllabus', + 'click .remove-course-introduction-video' : "removeVideo", + 'click .new-course-introduction-video' : 'assetVideo', }, + initialize : function() { + // TODO move the html frag to a loaded asset + this.fileAnchorTemplate = _.template(' 📄<%= filename %>'); + // Save every change as it occurs. This may be too noisy!!! If not every change, then need sophisticated logic. + this.model.on('change', this.model.save); + }, + render: function() { if (this.model.has('start_date')) this.$el.find('#course-start-date').datepicker('setDate', this.model.get('start_date')); if (this.model.has('end_date')) this.$el.find('#course-end-date').datepicker('setDate', this.model.get('end_date')); if (this.model.has('enrollment_start')) this.$el.find('#course-enrollment-start').datepicker('setDate', this.model.get('enrollment_start')); if (this.model.has('enrollment_end')) this.$el.find('#course-enrollment-end').datepicker('setDate', this.model.get('enrollment_end')); + + if (this.model.has('syllabus')) { + this.$el.find('.current-course-syllabus .doc-filename').html( + this.fileAnchorTemplate({ + fullpath : this.model.get('syllabus'), + filename: 'syllabus'})); + this.$el.find('.remove-course-syllabus').show(); + } + else this.$el.find('.remove-course-syllabus').hide(); + + if (this.model.has('overview')) + this.$el.find('#course-overview').text(this.model.get('overview')); + + if (this.model.has('intro_video')) { + this.$el.find('.current-course-introduction-video iframe').attr('src', this.model.get('intro_video')); + this.$el.find('.remove-course-introduction-video').show(); + } + else this.$el.find('.remove-course-introduction-video').hide(); }, + updateModel: function(event) { // figure out which field switch (event.currentTarget.id) { case 'course-start-date': - this.model.set('start_date', $(event.currentTarget).datepicker('getDate')); + var val = $(event.currentTarget).datepicker('getDate'); + this.model.set('start_date', val); break; case 'course-end-date': this.model.set('end_date', $(event.currentTarget).datepicker('getDate')); @@ -96,10 +126,28 @@ CMS.Views.Settings.Details = Backbone.View.extend({ this.model.set('enrollment_end', $(event.currentTarget).datepicker('getDate')); break; + case 'course-overview': + this.model.set('overview', $(event.currentTarget).text()); + break; + default: break; } - // save the updated model - this.model.save(); + }, + + removeSyllabus: function() { + if (this.model.has('syllabus')) this.model.set({'syllabus': null}); + }, + + assetSyllabus : function() { + // TODO implement + }, + + removeVideo: function() { + if (this.model.has('intro_video')) this.model.set({'intro_video': null}); + }, + + assetVideo : function() { + // TODO implement } }); \ No newline at end of file diff --git a/cms/templates/settings.html b/cms/templates/settings.html index cb1bebeb89..fc58674847 100644 --- a/cms/templates/settings.html +++ b/cms/templates/settings.html @@ -28,8 +28,14 @@ el: $('.main-wrapper'), model : settingsModel) }); - $('.set-date').datepicker({ 'dateFormat': 'm/d/yy' }); + $('.set-date').datepicker({ 'dateFormat': 'm/d/yy' }); + + $(":input, textarea").focus(function() { + $("label[for='" + this.id + "']").addClass("is-focused"); + }).blur(function() { + $("label").removeClass("is-focused"); + }); editor.render(); }); @@ -42,11 +48,6 @@ var gradeThresholds; var GRADES = ['A', 'B', 'C', 'D', 'E']; - $(" :input, textarea").focus(function() { - $("label[for='" + this.id + "']").addClass("is-focused"); - }).blur(function() { - $("label").removeClass("is-focused"); - }); (function() { $body = $('body'); @@ -146,7 +147,7 @@