diff --git a/cms/static/js/models/settings/advanced.js b/cms/static/js/models/settings/advanced.js
index 40b39427fd..089c5bd22d 100644
--- a/cms/static/js/models/settings/advanced.js
+++ b/cms/static/js/models/settings/advanced.js
@@ -2,20 +2,15 @@ if (!CMS.Models['Settings']) CMS.Models.Settings = {};
CMS.Models.Settings.Advanced = Backbone.Model.extend({
defaults: {
- // the properties are whatever the user types in
+ // the properties are whatever the user types in (in addition to whatever comes originally from the server)
},
// which keys to send as the deleted keys on next save
deleteKeys : [],
blacklistKeys : [], // an array which the controller should populate directly for now [static not instance based]
- initialize: function() {
- console.log('in initialize');
- var editor = ace.edit('course-advanced-policy-1-value');
- editor.setTheme("ace/theme/chrome");
- editor.getSession().setMode("ace/mode/json");
- },
- validate: function(attrs) {
+
+ validate: function (attrs) {
var errors = {};
- for (key in attrs) {
+ for (var key in attrs) {
if (_.contains(this.blacklistKeys, key)) {
errors[key] = key + " is a reserved keyword or has another editor";
}
@@ -23,194 +18,3 @@ CMS.Models.Settings.Advanced = Backbone.Model.extend({
if (!_.isEmpty(errors)) return errors;
}
});
-
-if (!CMS.Views['Settings']) CMS.Views.Settings = {};
-
-CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({
- // Model class is CMS.Models.Settings.Advanced
- events : {
- 'click .delete-button' : "deleteEntry",
- 'click .save-button' : "saveView",
- 'click .cancel-button' : "revertView",
- 'click .new-button' : "addEntry",
- // update model on changes
- 'change #course-advanced-policy-key' : "updateKey",
- 'change #course-advanced-policy-value' : "updateValue"
- // TODO enable/disable save (add disabled class) based on validation & dirty
- // TODO enable/disable new button?
- },
- initialize : function() {
- var self = this;
- // instantiates an editor template for each update in the collection
- window.templateLoader.loadRemoteTemplate("advanced_entry",
- "/static/client_templates/advanced_entry.html",
- function (raw_template) {
- self.template = _.template(raw_template);
- self.render();
- }
- );
- this.model.on('error', this.handleValidationError, this);
- },
- render: function() {
- // catch potential outside call before template loaded
- if (!this.template) return this;
-
- var listEle$ = this.$el.find('.course-advanced-policy-list');
- listEle$.empty();
- // same object so manipulations to one keep the other up to date
- this.fieldToSelectorMap = this.selectorToField = {};
-
- // iterate through model and produce key : value editors for each property in model.get
- var self = this;
- _.each(_.sortBy(_.keys(this.model.attributes), _.identity),
- function(key) {
- listEle$.append(self.template({ key : key, value : self.model.get(key)}));
- self.fieldToSelectorMap[key] = key;
- });
-
- // insert the empty one
- this.addEntry();
- // Should this be on an event rather than render?
-// var editor = ace.edit('course-advanced-policy-1-value');
-// editor.setTheme("ace/theme/monokai");
-// editor.getSession().setMode("ace/mode/javascript");
-
- return this;
- },
- deleteEntry : function(event) {
- event.preventDefault();
- // find out which entry
- var li$ = $(event.currentTarget).closest('li');
- // Not data b/c the validation view uses it for a selector
- var key = $('.key', li$).attr('id');
-
- delete this.fieldToSelectorMap[key];
- if (key !== '__new_advanced_key__') {
- this.model.deleteKeys.push(key);
- delete this.model[key];
- }
- li$.remove();
- },
- saveView : function(event) {
- // TODO one last verification scan:
- // call validateKey on each to ensure proper format
- // check for dupes
-
- this.model.save({
- success : function() { window.alert("Saved"); },
- error : CMS.ServerError
- });
- // FIXME don't delete if the validation didn't succeed in the save call
- // remove deleted attrs
- if (!_.isEmpty(this.model.deleteKeys)) {
- var self = this;
- // hmmm, not sure how to do this via backbone since we're not destroying the model
- $.ajax({
- url : this.model.url,
- // json to and fro
- contentType : "application/json",
- dataType : "json",
- // delete
- type : 'DELETE',
- // data
- data : JSON.stringify({ deleteKeys : this.model.deleteKeys})
- })
- .fail(function(hdr, status, error) { CMS.ServerError(self.model, "Deleting keys:" + status); })
- .done(function(data, status, error) {
- // clear deleteKeys on success
- self.model.deleteKeys = [];
- });
- }
- },
- revertView : function(event) {
- this.model.deleteKeys = [];
- var self = this;
- this.model.clear({silent : true});
- this.model.fetch({
- success : function() { self.render(); },
- error : CMS.ServerError
- });
- },
- addEntry : function() {
- var listEle$ = this.$el.find('.course-advanced-policy-list');
- listEle$.append(this.template({ key : "", value : ""}));
- // disable the value entry until there's an acceptable key
- listEle$.find('.course-advanced-policy-value').addClass('disabled');
- this.fieldToSelectorMap['__new_advanced_key__'] = '__new_advanced_key__';
- },
- updateKey : function(event) {
- // old key: either the key as in the model or __new_advanced_key__. That is, it doesn't change as the val changes until val is accepted
- var oldKey = $(event.currentTarget).closest('.key').attr('id');
- var newKey = $(event.currentTarget).val();
- console.log('update ', oldKey, newKey); // REMOVE ME
- if (oldKey !== newKey) {
- // may erase other errors but difficult to just remove these
- this.clearValidationErrors();
-
- if (!this.validateKey(oldKey, newKey)) return;
-
- if (this.model.has(newKey)) {
- console.log('dupe key');
- var error = {};
- error[oldKey] = newKey + " has another entry";
- error[newKey] = "Other entry for " + newKey;
- this.model.trigger("error", this.model, error);
- return false;
- }
-
- // explicitly call validate to determine whether to proceed (relying on triggered error means putting continuation in the success
- // method which is uglier I think?)
- var newEntryModel = {};
- // set the new key's value to the old one's
- newEntryModel[newKey] = (oldKey === '__new_advanced_key__' ? '' : this.model.get(oldKey));
-
- var validation = this.model.validate(newEntryModel);
- if (validation) {
- console.log('reserved key');
- this.model.trigger("error", this.model, validation);
- // abandon update
- return;
- }
-
- // Now safe to actually do the update
- this.model.set(newEntryModel);
-
- delete this.fieldToSelectorMap[oldKey];
-
- if (oldKey !== '__new_advanced_key__') {
- // mark the old key for deletion and delete from field maps
- this.model.deleteKeys.push(oldKey);
- this.model.unset(oldKey) ;
- }
- else {
- // enable the value entry
- this.$el.find('.course-advanced-policy-value').removeClass('disabled');
- }
-
- // update gui (sets all the ids etc)
- $(event.currentTarget).closest('li').replaceWith(this.template({key : newKey, value : this.model.get(newKey) }));
-
- this.fieldToSelectorMap[newKey] = newKey;
- }
- },
- validateKey : function(oldKey, newKey) {
- // model validation can't handle malformed keys nor notice if 2 fields have same key; so, need to add that chk here
- // TODO ensure there's no spaces or illegal chars
- if (_.isEmpty(newKey)) {
- console.log('no key');
- var error = {};
- error[oldKey] = "Key cannot be an empty string";
- this.model.trigger("error", this.model, error);
- return false;
- }
- else return true;
- },
- updateValue : function(event) {
- // much simpler than key munging. just update the value
- var key = $(event.currentTarget).closest('.row').children('.key').attr('id');
- var value = $(event.currentTarget).val();
- console.log('updating ', key, value);
-
- this.model.set(key, value, {validate:true});
- }
-});
diff --git a/cms/static/js/views/settings/advanced_view.js b/cms/static/js/views/settings/advanced_view.js
new file mode 100644
index 0000000000..d6484c88af
--- /dev/null
+++ b/cms/static/js/views/settings/advanced_view.js
@@ -0,0 +1,198 @@
+if (!CMS.Views['Settings']) CMS.Views.Settings = {};
+
+CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({
+ // the key for a newly added policy-- before the user has entered a key value
+ new_key : "__new_advanced_key__",
+
+ // Model class is CMS.Models.Settings.Advanced
+ events : {
+ 'click .delete-button' : "deleteEntry",
+ 'click .save-button' : "saveView",
+ 'click .cancel-button' : "revertView",
+ 'click .new-button' : "addEntry",
+ // update model on changes
+ 'change #course-advanced-policy-key' : "updateKey",
+ 'change #course-advanced-policy-value' : "updateValue"
+ // TODO enable/disable save (add disabled class) based on validation & dirty
+ // TODO enable/disable new button?
+ },
+ initialize : function() {
+ var self = this;
+ // instantiates an editor template for each update in the collection
+ window.templateLoader.loadRemoteTemplate("advanced_entry",
+ "/static/client_templates/advanced_entry.html",
+ function (raw_template) {
+ self.template = _.template(raw_template);
+ self.render();
+ }
+ );
+ this.model.on('error', this.handleValidationError, this);
+ },
+ render: function() {
+ // catch potential outside call before template loaded
+ if (!this.template) return this;
+
+ var listEle$ = this.$el.find('.course-advanced-policy-list');
+ listEle$.empty();
+ // In this case, the fieldToSelectorMap (inherited from ValidatingView) use a map
+ // from the key to itself. Therefore the selectorToField map is the same object.
+ this.fieldToSelectorMap = this.selectorToField = {};
+
+ // iterate through model and produce key : value editors for each property in model.get
+ var self = this;
+ _.each(_.sortBy(_.keys(this.model.attributes), _.identity),
+ function(key) {
+ // TODO: working here
+ var newEl = self.template({ key : key, value : self.model.get(key)});
+ listEle$.append(newEl);
+
+ self.fieldToSelectorMap[key] = key;
+
+ // var editor = ace.edit('course-advanced-policy-1-value');
+ // editor.setTheme("ace/theme/chrome");
+ // editor.getSession().setMode("ace/mode/json");
+
+ });
+ return this;
+ },
+ deleteEntry : function(event) {
+ event.preventDefault();
+ // find out which entry
+ var li$ = $(event.currentTarget).closest('li');
+ // Not data b/c the validation view uses it for a selector
+ var key = $('.key', li$).attr('id');
+
+ delete this.fieldToSelectorMap[key];
+ if (key !== this.new_key) {
+ this.model.deleteKeys.push(key);
+ delete this.model[key];
+ }
+ li$.remove();
+ },
+ saveView : function(event) {
+ // TODO one last verification scan:
+ // call validateKey on each to ensure proper format
+ // check for dupes
+
+ this.model.save({},
+ {
+ success : function() {
+ window.alert("Saved");
+ },
+ error : CMS.ServerError
+ });
+ // FIXME don't delete if the validation didn't succeed in the save call
+ // remove deleted attrs
+ if (!_.isEmpty(this.model.deleteKeys)) {
+ var self = this;
+ // not able to do via backbone since we're not destroying the model
+ $.ajax({
+ url : this.model.url,
+ // json to and fro
+ contentType : "application/json",
+ dataType : "json",
+ // delete
+ type : 'DELETE',
+ // data
+ data : JSON.stringify({ deleteKeys : this.model.deleteKeys})
+ })
+ .fail(function(hdr, status, error) { CMS.ServerError(self.model, "Deleting keys:" + status); })
+ .done(function(data, status, error) {
+ // clear deleteKeys on success
+ self.model.deleteKeys = [];
+ });
+ }
+ },
+ revertView : function(event) {
+ this.model.deleteKeys = [];
+ var self = this;
+ this.model.clear({silent : true});
+ this.model.fetch({
+ success : function() { self.render(); },
+ error : CMS.ServerError
+ });
+ },
+ addEntry : function() {
+ var listEle$ = this.$el.find('.course-advanced-policy-list');
+ listEle$.append(this.template({ key : "", value : ""}));
+ // disable the value entry until there's an acceptable key
+ listEle$.find('.course-advanced-policy-value').addClass('disabled');
+ this.fieldToSelectorMap[this.new_key] = this.new_key;
+ },
+ updateKey : function(event) {
+ // old key: either the key as in the model or new_key.
+ // That is, it doesn't change as the val changes until val is accepted.
+ var oldKey = $(event.currentTarget).closest('.key').attr('id');
+ var newKey = $(event.currentTarget).val();
+ console.log('update ', oldKey, newKey); // TODO: REMOVE ME
+ if (oldKey !== newKey) {
+ // may erase other errors but difficult to just remove these
+ this.clearValidationErrors();
+
+ if (!this.validateKey(oldKey, newKey)) return;
+
+ if (this.model.has(newKey)) {
+ console.log('dupe key');
+ var error = {};
+ error[oldKey] = newKey + " has another entry";
+ error[newKey] = "Other entry for " + newKey;
+ this.model.trigger("error", this.model, error);
+ return false;
+ }
+
+ // explicitly call validate to determine whether to proceed (relying on triggered error means putting continuation in the success
+ // method which is uglier I think?)
+ var newEntryModel = {};
+ // set the new key's value to the old one's
+ newEntryModel[newKey] = (oldKey === this.new_key ? '' : this.model.get(oldKey));
+
+ var validation = this.model.validate(newEntryModel);
+ if (validation) {
+ console.log('reserved key');
+ this.model.trigger("error", this.model, validation);
+ // abandon update
+ return;
+ }
+
+ // Now safe to actually do the update
+ this.model.set(newEntryModel);
+
+ delete this.fieldToSelectorMap[oldKey];
+
+ if (oldKey !== this.new_key) {
+ // mark the old key for deletion and delete from field maps
+ this.model.deleteKeys.push(oldKey);
+ this.model.unset(oldKey) ;
+ }
+ else {
+ // enable the value entry
+ this.$el.find('.course-advanced-policy-value').removeClass('disabled');
+ }
+
+ // update gui (sets all the ids etc)
+ $(event.currentTarget).closest('li').replaceWith(this.template({key : newKey, value : this.model.get(newKey) }));
+
+ this.fieldToSelectorMap[newKey] = newKey;
+ }
+ },
+ validateKey : function(oldKey, newKey) {
+ // model validation can't handle malformed keys nor notice if 2 fields have same key; so, need to add that chk here
+ // TODO ensure there's no spaces or illegal chars
+ if (_.isEmpty(newKey)) {
+ console.log('no key');
+ var error = {};
+ error[oldKey] = "Key cannot be an empty string";
+ this.model.trigger("error", this.model, error);
+ return false;
+ }
+ else return true;
+ },
+ updateValue : function(event) {
+ // much simpler than key munging. just update the value
+ var key = $(event.currentTarget).closest('.row').children('.key').attr('id');
+ var value = $(event.currentTarget).val();
+ console.log('updating ', key, value);
+
+ this.model.set(key, value, {validate:true});
+ }
+});
\ No newline at end of file
diff --git a/cms/templates/settings.html b/cms/templates/settings.html
index b2ac3aa772..3d688d86ee 100644
--- a/cms/templates/settings.html
+++ b/cms/templates/settings.html
@@ -23,6 +23,7 @@ from contentstore import utils
+