diff --git a/cms/static/js/models/settings/course_details.js b/cms/static/js/models/settings/course_details.js
index 46a31bb9fe..91e211be95 100644
--- a/cms/static/js/models/settings/course_details.js
+++ b/cms/static/js/models/settings/course_details.js
@@ -33,20 +33,57 @@ CMS.Models.Settings.CourseDetails = Backbone.Model.extend({
return attributes;
},
+ validate: function(newattrs) {
+ // Returns either nothing (no return call) so that validate works or an object of {field: errorstring} pairs
+ // A bit funny in that the video key validation is asynchronous; so, it won't stop the validation.
+ var errors = {};
+ if (newattrs.start_date && newattrs.end_date && newattrs.start_date >= newattrs.end_date) {
+ errors.end_date = "The course end date cannot be before the course start date.";
+ }
+ if (newattrs.start_date && newattrs.enrollment_start && newattrs.start_date < newattrs.enrollment_start) {
+ errors.enrollment_start = "The course start date cannot be before the enrollment start date.";
+ }
+ if (newattrs.enrollment_start && newattrs.enrollment_end && newattrs.enrollment_start >= newattrs.enrollment_end) {
+ errors.enrollment_end = "The enrollment start date cannot be after the enrollment end date.";
+ }
+ if (newattrs.end_date && newattrs.enrollment_end && newattrs.end_date < newattrs.enrollment_end) {
+ errors.enrollment_end = "The enrollment end date cannot be after the course end date.";
+ }
+ if (newattrs.intro_video && newattrs.intro_video != this.get('intro_video')) {
+ var videos = this.parse_videosource(newattrs.intro_video);
+ var vid_errors = new Array();
+ var cachethis = this;
+ for (var i=0; i or just the "speed:key, *" string
// returns the videosource for the preview which iss the key whose speed is closest to 1
if (newsource == null) this.save({'intro_video': null});
+ // TODO remove all whitespace w/in string
else if (this._getNextMatch(this._videoprefix, newsource, 0)) this.save('intro_video', newsource);
else this.save('intro_video', '');
diff --git a/cms/static/js/views/settings/main_settings_view.js b/cms/static/js/views/settings/main_settings_view.js
index e28f40ffd2..f959a97d90 100644
--- a/cms/static/js/views/settings/main_settings_view.js
+++ b/cms/static/js/views/settings/main_settings_view.js
@@ -87,43 +87,85 @@ CMS.Views.Settings.Details = Backbone.View.extend({
initialize : function() {
// TODO move the html frag to a loaded asset
this.fileAnchorTemplate = _.template(' 📄<%= filename %>');
+ this.errorTemplate = _.template('<%= message %>');
+ this.model.on('error', this.handleValidationError, this);
},
render: function() {
- this.setupDatePicker('#course-start', 'start_date');
- this.setupDatePicker('#course-end', 'end_date');
- this.setupDatePicker('#enrollment-start', 'enrollment_start');
- this.setupDatePicker('#enrollment-end', 'enrollment_end');
+ this.setupDatePicker('start_date')
+ this.setupDatePicker('end_date')
+ this.setupDatePicker('enrollment_start')
+ this.setupDatePicker('enrollment_end')
if (this.model.has('syllabus')) {
- this.$el.find('.current-course-syllabus .doc-filename').html(
+ this.$el.find(this.fieldToSelectorMap['syllabus']).html(
this.fileAnchorTemplate({
fullpath : this.model.get('syllabus'),
filename: 'syllabus'}));
this.$el.find('.remove-course-syllabus').show();
}
else {
- this.$el.find('.current-course-syllabus .doc-filename').html("");
+ this.$el.find(this.fieldToSelectorMap['syllabus']).html("");
this.$el.find('.remove-course-syllabus').hide();
}
- this.$el.find('#course-overview').val(this.model.get('overview'));
+ this.$el.find(this.fieldToSelectorMap['overview']).val(this.model.get('overview'));
this.$el.find('.current-course-introduction-video iframe').attr('src', this.model.videosourceSample());
if (this.model.has('intro_video')) {
this.$el.find('.remove-course-introduction-video').show();
- this.$el.find('#course-introduction-video').val(this.model.getVideoSource());
+ this.$el.find(this.fieldToSelectorMap['intro_video']).val(this.model.getVideoSource());
}
else this.$el.find('.remove-course-introduction-video').hide();
- this.$el.find("#course-effort").val(this.model.get('effort'));
+ this.$el.find(this.fieldToSelectorMap['effort']).val(this.model.get('effort'));
return this;
},
+ fieldToSelectorMap : {
+ 'start_date' : "#course-start",
+ 'end_date' : '#course-end',
+ 'enrollment_start' : '#enrollment-start',
+ 'enrollment_end' : '#enrollment-end',
+ 'syllabus' : '.current-course-syllabus .doc-filename',
+ 'overview' : '#course-overview',
+ 'intro_video' : '#course-introduction-video',
+ 'effort' : "#course-effort"
+ },
- setupDatePicker : function(elementName, fieldName) {
+ _cacheValidationErrors : null,
+ handleValidationError : function(model, error) {
+ this._cacheValidationErrors = error;
+ // error is object w/ fields and error strings
+ for (var field in error) {
+ var ele = this.$el.find(this.fieldToSelectorMap[field]);
+ if ($(ele).is('div')) {
+ // put error on the contained inputs
+ $(ele).find('input, textarea').addClass('error');
+ }
+ else $(ele).addClass('error');
+ $(ele).parent().append(this.errorTemplate({message : error[field]}));
+ }
+ },
+
+ clearValidationErrors : function() {
+ if (this._cacheValidationErrors == null) return;
+ // error is object w/ fields and error strings
+ for (var field in this._cacheValidationErrors) {
+ var ele = this.$el.find(this.fieldToSelectorMap[field]);
+ if ($(ele).is('div')) {
+ // put error on the contained inputs
+ $(ele).find('input, textarea').removeClass('error');
+ }
+ else $(ele).removeClass('error');
+ $(ele).nextAll('.message-error').remove();
+ }
+ this._cacheValidationErrors = null;
+ },
+
+ setupDatePicker : function(fieldName) {
var cacheModel = this.model;
- var div = this.$el.find(elementName);
+ var div = this.$el.find(this.fieldToSelectorMap[fieldName]);
var datefield = $(div).find(".date");
var timefield = $(div).find(".time");
var savefield = function() {
@@ -143,7 +185,8 @@ CMS.Views.Settings.Details = Backbone.View.extend({
},
updateModel: function(event) {
- // figure out which field
+ this.clearValidationErrors();
+
switch (event.currentTarget.id) {
case 'course-start-date': // handled via onSelect method
case 'course-end-date':
@@ -181,7 +224,7 @@ CMS.Views.Settings.Details = Backbone.View.extend({
if (this.model.has('intro_video')) {
this.model.save_videosource(null);
this.$el.find(".current-course-introduction-video iframe").attr("src", "");
- this.$el.find('#course-introduction-video').val("");
+ this.$el.find(this.fieldToSelectorMap['intro_video']).val("");
}
}