From 37ab2fc16a86072ad88a962a7cde8a046c6102a6 Mon Sep 17 00:00:00 2001 From: David Baumgold Date: Mon, 27 Apr 2015 15:37:22 -0400 Subject: [PATCH] Wrap block with license info in LMS only --- cms/static/js/models/license.js | 188 ++++++------ cms/static/js/spec/models/license_spec.js | 134 ++++---- cms/static/js/spec/views/license_spec.js | 286 +++++++++--------- cms/static/js/views/license.js | 256 ++++++++-------- cms/static/js/views/metadata.js | 3 +- cms/templates/js/license-selector.underscore | 195 ++++++------ common/lib/xmodule/xmodule/capa_base.py | 9 - .../lib/xmodule/xmodule/discussion_module.py | 9 - common/lib/xmodule/xmodule/editing_module.py | 9 + common/lib/xmodule/xmodule/html_module.py | 36 --- common/lib/xmodule/xmodule/mixin.py | 11 + common/lib/xmodule/xmodule/raw_module.py | 12 - common/templates/license_wrapper.html | 3 + common/templates/xblock_wrapper.html | 5 - lms/djangoapps/courseware/module_render.py | 4 + openedx/core/lib/xblock_utils.py | 6 - 16 files changed, 558 insertions(+), 608 deletions(-) create mode 100644 common/templates/license_wrapper.html diff --git a/cms/static/js/models/license.js b/cms/static/js/models/license.js index 3c237beb79..1d032149c9 100644 --- a/cms/static/js/models/license.js +++ b/cms/static/js/models/license.js @@ -1,100 +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({ + var LicenseModel = Backbone.Model.extend({ + defaults: { "type": null, "options": {}, - "custom": string - }, 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); } - } + }); - // 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; + return LicenseModel; }); diff --git a/cms/static/js/spec/models/license_spec.js b/cms/static/js/spec/models/license_spec.js index 6e84a2f0b6..291e0e22ca 100644 --- a/cms/static/js/spec/models/license_spec.js +++ b/cms/static/js/spec/models/license_spec.js @@ -1,72 +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(); + 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"); + }); }); - 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"}); + 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"); + }); }) - - 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"); - }); - }) }) diff --git a/cms/static/js/spec/views/license_spec.js b/cms/static/js/spec/views/license_spec.js index 6476f45ed1..6123b5190c 100644 --- a/cms/static/js/spec/views/license_spec.js +++ b/cms/static/js/spec/views/license_spec.js @@ -1,157 +1,157 @@ define(["js/views/license", "js/models/license", "js/common_helpers/template_helpers"], - function(LicenseView, LicenseModel, TemplateHelpers) { - describe("License view", function() { + 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}); - }); + 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 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("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("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("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("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("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 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("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("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"); - }); + 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"); + }); - }) + }) }) diff --git a/cms/static/js/views/license.js b/cms/static/js/views/license.js index 8b7a0dbfd9..1f1dcf5484 100644 --- a/cms/static/js/views/license.js +++ b/cms/static/js/views/license.js @@ -1,139 +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", + var defaultLicenseInfo = { + "all-rights-reserved": { + "name": gettext("All Rights Reserved"), + "tooltip": gettext("You reserve all rights for your work") }, - "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"] + "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"] } - }, - "option_order": ["BY", "NC", "ND", "SA"] } - } - var LicenseView = BaseView.extend({ - events: { - "click ul.license-types li button" : "onLicenseClick", - "click ul.license-options li": "onOptionClick" - }, + 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"); + 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(); - }, + // 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 {}; + 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") + } } - 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; + }); + return LicenseView; }); diff --git a/cms/static/js/views/metadata.js b/cms/static/js/views/metadata.js index ec7c6b75f7..62565e58d2 100644 --- a/cms/static/js/views/metadata.js +++ b/cms/static/js/views/metadata.js @@ -6,7 +6,8 @@ define( "js/views/video/transcripts/metadata_videolist", "js/views/video/translations_editor" ], -function(BaseView, _, MetadataModel, AbstractEditor, FileUpload, UploadDialog, LicenseModel, LicenseView, VideoList, VideoTranslations) { +function(BaseView, _, MetadataModel, AbstractEditor, FileUpload, UploadDialog, + LicenseModel, LicenseView, VideoList, VideoTranslations) { var Metadata = {}; Metadata.Editor = BaseView.extend({ diff --git a/cms/templates/js/license-selector.underscore b/cms/templates/js/license-selector.underscore index a279042934..7e5938ab50 100644 --- a/cms/templates/js/license-selector.underscore +++ b/cms/templates/js/license-selector.underscore @@ -1,107 +1,106 @@
- - + + <% var license = licenseInfo[model.type]; %> <% if(license && !_.isEmpty(license.options)) { %> -
- -

- <%- gettext("The following options are available for the {license_name} license.") - .replace("{license_name}", license.name) %> -

-
    - <% _.each(license.option_order, function(optionKey) { %> - <% var optionInfo = license.options[optionKey]; %> - <% if (optionInfo.type == "boolean") { %> - <% var optionSelected = model.options[optionKey]; %> - <% var optionDisabled = optionInfo.disabled || - (optionInfo.conflictsWith && _.any(optionInfo.conflictsWith, function(key) {return model.options[key];})); - %> -
  • - <% if (optionDisabled) { print("is-disabled"); } else { print("is-clickable"); } %>" - > - - <% if(optionDisabled) { print("is-disabled"); } %> - "> -

    <%- optionInfo.name %>

    -
    <%- optionInfo.help %>
    -
  • - <% } // could implement other types here %> - <% }) %> -
-
+
+ +

+ <%- gettext("The following options are available for the {license_name} license.") + .replace("{license_name}", license.name) %> +

+
    + <% _.each(license.option_order, function(optionKey) { %> + <% var optionInfo = license.options[optionKey]; %> + <% if (optionInfo.type == "boolean") { %> + <% var optionSelected = model.options[optionKey]; %> + <% var optionDisabled = optionInfo.disabled || + (optionInfo.conflictsWith && _.any(optionInfo.conflictsWith, function(key) {return model.options[key];})); + %> +
  • + <% if (optionDisabled) { print("is-disabled"); } else { print("is-clickable"); } %>" + > + + <% if(optionDisabled) { print("is-disabled"); } %> + "> +

    <%- optionInfo.name %>

    +
    <%- optionInfo.help %>
    +
  • + <% } // could implement other types here %> + <% }) %> +
+
<% } %> <% if (showPreview) { %> -
- -

- <%= gettext("The following message will be displayed at the bottom of the courseware pages within your course.") %> -

-
- <% // keep this synchronized with the contents of common/templates/license.html %> - <% if (model.type === "all-rights-reserved") { %> - © <%= gettext("All Rights Reserved") %> - <% } else if (model.type === "creative-commons") { - var possible = ["by", "nc", "nd", "sa"]; - var enabled = _.filter(possible, function(option) { - return model.options[option] === true || model.options[option.toUpperCase()] === true; - }); - var version = model.options.ver || "4.0"; - if (_.isEmpty(enabled)) { - enabled = ["zero"]; - version = model.options.ver || "1.0"; - } - %> - /<%- version %>/"> - <% if (previewButton) { %> - /<%- version %>/<%- typeof buttonSize == "string" ? buttonSize : "88x31" %>.png" - alt="<%- typeof licenseString == "string" ? licenseString : "" %>" - /> - <% } else { %> - - <% _.each(enabled, function(option) { %> - - <% }); %> - <%= gettext("Some Rights Reserved") %> - <% } %> - <% } else { %> - <%= typeof licenseString == "string" ? licenseString : "" %> - <% } %> - -
+
+ +

+ <%= gettext("The following message will be displayed at the bottom of the courseware pages within your course.") %> +

+
+ <% // keep this synchronized with the contents of common/templates/license.html %> + <% if (model.type === "all-rights-reserved") { %> + © <%= gettext("All Rights Reserved") %> + <% } else if (model.type === "creative-commons") { + var possible = ["by", "nc", "nd", "sa"]; + var enabled = _.filter(possible, function(option) { + return model.options[option] === true || model.options[option.toUpperCase()] === true; + }); + var version = model.options.ver || "4.0"; + if (_.isEmpty(enabled)) { + enabled = ["zero"]; + version = model.options.ver || "1.0"; + } + %> + /<%- version %>/"> + <% if (previewButton) { %> + /<%- version %>/<%- typeof buttonSize == "string" ? buttonSize : "88x31" %>.png" + alt="<%- typeof licenseString == "string" ? licenseString : "" %>" + /> + <% } else { %> + + <% _.each(enabled, function(option) { %> + + <% }); %> + <%= gettext("Some Rights Reserved") %> + <% } %> + <% } else { %> + <%= typeof licenseString == "string" ? licenseString : "" %> + <% } %> + +
<% } %>
diff --git a/common/lib/xmodule/xmodule/capa_base.py b/common/lib/xmodule/xmodule/capa_base.py index 31cd165fe4..3c0396a7bf 100644 --- a/common/lib/xmodule/xmodule/capa_base.py +++ b/common/lib/xmodule/xmodule/capa_base.py @@ -200,15 +200,6 @@ class CapaFields(object): scope=Scope.settings ) - @property - def non_editable_metadata_fields(self): - """ - `data` should not be editable in the Studio settings editor. - """ - non_editable_fields = super(CapaFields, self).non_editable_metadata_fields - non_editable_fields.append(CapaFields.data) - return non_editable_fields - class CapaMixin(CapaFields): """ diff --git a/common/lib/xmodule/xmodule/discussion_module.py b/common/lib/xmodule/xmodule/discussion_module.py index b835bc53ae..ed787fb296 100644 --- a/common/lib/xmodule/xmodule/discussion_module.py +++ b/common/lib/xmodule/xmodule/discussion_module.py @@ -41,15 +41,6 @@ class DiscussionFields(object): ) sort_key = String(scope=Scope.settings) - @property - def non_editable_metadata_fields(self): - """ - `data` should not be editable in the Studio settings editor. - """ - non_editable_fields = super(DiscussionFields, self).non_editable_metadata_fields - non_editable_fields.append(DiscussionFields.data) - return non_editable_fields - class DiscussionModule(DiscussionFields, XModule): js = { diff --git a/common/lib/xmodule/xmodule/editing_module.py b/common/lib/xmodule/xmodule/editing_module.py index 35e3e4c6d1..466fd35155 100644 --- a/common/lib/xmodule/xmodule/editing_module.py +++ b/common/lib/xmodule/xmodule/editing_module.py @@ -22,6 +22,15 @@ class EditingDescriptor(EditingFields, MakoModuleDescriptor): """ mako_template = "widgets/raw-edit.html" + @property + def non_editable_metadata_fields(self): + """ + `data` should not be editable in the Studio settings editor. + """ + non_editable_fields = super(EditingDescriptor, self).non_editable_metadata_fields + non_editable_fields.append(self.fields['data']) + return non_editable_fields + # cdodge: a little refactoring here, since we're basically doing the same thing # here as with our parent class, let's call into it to get the basic fields # set and then add our additional fields. Trying to keep it DRY. diff --git a/common/lib/xmodule/xmodule/html_module.py b/common/lib/xmodule/xmodule/html_module.py index e7d3958ab0..cc3e6bea55 100644 --- a/common/lib/xmodule/xmodule/html_module.py +++ b/common/lib/xmodule/xmodule/html_module.py @@ -55,15 +55,6 @@ class HtmlFields(object): scope=Scope.settings ) - @property - def non_editable_metadata_fields(self): - """ - `data` should not be editable in the Studio settings editor. - """ - non_editable_fields = super(HtmlFields, self).non_editable_metadata_fields - non_editable_fields.append(HtmlFields.data) - return non_editable_fields - class HtmlModuleMixin(HtmlFields, XModule): """ @@ -312,15 +303,6 @@ class AboutFields(object): scope=Scope.content ) - @property - def non_editable_metadata_fields(self): - """ - `data` should not be editable in the Studio settings editor. - """ - non_editable_fields = super(AboutFields, self).non_editable_metadata_fields - non_editable_fields.append(AboutFields.data) - return non_editable_fields - @XBlock.tag("detached") class AboutModule(AboutFields, HtmlModuleMixin): @@ -358,15 +340,6 @@ class StaticTabFields(object): help=_("HTML for the additional pages") ) - @property - def non_editable_metadata_fields(self): - """ - `data` should not be editable in the Studio settings editor. - """ - non_editable_fields = super(StaticTabFields, self).non_editable_metadata_fields - non_editable_fields.append(StaticTabFields.data) - return non_editable_fields - @XBlock.tag("detached") class StaticTabModule(StaticTabFields, HtmlModuleMixin): @@ -401,15 +374,6 @@ class CourseInfoFields(object): scope=Scope.content ) - @property - def non_editable_metadata_fields(self): - """ - `data` should not be editable in the Studio settings editor. - """ - non_editable_fields = super(CourseInfoFields, self).non_editable_metadata_fields - non_editable_fields.append(CourseInfoFields.data) - return non_editable_fields - @XBlock.tag("detached") class CourseInfoModule(CourseInfoFields, HtmlModuleMixin): diff --git a/common/lib/xmodule/xmodule/mixin.py b/common/lib/xmodule/xmodule/mixin.py index 492b375244..7468ccb512 100644 --- a/common/lib/xmodule/xmodule/mixin.py +++ b/common/lib/xmodule/xmodule/mixin.py @@ -51,3 +51,14 @@ class LicenseMixin(XBlockMixin): """ if getattr(self, "license", None): node.set('license', self.license) + + +def wrap_with_license(block, view, frag, context): # pylint: disable=unused-argument + """ + In the LMS, display the custom license underneath the XBlock. + """ + license = getattr(block, "license", None) # pylint: disable=redefined-builtin + if license: + context = {"license": license} + frag.content += block.runtime.render_template('license_wrapper.html', context) + return frag diff --git a/common/lib/xmodule/xmodule/raw_module.py b/common/lib/xmodule/xmodule/raw_module.py index 7aa522cf04..0e9087c540 100644 --- a/common/lib/xmodule/xmodule/raw_module.py +++ b/common/lib/xmodule/xmodule/raw_module.py @@ -15,12 +15,6 @@ class RawDescriptor(XmlDescriptor, XMLEditingDescriptor): """ data = String(help="XML data for the module", default="", scope=Scope.content) - @property - def non_editable_metadata_fields(self): - non_editable_fields = super(RawDescriptor, self).non_editable_metadata_fields - non_editable_fields.append(RawDescriptor.data) - return non_editable_fields - @classmethod def definition_from_xml(cls, xml_object, system): return {'data': etree.tostring(xml_object, pretty_print=True, encoding='unicode')}, [] @@ -50,12 +44,6 @@ class EmptyDataRawDescriptor(XmlDescriptor, XMLEditingDescriptor): """ data = String(default='', scope=Scope.content) - @property - def non_editable_metadata_fields(self): - non_editable_fields = super(EmptyDataRawDescriptor, self).non_editable_metadata_fields - non_editable_fields.append(EmptyDataRawDescriptor.data) - return non_editable_fields - @classmethod def definition_from_xml(cls, xml_object, system): if len(xml_object) == 0 and len(xml_object.items()) == 0: diff --git a/common/templates/license_wrapper.html b/common/templates/license_wrapper.html new file mode 100644 index 0000000000..4140a39c2f --- /dev/null +++ b/common/templates/license_wrapper.html @@ -0,0 +1,3 @@ +
+ <%include file="license.html" args="license=license" /> +
diff --git a/common/templates/xblock_wrapper.html b/common/templates/xblock_wrapper.html index 1026337a83..998d00a90a 100644 --- a/common/templates/xblock_wrapper.html +++ b/common/templates/xblock_wrapper.html @@ -5,9 +5,4 @@ % endif ${content} -% if license: -
- <%include file="license.html" args="license=license" /> -
-% endif
diff --git a/lms/djangoapps/courseware/module_render.py b/lms/djangoapps/courseware/module_render.py index 720304ac51..ada35ac1f7 100644 --- a/lms/djangoapps/courseware/module_render.py +++ b/lms/djangoapps/courseware/module_render.py @@ -67,6 +67,7 @@ from openedx.core.lib.xblock_utils import ( ) from xmodule.lti_module import LTIModule from xmodule.x_module import XModuleDescriptor +from xmodule.mixin import wrap_with_license from xblock_django.user_service import DjangoXBlockUserService from util.json_request import JsonResponse from util.sandboxing import can_execute_unsafe_code, get_python_lib_zip @@ -533,6 +534,9 @@ def get_module_system_for_user(user, field_data_cache, # to the Fragment content coming out of the xblocks that are about to be rendered. block_wrappers = [] + if settings.FEATURES.get("LICENSING", False): + block_wrappers.append(wrap_with_license) + # Wrap the output display in a single div to allow for the XModule # javascript to be bound correctly if wrap_xmodule_display is True: diff --git a/openedx/core/lib/xblock_utils.py b/openedx/core/lib/xblock_utils.py index 2e007ef2b0..98cddb8650 100644 --- a/openedx/core/lib/xblock_utils.py +++ b/openedx/core/lib/xblock_utils.py @@ -122,18 +122,12 @@ def wrap_xblock( if block.name: data['name'] = block.name - if settings.FEATURES.get("LICENSING", False): - license = getattr(block, "license", None) - else: - license = None - template_context = { 'content': block.display_name if display_name_only else frag.content, 'classes': css_classes, 'display_name': block.display_name_with_default, 'data_attributes': u' '.join(u'data-{}="{}"'.format(markupsafe.escape(key), markupsafe.escape(value)) for key, value in data.iteritems()), - 'license': license, } if hasattr(frag, 'json_init_args') and frag.json_init_args is not None: