Added a simple XBlockMixin for courseware licenses
This allows course authors to choose between two difference licenses for their course content: All Rights Reserved, or Creative Commons. In the backend, XBlocks that wish to allow custom licenses need only inherit from LicenseMixin, which adds a `license` field as a string. License information is displayed in the Studio editor view, and just below the rendered XBlock in the LMS. In addition, if the course block itself has a custom license set, this license will display just below the main body of the page on courseware pages. This entire feature is gated behind the LICENSING feature flag.
This commit is contained in:
committed by
Sarina Canelake
parent
8a4503b3f4
commit
2159d34128
100
cms/static/js/models/license.js
Normal file
100
cms/static/js/models/license.js
Normal file
@@ -0,0 +1,100 @@
|
||||
define(["backbone", "underscore"], function(Backbone, _) {
|
||||
var LicenseModel = Backbone.Model.extend({
|
||||
defaults: {
|
||||
"type": null,
|
||||
"options": {},
|
||||
"custom": false // either `false`, or a string
|
||||
},
|
||||
|
||||
initialize: function(attributes) {
|
||||
if(attributes && attributes.asString) {
|
||||
this.setFromString(attributes.asString);
|
||||
this.unset("asString");
|
||||
}
|
||||
},
|
||||
|
||||
toString: function() {
|
||||
var custom = this.get("custom");
|
||||
if (custom) {
|
||||
return custom;
|
||||
}
|
||||
|
||||
var type = this.get("type"),
|
||||
options = this.get("options");
|
||||
|
||||
if (_.isEmpty(options)) {
|
||||
return type || "";
|
||||
}
|
||||
|
||||
// options are where it gets tricky
|
||||
var optionStrings = _.map(options, function (value, key) {
|
||||
if(_.isBoolean(value)) {
|
||||
return value ? key : null
|
||||
} else {
|
||||
return key + "=" + value
|
||||
}
|
||||
});
|
||||
// filter out nulls
|
||||
optionStrings = _.filter(optionStrings, _.identity);
|
||||
// build license string and return
|
||||
return type + ": " + optionStrings.join(" ");
|
||||
},
|
||||
|
||||
setFromString: function(string, options) {
|
||||
if (!string) {
|
||||
// reset to defaults
|
||||
return this.set(this.defaults, options);
|
||||
}
|
||||
|
||||
var colonIndex = string.indexOf(":"),
|
||||
spaceIndex = string.indexOf(" ");
|
||||
|
||||
// a string without a colon could be a custom license, or a license
|
||||
// type without options
|
||||
if (colonIndex == -1) {
|
||||
if (spaceIndex == -1) {
|
||||
// if there's no space, it's a license type without options
|
||||
return this.set({
|
||||
"type": string,
|
||||
"options": {},
|
||||
"custom": false
|
||||
}, options);
|
||||
} else {
|
||||
// if there is a space, it's a custom license
|
||||
return this.set({
|
||||
"type": null,
|
||||
"options": {},
|
||||
"custom": string
|
||||
}, options);
|
||||
}
|
||||
}
|
||||
|
||||
// there is a colon, which indicates a license type with options.
|
||||
var type = string.substring(0, colonIndex),
|
||||
optionsObj = {},
|
||||
optionsString = string.substring(colonIndex + 1);
|
||||
|
||||
_.each(optionsString.split(" "), function(optionString) {
|
||||
if (_.isEmpty(optionString)) {
|
||||
return;
|
||||
}
|
||||
var eqIndex = optionString.indexOf("=");
|
||||
if(eqIndex == -1) {
|
||||
// this is a boolean flag
|
||||
optionsObj[optionString] = true;
|
||||
} else {
|
||||
// this is a key-value pair
|
||||
var optionKey = optionString.substring(0, eqIndex);
|
||||
var optionVal = optionString.substring(eqIndex + 1);
|
||||
optionsObj[optionKey] = optionVal;
|
||||
}
|
||||
});
|
||||
|
||||
return this.set({
|
||||
"type": type, "options": optionsObj, "custom": false,
|
||||
}, options);
|
||||
}
|
||||
});
|
||||
|
||||
return LicenseModel;
|
||||
});
|
||||
@@ -15,6 +15,7 @@ var CourseDetails = Backbone.Model.extend({
|
||||
overview: "",
|
||||
intro_video: null,
|
||||
effort: null, // an int or null,
|
||||
license: null,
|
||||
course_image_name: '', // the filename
|
||||
course_image_asset_path: '', // the full URL (/c4x/org/course/num/asset/filename)
|
||||
pre_requisite_courses: [],
|
||||
|
||||
72
cms/static/js/spec/models/license_spec.js
Normal file
72
cms/static/js/spec/models/license_spec.js
Normal file
@@ -0,0 +1,72 @@
|
||||
define(["js/models/license"], function(LicenseModel) {
|
||||
describe("License model constructor", function() {
|
||||
it("accepts no arguments", function() {
|
||||
var model = new LicenseModel()
|
||||
expect(model.get("type")).toBeNull();
|
||||
expect(model.get("options")).toEqual({});
|
||||
expect(model.get("custom")).toBeFalsy();
|
||||
});
|
||||
|
||||
it("accepts normal arguments", function() {
|
||||
var model = new LicenseModel({
|
||||
"type": "creative-commons",
|
||||
"options": {"fake-boolean": true, "version": "your momma"}
|
||||
});
|
||||
expect(model.get("type")).toEqual("creative-commons");
|
||||
expect(model.get("options")).toEqual({"fake-boolean": true, "version": "your momma"});
|
||||
})
|
||||
|
||||
it("accepts a license string argument", function() {
|
||||
var model = new LicenseModel({"asString": "all-rights-reserved"});
|
||||
expect(model.get("type")).toEqual("all-rights-reserved");
|
||||
expect(model.get("options")).toEqual({});
|
||||
expect(model.get("custom")).toBeFalsy();
|
||||
});
|
||||
|
||||
it("accepts a custom license argument", function() {
|
||||
var model = new LicenseModel({"asString": "Mozilla Public License 2.0"})
|
||||
expect(model.get("type")).toBeNull();
|
||||
expect(model.get("options")).toEqual({});
|
||||
expect(model.get("custom")).toEqual("Mozilla Public License 2.0");
|
||||
});
|
||||
});
|
||||
|
||||
describe("License model", function() {
|
||||
beforeEach(function() {
|
||||
this.model = new LicenseModel();
|
||||
});
|
||||
|
||||
it("can parse license strings", function() {
|
||||
this.model.setFromString("creative-commons: BY")
|
||||
expect(this.model.get("type")).toEqual("creative-commons")
|
||||
expect(this.model.get("options")).toEqual({"BY": true})
|
||||
expect(this.model.get("custom")).toBeFalsy();
|
||||
});
|
||||
|
||||
it("can stringify a null license", function() {
|
||||
expect(this.model.toString()).toEqual("");
|
||||
});
|
||||
|
||||
it("can stringify a simple license", function() {
|
||||
this.model.set("type", "foobie thinger");
|
||||
expect(this.model.toString()).toEqual("foobie thinger");
|
||||
});
|
||||
|
||||
it("can stringify a license with options", function() {
|
||||
this.model.set({
|
||||
"type": "abc",
|
||||
"options": {"ping": "pong", "bing": true, "buzz": true, "beep": false}}
|
||||
);
|
||||
expect(this.model.toString()).toEqual("abc: ping=pong bing buzz");
|
||||
});
|
||||
|
||||
it("can stringify a custom license", function() {
|
||||
this.model.set({
|
||||
"type": "doesn't matter",
|
||||
"options": {"ignore": "me"},
|
||||
"custom": "this is my super cool license"
|
||||
});
|
||||
expect(this.model.toString()).toEqual("this is my super cool license");
|
||||
});
|
||||
})
|
||||
})
|
||||
157
cms/static/js/spec/views/license_spec.js
Normal file
157
cms/static/js/spec/views/license_spec.js
Normal file
@@ -0,0 +1,157 @@
|
||||
define(["js/views/license", "js/models/license", "js/common_helpers/template_helpers"],
|
||||
function(LicenseView, LicenseModel, TemplateHelpers) {
|
||||
describe("License view", function() {
|
||||
|
||||
beforeEach(function() {
|
||||
TemplateHelpers.installTemplate("license-selector", true);
|
||||
this.model = new LicenseModel();
|
||||
this.view = new LicenseView({model: this.model});
|
||||
});
|
||||
|
||||
it("renders with no license", function() {
|
||||
this.view.render();
|
||||
expect(this.view.$("li[data-license=all-rights-reserved] button"))
|
||||
.toHaveText("All Rights Reserved");
|
||||
expect(this.view.$("li[data-license=all-rights-reserved] button"))
|
||||
.not.toHaveClass("is-selected");
|
||||
expect(this.view.$("li[data-license=creative-commons] button"))
|
||||
.toHaveText("Creative Commons");
|
||||
expect(this.view.$("li[data-license=creative-commons] button"))
|
||||
.not.toHaveClass("is-selected");
|
||||
});
|
||||
|
||||
it("renders with the right license selected", function() {
|
||||
this.model.set("type", "all-rights-reserved");
|
||||
expect(this.view.$("li[data-license=all-rights-reserved] button"))
|
||||
.toHaveClass("is-selected");
|
||||
expect(this.view.$("li[data-license=creative-commons] button"))
|
||||
.not.toHaveClass("is-selected");
|
||||
});
|
||||
|
||||
it("switches license type on click", function() {
|
||||
var arrBtn = this.view.$("li[data-license=all-rights-reserved] button");
|
||||
expect(this.model.get("type")).toBeNull();
|
||||
arrBtn.click();
|
||||
expect(this.model.get("type")).toEqual("all-rights-reserved");
|
||||
// view has re-rendered, so get a new reference to the button
|
||||
arrBtn = this.view.$("li[data-license=all-rights-reserved] button");
|
||||
expect(arrBtn).toHaveClass("is-selected");
|
||||
// now switch to creative commons
|
||||
var ccBtn = this.view.$("li[data-license=creative-commons] button");
|
||||
ccBtn.click();
|
||||
expect(this.model.get("type")).toEqual("creative-commons");
|
||||
// update references again
|
||||
arrBtn = this.view.$("li[data-license=all-rights-reserved] button");
|
||||
ccBtn = this.view.$("li[data-license=creative-commons] button");
|
||||
expect(arrBtn).not.toHaveClass("is-selected");
|
||||
expect(ccBtn).toHaveClass("is-selected");
|
||||
});
|
||||
|
||||
it("sets default license options when switching license types", function() {
|
||||
expect(this.model.get("options")).toEqual({});
|
||||
var ccBtn = this.view.$("li[data-license=creative-commons] button");
|
||||
ccBtn.click()
|
||||
expect(this.model.get("options")).toEqual(
|
||||
{"ver": "4.0", "BY": true, "NC": true, "ND": true, "SA": false}
|
||||
);
|
||||
var arrBtn = this.view.$("li[data-license=all-rights-reserved] button");
|
||||
arrBtn.click()
|
||||
expect(this.model.get("options")).toEqual({});
|
||||
});
|
||||
|
||||
it("renders license options", function() {
|
||||
this.model.set({"type": "creative-commons"})
|
||||
expect(this.view.$("ul.license-options li[data-option=BY]"))
|
||||
.toContainText("Attribution");
|
||||
expect(this.view.$("ul.license-options li[data-option=NC]"))
|
||||
.toContainText("Noncommercial");
|
||||
expect(this.view.$("ul.license-options li[data-option=ND]"))
|
||||
.toContainText("No Derivatives");
|
||||
expect(this.view.$("ul.license-options li[data-option=SA]"))
|
||||
.toContainText("Share Alike");
|
||||
expect(this.view.$("ul.license-options li").length).toEqual(4);
|
||||
});
|
||||
|
||||
it("toggles boolean options on click", function() {
|
||||
this.view.$("li[data-license=creative-commons] button").click();
|
||||
expect(this.model.get("options")).toEqual(
|
||||
{"ver": "4.0", "BY": true, "NC": true, "ND": true, "SA": false}
|
||||
);
|
||||
// toggle NC option
|
||||
this.view.$("li[data-option=NC]").click();
|
||||
expect(this.model.get("options")).toEqual(
|
||||
{"ver": "4.0", "BY": true, "NC": false, "ND": true, "SA": false}
|
||||
);
|
||||
});
|
||||
|
||||
it("doesn't toggle disabled options", function() {
|
||||
this.view.$("li[data-license=creative-commons] button").click();
|
||||
expect(this.model.get("options")).toEqual(
|
||||
{"ver": "4.0", "BY": true, "NC": true, "ND": true, "SA": false}
|
||||
);
|
||||
var BY = this.view.$("li[data-option=BY]");
|
||||
expect(BY).toHaveClass("is-disabled");
|
||||
// try to toggle BY option
|
||||
BY.click()
|
||||
// no change
|
||||
expect(this.model.get("options")).toEqual(
|
||||
{"ver": "4.0", "BY": true, "NC": true, "ND": true, "SA": false}
|
||||
);
|
||||
});
|
||||
|
||||
it("doesn't allow simultaneous conflicting options", function() {
|
||||
this.view.$("li[data-license=creative-commons] button").click();
|
||||
expect(this.model.get("options")).toEqual(
|
||||
{"ver": "4.0", "BY": true, "NC": true, "ND": true, "SA": false}
|
||||
);
|
||||
// SA and ND conflict
|
||||
var SA = this.view.$("li[data-option=SA]");
|
||||
expect(SA).toHaveClass("is-disabled");
|
||||
// try to turn on SA option, fail
|
||||
SA.click()
|
||||
// no change
|
||||
expect(this.model.get("options")).toEqual(
|
||||
{"ver": "4.0", "BY": true, "NC": true, "ND": true, "SA": false}
|
||||
);
|
||||
// turn off ND
|
||||
var ND = this.view.$("li[data-option=ND]");
|
||||
expect(ND).not.toHaveClass("is-disabled");
|
||||
ND.click()
|
||||
expect(this.model.get("options")).toEqual(
|
||||
{"ver": "4.0", "BY": true, "NC": true, "ND": false, "SA": false}
|
||||
);
|
||||
// turn on SA
|
||||
SA = this.view.$("li[data-option=SA]");
|
||||
expect(SA).not.toHaveClass("is-disabled");
|
||||
SA.click()
|
||||
expect(this.model.get("options")).toEqual(
|
||||
{"ver": "4.0", "BY": true, "NC": true, "ND": false, "SA": true}
|
||||
);
|
||||
// try to turn on ND option, fail
|
||||
ND = this.view.$("li[data-option=ND]");
|
||||
expect(ND).toHaveClass("is-disabled");
|
||||
ND.click();
|
||||
expect(this.model.get("options")).toEqual(
|
||||
{"ver": "4.0", "BY": true, "NC": true, "ND": false, "SA": true}
|
||||
);
|
||||
});
|
||||
|
||||
it("has no preview by default", function () {
|
||||
this.view.render();
|
||||
expect(this.view.$("#license-preview").length).toEqual(0)
|
||||
this.view.$("li[data-license=creative-commons] button").click();
|
||||
expect(this.view.$("#license-preview").length).toEqual(0)
|
||||
});
|
||||
|
||||
it("displays a preview if showPreview is true", function() {
|
||||
this.view = new LicenseView({model: this.model, showPreview: true});
|
||||
this.view.render()
|
||||
expect(this.view.$("#license-preview").length).toEqual(1)
|
||||
expect(this.view.$("#license-preview")).toHaveText("");
|
||||
this.view.$("li[data-license=creative-commons] button").click();
|
||||
expect(this.view.$("#license-preview").length).toEqual(1)
|
||||
expect(this.view.$("#license-preview")).toContainText("Some Rights Reserved");
|
||||
});
|
||||
|
||||
})
|
||||
})
|
||||
@@ -29,7 +29,8 @@ define([
|
||||
course_image_asset_path : '',
|
||||
pre_requisite_courses : [],
|
||||
entrance_exam_enabled : '',
|
||||
entrance_exam_minimum_score_pct: '50'
|
||||
entrance_exam_minimum_score_pct: '50',
|
||||
license: null
|
||||
},
|
||||
mockSettingsPage = readFixtures('mock/mock-settings-page.underscore');
|
||||
|
||||
|
||||
139
cms/static/js/views/license.js
Normal file
139
cms/static/js/views/license.js
Normal file
@@ -0,0 +1,139 @@
|
||||
define(["js/views/baseview", "underscore"], function(BaseView, _) {
|
||||
var defaultLicenseInfo = {
|
||||
"all-rights-reserved": {
|
||||
"name": gettext("All Rights Reserved"),
|
||||
"tooltip": gettext("You reserve all rights for your work")
|
||||
},
|
||||
"creative-commons": {
|
||||
"name": gettext("Creative Commons"),
|
||||
"tooltip": gettext("You waive some rights for your work, such that others can use it too"),
|
||||
"url": "//creativecommons.org/about",
|
||||
"options": {
|
||||
"ver": {
|
||||
"name": gettext("Version"),
|
||||
"type": "string",
|
||||
"default": "4.0",
|
||||
},
|
||||
"BY": {
|
||||
"name": gettext("Attribution"),
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"help": gettext("Allow others to copy, distribute, display and perform " +
|
||||
"your copyrighted work but only if they give credit the way you request."),
|
||||
"disabled": true,
|
||||
},
|
||||
"NC": {
|
||||
"name": gettext("Noncommercial"),
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"help": gettext("Allow others to copy, distribute, display and perform " +
|
||||
"your work - and derivative works based upon it - but for noncommercial purposes only."),
|
||||
},
|
||||
"ND": {
|
||||
"name": gettext("No Derivatives"),
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"help": gettext("Allow others to copy, distribute, display and perform " +
|
||||
"only verbatim copies of your work, not derivative works based upon it."),
|
||||
"conflictsWith": ["SA"]
|
||||
},
|
||||
"SA": {
|
||||
"name": gettext("Share Alike"),
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"help": gettext("Allow others to distribute derivative works only under " +
|
||||
"a license identical to the license that governs your work."),
|
||||
"conflictsWith": ["ND"]
|
||||
}
|
||||
},
|
||||
"option_order": ["BY", "NC", "ND", "SA"]
|
||||
}
|
||||
}
|
||||
|
||||
var LicenseView = BaseView.extend({
|
||||
events: {
|
||||
"click ul.license-types li button" : "onLicenseClick",
|
||||
"click ul.license-options li": "onOptionClick"
|
||||
},
|
||||
|
||||
initialize: function(options) {
|
||||
this.licenseInfo = options.licenseInfo || defaultLicenseInfo;
|
||||
this.showPreview = !!options.showPreview; // coerce to boolean
|
||||
this.template = this.loadTemplate("license-selector");
|
||||
|
||||
// Rerender when the model changes
|
||||
this.listenTo(this.model, 'change', this.render);
|
||||
this.render();
|
||||
},
|
||||
|
||||
getDefaultOptionsForLicenseType: function(licenseType) {
|
||||
if (!this.licenseInfo[licenseType]) {
|
||||
// custom license type, no options
|
||||
return {};
|
||||
}
|
||||
if (!this.licenseInfo[licenseType].options) {
|
||||
// defined license type without options
|
||||
return {};
|
||||
}
|
||||
var defaults = {};
|
||||
_.each(this.licenseInfo[licenseType].options, function(value, key) {
|
||||
defaults[key] = value.default;
|
||||
})
|
||||
return defaults;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
this.$el.html(this.template({
|
||||
model: this.model.attributes,
|
||||
licenseString: this.model.toString() || "",
|
||||
licenseInfo: this.licenseInfo,
|
||||
showPreview: this.showPreview,
|
||||
previewButton: false,
|
||||
}));
|
||||
return this;
|
||||
},
|
||||
|
||||
onLicenseClick: function(e) {
|
||||
var $li = $(e.srcElement || e.target).closest('li');
|
||||
var licenseType = $li.data("license");
|
||||
this.model.set({
|
||||
"type": licenseType,
|
||||
"options": this.getDefaultOptionsForLicenseType(licenseType)
|
||||
});
|
||||
},
|
||||
|
||||
onOptionClick: function(e) {
|
||||
var licenseType = this.model.get("type"),
|
||||
licenseOptions = $.extend({}, this.model.get("options")),
|
||||
$li = $(e.srcElement || e.target).closest('li');
|
||||
|
||||
var optionKey = $li.data("option")
|
||||
var licenseInfo = this.licenseInfo[licenseType];
|
||||
var optionInfo = licenseInfo.options[optionKey];
|
||||
if (optionInfo.disabled) {
|
||||
// we're done here
|
||||
return;
|
||||
}
|
||||
var currentOptionValue = licenseOptions[optionKey];
|
||||
if (optionInfo.type === "boolean") {
|
||||
// toggle current value
|
||||
currentOptionValue = !currentOptionValue;
|
||||
licenseOptions[optionKey] = currentOptionValue;
|
||||
}
|
||||
// check for conflicts
|
||||
if (currentOptionValue && optionInfo.conflictsWith &&
|
||||
_.any(optionInfo.conflictsWith, function (key) { return licenseOptions[key];})) {
|
||||
// conflict! don't set new options
|
||||
// need some feedback here
|
||||
return;
|
||||
} else {
|
||||
this.model.set({"options": licenseOptions})
|
||||
// Backbone has trouble identifying when objects change, so we'll
|
||||
// fire the change event manually.
|
||||
this.model.trigger("change change:options")
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
return LicenseView;
|
||||
});
|
||||
@@ -2,10 +2,11 @@ define(
|
||||
[
|
||||
"js/views/baseview", "underscore", "js/models/metadata", "js/views/abstract_editor",
|
||||
"js/models/uploads", "js/views/uploads",
|
||||
"js/models/license", "js/views/license",
|
||||
"js/views/video/transcripts/metadata_videolist",
|
||||
"js/views/video/translations_editor"
|
||||
],
|
||||
function(BaseView, _, MetadataModel, AbstractEditor, FileUpload, UploadDialog, VideoList, VideoTranslations) {
|
||||
function(BaseView, _, MetadataModel, AbstractEditor, FileUpload, UploadDialog, LicenseModel, LicenseView, VideoList, VideoTranslations) {
|
||||
var Metadata = {};
|
||||
|
||||
Metadata.Editor = BaseView.extend({
|
||||
@@ -550,5 +551,41 @@ function(BaseView, _, MetadataModel, AbstractEditor, FileUpload, UploadDialog, V
|
||||
}
|
||||
});
|
||||
|
||||
Metadata.License = AbstractEditor.extend({
|
||||
template: _.template(
|
||||
'<label class="label setting-label" for="list-license-types">' +
|
||||
'<%- model.display_name %>' +
|
||||
'</label>'
|
||||
),
|
||||
|
||||
initialize: function(options) {
|
||||
this.licenseModel = new LicenseModel({"asString": this.model.getValue()});
|
||||
this.licenseView = new LicenseView({model: this.licenseModel});
|
||||
|
||||
// Rerender when the license model changes
|
||||
this.listenTo(this.licenseModel, 'change', this.setLicense);
|
||||
this.render();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
this.licenseView.undelegateEvents();
|
||||
this.$el.html(this.template({
|
||||
model: this.model.attributes
|
||||
}));
|
||||
// make the licenseView display after this template, inline
|
||||
this.licenseView.render().$el.css("display", "inline")
|
||||
this.$el.append(this.licenseView.el)
|
||||
// restore event bindings
|
||||
this.licenseView.delegateEvents();
|
||||
return this;
|
||||
},
|
||||
|
||||
setLicense: function() {
|
||||
this.model.setValue(this.licenseModel.toString());
|
||||
this.render()
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return Metadata;
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
define(["js/views/validation", "codemirror", "underscore", "jquery", "jquery.ui", "js/utils/date_utils", "js/models/uploads",
|
||||
"js/views/uploads", "js/utils/change_on_enter", "jquery.timepicker", "date"],
|
||||
function(ValidatingView, CodeMirror, _, $, ui, DateUtils, FileUploadModel, FileUploadDialog, TriggerChangeEventOnEnter) {
|
||||
"js/views/uploads", "js/utils/change_on_enter", "js/views/license", "js/models/license", "jquery.timepicker", "date"],
|
||||
function(ValidatingView, CodeMirror, _, $, ui, DateUtils, FileUploadModel,
|
||||
FileUploadDialog, TriggerChangeEventOnEnter, LicenseView, LicenseModel) {
|
||||
|
||||
var DetailsView = ValidatingView.extend({
|
||||
// Model class is CMS.Models.Settings.CourseDetails
|
||||
@@ -39,6 +40,14 @@ var DetailsView = ValidatingView.extend({
|
||||
this.listenTo(this.model, 'invalid', this.handleValidationError);
|
||||
this.listenTo(this.model, 'change', this.showNotificationBar);
|
||||
this.selectorToField = _.invert(this.fieldToSelectorMap);
|
||||
// handle license separately, to avoid reimplementing view logic
|
||||
this.licenseModel = new LicenseModel({"asString": this.model.get('license')});
|
||||
this.licenseView = new LicenseView({
|
||||
model: this.licenseModel,
|
||||
el: this.$("#course-license-selector").get(),
|
||||
showPreview: true
|
||||
});
|
||||
this.listenTo(this.licenseModel, 'change', this.handleLicenseChange);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
@@ -79,6 +88,8 @@ var DetailsView = ValidatingView.extend({
|
||||
}
|
||||
this.$('#' + this.fieldToSelectorMap['entrance_exam_minimum_score_pct']).val(this.model.get('entrance_exam_minimum_score_pct'));
|
||||
|
||||
this.licenseView.render()
|
||||
|
||||
return this;
|
||||
},
|
||||
fieldToSelectorMap : {
|
||||
@@ -316,6 +327,11 @@ var DetailsView = ValidatingView.extend({
|
||||
}
|
||||
});
|
||||
modal.show();
|
||||
},
|
||||
|
||||
handleLicenseChange: function() {
|
||||
this.showNotificationBar()
|
||||
this.model.set("license", this.licenseModel.toString())
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user