Commit 37ab2fc1 by David Baumgold Committed by Sarina Canelake

Wrap block with license info in LMS only

parent ca2fee12
define(["backbone", "underscore"], function(Backbone, _) {
var LicenseModel = Backbone.Model.extend({
defaults: {
"type": null,
"options": {},
"custom": false // either `false`, or a string
},
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");
}
},
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;
}
toString: function() {
var custom = this.get("custom");
if (custom) {
return custom;
}
var type = this.get("type"),
options = this.get("options");
var type = this.get("type"),
options = this.get("options");
if (_.isEmpty(options)) {
return type || "";
}
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(" ");
},
// 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);
}
setFromString: function(string, options) {
if (!string) {
// reset to defaults
return this.set(this.defaults, options);
}
var colonIndex = string.indexOf(":"),
spaceIndex = string.indexOf(" ");
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);
}
}
// 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);
// 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;
}
});
_.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 this.set({
"type": type, "options": optionsObj, "custom": false,
}, options);
}
});
return LicenseModel;
return LicenseModel;
});
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 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 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 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();
});
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 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 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 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 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("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");
});
})
})
......@@ -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({
......
<div class="license-wrapper">
<label class="label setting-label" for="list-license-types">
<%= gettext("License Type") %>
</label>
<ul class="license-types" id="list-license-types">
<% var link_start_tpl = '<a href="{url}" target="_blank">'; %>
<% _.each(licenseInfo, function(license, licenseType) { %>
<li data-license="<%- licenseType %>">
<button name="license-<%- licenseType %>" class="action license-button
<% if(model.type === licenseType) { print("is-selected"); } %>"
name="license-<%- licenseType %>"
<% if (license.tooltip) { %>data-tooltip="<%- license.tooltip %>"<% } %>>
<%- license.name %>
</button>
<p class="tip">
<% if(license.url) { %>
<a href="<%- license.url %>" target="_blank">
<%= gettext("Learn more about {license_name}")
.replace("{license_name}", license.name)
%>
</a>
<% } else { %>
&nbsp;
<% } %>
</p>
</li>
<% }) %>
</ul>
<label class="label setting-label" for="list-license-types">
<%= gettext("License Type") %>
</label>
<ul class="license-types" id="list-license-types">
<% var link_start_tpl = '<a href="{url}" target="_blank">'; %>
<% _.each(licenseInfo, function(license, licenseType) { %>
<li data-license="<%- licenseType %>">
<button name="license-<%- licenseType %>"
class="action license-button <% if(model.type === licenseType) { print("is-selected"); } %>"
<% if (license.tooltip) { %>data-tooltip="<%- license.tooltip %>"<% } %>>
<%- license.name %>
</button>
<p class="tip">
<% if(license.url) { %>
<a href="<%- license.url %>" target="_blank">
<%= gettext("Learn more about {license_name}")
.replace("{license_name}", license.name)
%>
</a>
<% } else { %>
&nbsp;
<% } %>
</p>
</li>
<% }) %>
</ul>
<% var license = licenseInfo[model.type]; %>
<% if(license && !_.isEmpty(license.options)) { %>
<div class="wrapper-license-options">
<label class="label setting-label" for="list-license-options">
<%- gettext("Options for {license_name}").replace("{license_name}", license.name) %>
</label>
<p class='tip tip-inline'>
<%- gettext("The following options are available for the {license_name} license.")
.replace("{license_name}", license.name) %>
</p>
<ul class="license-options" id="list-license-options">
<% _.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];}));
%>
<li data-option="<%- optionKey %>"
class="action-item license-button
<% if (optionSelected) { print("is-selected"); } %>
<% if (optionDisabled) { print("is-disabled"); } else { print("is-clickable"); } %>"
>
<i class="fa fa-fw
<% if(optionSelected) { print("fa-check-square-o"); } else { print("fa-square-o"); } %>
<% if(optionDisabled) { print("is-disabled"); } %>
"></i>
<h4 class="option-name"><%- optionInfo.name %></h4>
<div class="explanation"><%- optionInfo.help %></div>
</li>
<% } // could implement other types here %>
<% }) %>
</ul>
</div>
<div class="wrapper-license-options">
<label class="label setting-label" for="list-license-options">
<%- gettext("Options for {license_name}").replace("{license_name}", license.name) %>
</label>
<p class='tip tip-inline'>
<%- gettext("The following options are available for the {license_name} license.")
.replace("{license_name}", license.name) %>
</p>
<ul class="license-options" id="list-license-options">
<% _.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];}));
%>
<li data-option="<%- optionKey %>"
class="action-item license-button
<% if (optionSelected) { print("is-selected"); } %>
<% if (optionDisabled) { print("is-disabled"); } else { print("is-clickable"); } %>"
>
<i class="fa fa-fw
<% if(optionSelected) { print("fa-check-square-o"); } else { print("fa-square-o"); } %>
<% if(optionDisabled) { print("is-disabled"); } %>
"></i>
<h4 class="option-name"><%- optionInfo.name %></h4>
<div class="explanation"><%- optionInfo.help %></div>
</li>
<% } // could implement other types here %>
<% }) %>
</ul>
</div>
<% } %>
<% if (showPreview) { %>
<div class="license-preview-wrapper">
<label class="label setting-label" for="license-preview">
<%= gettext("License Display") %>
</label>
<p class="tip">
<%= gettext("The following message will be displayed at the bottom of the courseware pages within your course.") %>
</p>
<div id="license-preview">
<% // keep this synchronized with the contents of common/templates/license.html %>
<% if (model.type === "all-rights-reserved") { %>
© <span class="license-text"><%= gettext("All Rights Reserved") %></span>
<% } 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";
}
%>
<a rel="license" href="//creativecommons.org/licenses/<%- enabled.join("-") %>/<%- version %>/">
<% if (previewButton) { %>
<img src="https://licensebuttons.net/l/<%- enabled.join("-") %>/<%- version %>/<%- typeof buttonSize == "string" ? buttonSize : "88x31" %>.png"
alt="<%- typeof licenseString == "string" ? licenseString : "" %>"
/>
<% } else { %>
<i class="icon-cc"></i>
<% _.each(enabled, function(option) { %>
<i class="icon-cc-<%- option %>"></i>
<% }); %>
<span class="license-text"><%= gettext("Some Rights Reserved") %></span>
<% } %>
<% } else { %>
<%= typeof licenseString == "string" ? licenseString : "" %>
<% } %>
</a>
</div>
<div class="license-preview-wrapper">
<label class="label setting-label" for="license-preview">
<%= gettext("License Display") %>
</label>
<p class="tip">
<%= gettext("The following message will be displayed at the bottom of the courseware pages within your course.") %>
</p>
<div id="license-preview">
<% // keep this synchronized with the contents of common/templates/license.html %>
<% if (model.type === "all-rights-reserved") { %>
© <span class="license-text"><%= gettext("All Rights Reserved") %></span>
<% } 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";
}
%>
<a rel="license" href="//creativecommons.org/licenses/<%- enabled.join("-") %>/<%- version %>/">
<% if (previewButton) { %>
<img src="https://licensebuttons.net/l/<%- enabled.join("-") %>/<%- version %>/<%- typeof buttonSize == "string" ? buttonSize : "88x31" %>.png"
alt="<%- typeof licenseString == "string" ? licenseString : "" %>"
/>
<% } else { %>
<i class="icon-cc"></i>
<% _.each(enabled, function(option) { %>
<i class="icon-cc-<%- option %>"></i>
<% }); %>
<span class="license-text"><%= gettext("Some Rights Reserved") %></span>
<% } %>
<% } else { %>
<%= typeof licenseString == "string" ? licenseString : "" %>
<% } %>
</a>
</div>
<% } %>
</div>
......@@ -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):
"""
......
......@@ -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 = {
......
......@@ -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.
......
......@@ -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):
......
......@@ -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
......@@ -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:
......
<div class="xblock-license">
<%include file="license.html" args="license=license" />
</div>
......@@ -5,9 +5,4 @@
</script>
% endif
${content}
% if license:
<div class="xblock-license">
<%include file="license.html" args="license=license" />
</div>
% endif
</div>
......@@ -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:
......
......@@ -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:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment