Commit ca64c665 by Sarina Canelake

Default course license to All Rights Reserved

Clicking on conflicting option box unchecks all conflicts
LMS: Clicking license text should bring to new window
updated styles to reflect html reuse inside of xblock edit modal area.

Add ARIA attributes to license for a11y

Gracefully handle re-selecting of selected license
parent a3887e95
...@@ -42,7 +42,7 @@ class CourseDetails(object): ...@@ -42,7 +42,7 @@ class CourseDetails(object):
self.overview = "" # html to render as the overview self.overview = "" # html to render as the overview
self.intro_video = None # a video pointer self.intro_video = None # a video pointer
self.effort = None # int hours/week self.effort = None # int hours/week
self.license = None self.license = "all-rights-reserved" # default course license is all rights reserved
self.course_image_name = "" self.course_image_name = ""
self.course_image_asset_path = "" # URL of the course image self.course_image_asset_path = "" # URL of the course image
self.pre_requisite_courses = [] # pre-requisite courses self.pre_requisite_courses = [] # pre-requisite courses
...@@ -80,7 +80,8 @@ class CourseDetails(object): ...@@ -80,7 +80,8 @@ class CourseDetails(object):
course_details.pre_requisite_courses = descriptor.pre_requisite_courses course_details.pre_requisite_courses = descriptor.pre_requisite_courses
course_details.course_image_name = descriptor.course_image course_details.course_image_name = descriptor.course_image
course_details.course_image_asset_path = course_image_url(descriptor) course_details.course_image_asset_path = course_image_url(descriptor)
course_details.license = getattr(descriptor, "license", None) # Default course license is "All Rights Reserved"
course_details.license = getattr(descriptor, "license", "all-rights-reserved")
for attribute in ABOUT_ATTRIBUTES: for attribute in ABOUT_ATTRIBUTES:
value = cls._fetch_about_attribute(course_key, attribute) value = cls._fetch_about_attribute(course_key, attribute)
......
...@@ -106,33 +106,18 @@ define(["js/views/license", "js/models/license", "js/common_helpers/template_hel ...@@ -106,33 +106,18 @@ define(["js/views/license", "js/models/license", "js/common_helpers/template_hel
); );
// SA and ND conflict // SA and ND conflict
var SA = this.view.$("li[data-option=SA]"); var SA = this.view.$("li[data-option=SA]");
expect(SA).toHaveClass("is-disabled"); // try to turn on SA option
// 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() SA.click()
// ND should no longer be selected
expect(this.model.get("options")).toEqual( expect(this.model.get("options")).toEqual(
{"ver": "4.0", "BY": true, "NC": true, "ND": false, "SA": true} {"ver": "4.0", "BY": true, "NC": true, "ND": false, "SA": true}
); );
// try to turn on ND option, fail
// try to turn on ND option
ND = this.view.$("li[data-option=ND]"); ND = this.view.$("li[data-option=ND]");
expect(ND).toHaveClass("is-disabled");
ND.click(); ND.click();
expect(this.model.get("options")).toEqual( expect(this.model.get("options")).toEqual(
{"ver": "4.0", "BY": true, "NC": true, "ND": false, "SA": true} {"ver": "4.0", "BY": true, "NC": true, "ND": true, "SA": false}
); );
}); });
...@@ -147,7 +132,8 @@ define(["js/views/license", "js/models/license", "js/common_helpers/template_hel ...@@ -147,7 +132,8 @@ define(["js/views/license", "js/models/license", "js/common_helpers/template_hel
this.view = new LicenseView({model: this.model, showPreview: true}); this.view = new LicenseView({model: this.model, showPreview: true});
this.view.render() this.view.render()
expect(this.view.$(".license-preview").length).toEqual(1) expect(this.view.$(".license-preview").length).toEqual(1)
expect(this.view.$(".license-preview")).toHaveText(""); // Expect default text to be "All Rights Reserved"
expect(this.view.$(".license-preview")).toContainText("All Rights Reserved");
this.view.$("li[data-license=creative-commons] button").click(); this.view.$("li[data-license=creative-commons] button").click();
expect(this.view.$(".license-preview").length).toEqual(1) expect(this.view.$(".license-preview").length).toEqual(1)
expect(this.view.$(".license-preview")).toContainText("Some Rights Reserved"); expect(this.view.$(".license-preview")).toContainText("Some Rights Reserved");
......
...@@ -92,10 +92,17 @@ define(["js/views/baseview", "underscore"], function(BaseView, _) { ...@@ -92,10 +92,17 @@ define(["js/views/baseview", "underscore"], function(BaseView, _) {
onLicenseClick: function(e) { onLicenseClick: function(e) {
var $li = $(e.srcElement || e.target).closest('li'); var $li = $(e.srcElement || e.target).closest('li');
var licenseType = $li.data("license"); var licenseType = $li.data("license");
this.model.set({
"type": licenseType, // Check that we've selected a different license type than what's currently selected
"options": this.getDefaultOptionsForLicenseType(licenseType) if (licenseType != this.model.attributes.type) {
}); this.model.set({
"type": licenseType,
"options": this.getDefaultOptionsForLicenseType(licenseType)
});
// Fire the change event manually
this.model.trigger("change change:type")
}
e.preventDefault();
}, },
onOptionClick: function(e) { onOptionClick: function(e) {
...@@ -117,17 +124,19 @@ define(["js/views/baseview", "underscore"], function(BaseView, _) { ...@@ -117,17 +124,19 @@ define(["js/views/baseview", "underscore"], function(BaseView, _) {
licenseOptions[optionKey] = currentOptionValue; licenseOptions[optionKey] = currentOptionValue;
} }
// check for conflicts // check for conflicts
if (currentOptionValue && optionInfo.conflictsWith && if (currentOptionValue && optionInfo.conflictsWith) {
_.any(optionInfo.conflictsWith, function (key) { return licenseOptions[key];})) { var conflicts = optionInfo.conflictsWith;
// conflict! don't set new options for (var i=0; i<conflicts.length; i++) {
// need some feedback here // Uncheck all conflicts
return; licenseOptions[conflicts[i]] = false;
} else { console.log(licenseOptions);
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")
} }
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")
e.preventDefault(); e.preventDefault();
} }
......
...@@ -879,3 +879,96 @@ div.wrapper-comp-editor.is-inactive ~ div.launch-latex-compiler { ...@@ -879,3 +879,96 @@ div.wrapper-comp-editor.is-inactive ~ div.launch-latex-compiler {
font-style: italic; font-style: italic;
} }
} }
// CASE: xblock license settings
.wrapper-license {
.license-types {
text-align: center;
vertical-align: middle;
display: inline-block;
.license-type {
display: inline-block;
}
.action.license-button {
@include grey-button;
@extend %t-action2;
display: inline-block;
text-align: center;
width: 220px;
height: 40px;
cursor: pointer;
&.is-selected {
@include blue-button;
}
}
.tip {
@extend %t-copy-sub2;
}
}
.wrapper-license-options {
margin-bottom: ($baseline/2);
.tip {
@extend %t-copy-sub2;
}
.license-options {
padding-bottom: ($baseline/2);
.license-option {
line-height: 1.5;
border-bottom: 1px solid $gray-l4;
padding: ($baseline/2) 0 ($baseline*0.4);
&.is-clickable {
cursor: pointer;
}
&:last-child {
border-bottom: none;
}
input[type=checkbox] {
vertical-align: top;
width: auto;
min-width: auto;
height: auto;
border: 0;
margin: ($baseline*0.15) 15px 0px;
}
.option-name {
@extend %t-action3;
@extend %t-strong;
display: inline-block;
width: 15%;
vertical-align: top;
cursor: pointer;
}
.explanation {
@extend %t-action4;
display: inline-block;
width: 75%;
vertical-align: top;
color: $gray;
}
}
}
}
.license-preview a {
color: $gray;
&:hover {
color: $ui-link-color;
}
}
.list-input.settings-list ul.license-options li {
// to make sure the padding is correctly overridden
padding: ($baseline / 2) 0 ($baseline * 0.4);
}
}
...@@ -985,101 +985,4 @@ ...@@ -985,101 +985,4 @@
} }
} }
} }
}
// +Settings - Licenses
// ====================
.view-settings {
.license-img {
padding: ($baseline/5);
}
.license-types {
text-align: center;
vertical-align: middle;
display: inline-block;
.license-type {
display: inline-block;
}
.action.license-button {
@include grey-button;
@extend %t-action2;
display: inline-block;
text-align: center;
width: 220px;
height: 40px;
cursor: pointer;
&.is-selected {
@include blue-button;
}
}
.tip {
@extend %t-copy-sub2;
}
}
.wrapper-license-options {
margin-bottom: ($baseline/2);
.tip {
@extend %t-copy-sub2;
}
.license-options {
padding-bottom: ($baseline/2);
.license-option {
line-height: 1.5;
border-bottom: 1px solid $gray-l4;
padding: ($baseline/2) 0 ($baseline*0.4);
&.is-clickable {
cursor: pointer;
}
&:last-child {
border-bottom: none;
}
input[type=checkbox] {
vertical-align: top;
width: auto;
min-width: auto;
height: auto;
border: 0;
margin: ($baseline*0.15) 15px 0px;
}
.option-name {
@extend %t-action3;
@extend %t-strong;
display: inline-block;
width: 15%;
vertical-align: top;
cursor: pointer;
}
.explanation {
@extend %t-action4;
display: inline-block;
width: 75%;
vertical-align: top;
color: $gray;
}
}
}
}
.license-preview a {
color: $gray;
&:hover {
color: $ui-link-color;
}
}
.list-input.settings-list ul.license-options li {
// to make sure the padding is correctly overridden
padding: ($baseline / 2) 0 ($baseline * 0.4);
}
} }
\ No newline at end of file
...@@ -42,9 +42,7 @@ ...@@ -42,9 +42,7 @@
<% var optionInfo = license.options[optionKey]; %> <% var optionInfo = license.options[optionKey]; %>
<% if (optionInfo.type == "boolean") { %> <% if (optionInfo.type == "boolean") { %>
<% var optionSelected = model.options[optionKey]; %> <% var optionSelected = model.options[optionKey]; %>
<% var optionDisabled = optionInfo.disabled || <% var optionDisabled = optionInfo.disabled %>
(optionInfo.conflictsWith && _.any(optionInfo.conflictsWith, function(key) {return model.options[key];}));
%>
<li data-option="<%- optionKey %>" <li data-option="<%- optionKey %>"
class="action-item license-option class="action-item license-option
<% if (optionSelected) { print("is-selected"); } %> <% if (optionSelected) { print("is-selected"); } %>
...@@ -76,7 +74,7 @@ ...@@ -76,7 +74,7 @@
<%= gettext("License Display") %> <%= gettext("License Display") %>
</h4> </h4>
<p class="tip"> <p class="tip">
<%= gettext("The following message will be displayed at the bottom of the courseware pages within your course.") %> <%= gettext("The following message will be displayed at the bottom of the courseware pages within your course:") %>
</p> </p>
<div class="license-preview"> <div class="license-preview">
<% // keep this synchronized with the contents of common/templates/license.html %> <% // keep this synchronized with the contents of common/templates/license.html %>
...@@ -99,14 +97,17 @@ ...@@ -99,14 +97,17 @@
alt="<%- typeof licenseString == "string" ? licenseString : "" %>" alt="<%- typeof licenseString == "string" ? licenseString : "" %>"
/> />
<% } else { %> <% } else { %>
<i aria-hidden="true" class="icon-cc"></i> <% //<span> must come before <i> icon or else spacing gets messed up %>
<span class="sr">gettext("Creative Commons licensed content, with terms as follow:")&nbsp;</span><i aria-hidden="true" class="icon-cc"></i>
<% _.each(enabled, function(option) { %> <% _.each(enabled, function(option) { %>
<i aria-hidden="true" class="icon-cc-<%- option %>"></i> <span class="sr"><%- license.options[option.toUpperCase()].name %>&nbsp;</span><i aria-hidden="true" class="icon-cc-<%- option %>"></i>
<% }); %> <% }); %>
<span class="license-text"><%= gettext("Some Rights Reserved") %></span> <span class="license-text"><%= gettext("Some Rights Reserved") %></span>
<% } %> <% } %>
<% } else { %> <% } else { %>
<%= typeof licenseString == "string" ? licenseString : "" %> <%= typeof licenseString == "string" ? licenseString : "" %>
<% // Default to ARR license %>
© <span class="license-text"><%= gettext("All Rights Reserved") %></span>
<% } %> <% } %>
</a> </a>
</div> </div>
......
...@@ -1023,8 +1023,9 @@ class CourseDescriptor(CourseFields, LicenseMixin, SequenceDescriptor): ...@@ -1023,8 +1023,9 @@ class CourseDescriptor(CourseFields, LicenseMixin, SequenceDescriptor):
wiki_xml_object.set('slug', self.wiki_slug) wiki_xml_object.set('slug', self.wiki_slug)
xml_object.append(wiki_xml_object) xml_object.append(wiki_xml_object)
# handle license specifically # handle license specifically. Default the course to have a license
self.add_license_to_xml(xml_object) # of "All Rights Reserved", if a license is not explicitly set.
self.add_license_to_xml(xml_object, default="all-rights-reserved")
return xml_object return xml_object
......
...@@ -41,7 +41,7 @@ class LicenseMixin(XBlockMixin): ...@@ -41,7 +41,7 @@ class LicenseMixin(XBlockMixin):
definition['license'] = license definition['license'] = license
return definition return definition
def add_license_to_xml(self, node): def add_license_to_xml(self, node, default=None):
""" """
When generating XML from an XBlock, this method will add the XBlock's When generating XML from an XBlock, this method will add the XBlock's
license to the XML representation before it is serialized. license to the XML representation before it is serialized.
...@@ -49,7 +49,7 @@ class LicenseMixin(XBlockMixin): ...@@ -49,7 +49,7 @@ class LicenseMixin(XBlockMixin):
to this method, rather than reimplementing it in their XML export to this method, rather than reimplementing it in their XML export
functions. functions.
""" """
if getattr(self, "license", None): if getattr(self, "license", default):
node.set('license', self.license) node.set('license', self.license)
......
...@@ -29,6 +29,10 @@ def parse_license(lic): ...@@ -29,6 +29,10 @@ def parse_license(lic):
% elif license_type == "creative-commons": % elif license_type == "creative-commons":
<% <%
possible = ["by", "nc", "nd", "sa"] possible = ["by", "nc", "nd", "sa"]
names = {
"by": _("Attribution"), "nc": _("Noncommercial"),
"nd": _("No Derivatives"), "sa": _("Share Alike")
}
enabled = [opt for opt in possible enabled = [opt for opt in possible
if license_options.get(opt) or license_options.get(opt.upper())] if license_options.get(opt) or license_options.get(opt.upper())]
version = license_options.get("ver", "4.0") version = license_options.get("ver", "4.0")
...@@ -36,16 +40,17 @@ def parse_license(lic): ...@@ -36,16 +40,17 @@ def parse_license(lic):
enabled = ["zero"] enabled = ["zero"]
version = license_options.get("ver", "1.0") version = license_options.get("ver", "1.0")
%> %>
<a rel="license" href="https://creativecommons.org/licenses/${'-'.join(enabled)}/${version}/"> <a rel="license" href="https://creativecommons.org/licenses/${'-'.join(enabled)}/${version}/" target="_blank">
% if button: % if button:
<img src="https://licensebuttons.net/l/${'-'.join(enabled)}/${version}/${button_size}.png" <img src="https://licensebuttons.net/l/${'-'.join(enabled)}/${version}/${button_size}.png"
alt="${license}" alt="${license}"
/> />
</a> </a>
% else: % else:
<i aria-hidden="true" class="icon-cc"></i> ## <span> must come before <i> icon or else spacing gets messed up
<span class="sr">${_("Creative Commons licensed content, with terms as follow:")}&nbsp;</span><i aria-hidden="true" class="icon-cc"></i>
% for option in enabled: % for option in enabled:
<i aria-hidden="true" class="icon-cc-${option}"></i> <span class="sr">${names[option]}&nbsp;</span><i aria-hidden="true" class="icon-cc-${option}"></i>
% endfor % endfor
<span class="license-text">${_("Some Rights Reserved")}</span> <span class="license-text">${_("Some Rights Reserved")}</span>
% endif % endif
......
...@@ -90,6 +90,9 @@ class CoursewarePage(CoursePage): ...@@ -90,6 +90,9 @@ class CoursewarePage(CoursePage):
@property @property
def course_license(self): def course_license(self):
"""
Returns the course license text, if present. Else returns None.
"""
element = self.q(css="#content .container-footer .course-license") element = self.q(css="#content .container-footer .course-license")
if element.is_present(): if element.is_present():
return element.text[0] return element.text[0]
......
...@@ -488,6 +488,9 @@ class XBlockWrapper(PageObject): ...@@ -488,6 +488,9 @@ class XBlockWrapper(PageObject):
type_in_codemirror(self, index, text, find_prefix='$("{}").find'.format(self.editor_selector)) type_in_codemirror(self, index, text, find_prefix='$("{}").find'.format(self.editor_selector))
def set_license(self, license_type): def set_license(self, license_type):
"""
Uses the UI to set the course's license to the given license_type (str)
"""
css_selector = ( css_selector = (
"ul.license-types li[data-license={license_type}] button" "ul.license-types li[data-license={license_type}] button"
).format(license_type=license_type) ).format(license_type=license_type)
......
...@@ -12,7 +12,6 @@ from selenium.webdriver.common.action_chains import ActionChains ...@@ -12,7 +12,6 @@ from selenium.webdriver.common.action_chains import ActionChains
from .course_page import CoursePage from .course_page import CoursePage
from .container import ContainerPage from .container import ContainerPage
from .settings import SettingsPage
from .utils import set_input_value_and_save, set_input_value, click_css, confirm_prompt from .utils import set_input_value_and_save, set_input_value, click_css, confirm_prompt
...@@ -582,19 +581,11 @@ class CourseOutlinePage(CoursePage, CourseOutlineContainer): ...@@ -582,19 +581,11 @@ class CourseOutlinePage(CoursePage, CourseOutlineContainer):
@property @property
def license(self): def license(self):
"""
Returns the course license text, if present. Else returns None.
"""
return self.q(css=".license-value").first.text[0] return self.q(css=".license-value").first.text[0]
def edit_course_start_date(self):
self.q(css=".status-release .action-edit a.action-button").click()
sp = SettingsPage(
self.browser,
self.course_info['course_org'],
self.course_info['course_num'],
self.course_info['course_run'],
)
sp.wait_for_page()
return sp
class CourseOutlineModal(object): class CourseOutlineModal(object):
MODAL_SELECTOR = ".wrapper-modal-window" MODAL_SELECTOR = ".wrapper-modal-window"
......
...@@ -81,6 +81,10 @@ class SettingsPage(CoursePage): ...@@ -81,6 +81,10 @@ class SettingsPage(CoursePage):
@property @property
def course_license(self): def course_license(self):
"""
Property. Returns the text of the license type for the course
("All Rights Reserved" or "Creative Commons")
"""
license_types_css = "section.license ul.license-types li.license-type" license_types_css = "section.license ul.license-types li.license-type"
self.wait_for_element_presence( self.wait_for_element_presence(
license_types_css, license_types_css,
...@@ -89,10 +93,20 @@ class SettingsPage(CoursePage): ...@@ -89,10 +93,20 @@ class SettingsPage(CoursePage):
selected = self.q(css=license_types_css + " button.is-selected") selected = self.q(css=license_types_css + " button.is-selected")
if selected.is_present(): if selected.is_present():
return selected.text[0] return selected.text[0]
# Look for the license text that will be displayed by default,
# if no button is yet explicitly selected
license_text = self.q(css='section.license span.license-text')
if license_text.is_present():
return license_text.text[0]
return None return None
@course_license.setter @course_license.setter
def course_license(self, license_name): def course_license(self, license_name):
"""
Sets the course license to the given license_name
(str, "All Rights Reserved" or "Creative Commons")
"""
license_types_css = "section.license ul.license-types li.license-type" license_types_css = "section.license ul.license-types li.license-type"
self.wait_for_element_presence( self.wait_for_element_presence(
license_types_css, license_types_css,
......
...@@ -406,7 +406,11 @@ class AdvancedSettingsValidationTest(StudioCourseTest): ...@@ -406,7 +406,11 @@ class AdvancedSettingsValidationTest(StudioCourseTest):
@attr('shard_1') @attr('shard_1')
class ContentLicenseTest(StudioCourseTest): class ContentLicenseTest(StudioCourseTest):
def setUp(self): """
Tests for course-level licensing (that is, setting the license,
for an entire course's content, to All Rights Reserved or Creative Commons)
"""
def setUp(self): # pylint: disable=arguments-differ
super(ContentLicenseTest, self).setUp() super(ContentLicenseTest, self).setUp()
self.outline_page = CourseOutlinePage( self.outline_page = CourseOutlinePage(
self.browser, self.browser,
...@@ -429,13 +433,13 @@ class ContentLicenseTest(StudioCourseTest): ...@@ -429,13 +433,13 @@ class ContentLicenseTest(StudioCourseTest):
def test_empty_license(self): def test_empty_license(self):
""" """
When I visit the Studio settings page, When I visit the Studio settings page,
I see that the course license is "None" by default. I see that the course license is "All Rights Reserved" by default.
Then I visit the LMS courseware page, Then I visit the LMS courseware page,
and I see that there is no course license displayed. and I see that the default course license is displayed.
""" """
self.assertIsNone(self.settings_page.course_license) self.assertEqual(self.settings_page.course_license, "All Rights Reserved")
self.lms_courseware.visit() self.lms_courseware.visit()
self.assertIsNone(self.lms_courseware.course_license) self.assertEqual(self.lms_courseware.course_license, "© All Rights Reserved")
def test_arr_license(self): def test_arr_license(self):
""" """
...@@ -469,4 +473,6 @@ class ContentLicenseTest(StudioCourseTest): ...@@ -469,4 +473,6 @@ class ContentLicenseTest(StudioCourseTest):
self.assertEqual(self.settings_page.course_license, "Creative Commons") self.assertEqual(self.settings_page.course_license, "Creative Commons")
self.lms_courseware.visit() self.lms_courseware.visit()
self.assertEqual(self.lms_courseware.course_license, "Some Rights Reserved") # The course_license text will include a bunch of screen reader text to explain
# the selected options
self.assertIn("Some Rights Reserved", self.lms_courseware.course_license)
# coding: utf-8 # coding: utf-8
"""
Acceptance tests for licensing of the Video module
"""
from __future__ import unicode_literals from __future__ import unicode_literals
from nose.plugins.attrib import attr from nose.plugins.attrib import attr
from ..studio.base_studio_test import StudioCourseTest from ..studio.base_studio_test import StudioCourseTest
...@@ -11,8 +14,11 @@ from ...fixtures.course import XBlockFixtureDesc ...@@ -11,8 +14,11 @@ from ...fixtures.course import XBlockFixtureDesc
@attr('shard_1') @attr('shard_1')
class VideoLicenseTest(StudioCourseTest): class VideoLicenseTest(StudioCourseTest):
"""
def setUp(self): Tests for video module-level licensing (that is, setting the license,
for a specific video module, to All Rights Reserved or Creative Commons)
"""
def setUp(self): # pylint: disable=arguments-differ
super(VideoLicenseTest, self).setUp() super(VideoLicenseTest, self).setUp()
self.lms_courseware = CoursewarePage( self.lms_courseware = CoursewarePage(
...@@ -107,4 +113,4 @@ class VideoLicenseTest(StudioCourseTest): ...@@ -107,4 +113,4 @@ class VideoLicenseTest(StudioCourseTest):
self.assertTrue(video.is_present()) self.assertTrue(video.is_present())
video_license = self.lms_courseware.q(css=".vert .xblock.xmodule_VideoModule .xblock-license") video_license = self.lms_courseware.q(css=".vert .xblock.xmodule_VideoModule .xblock-license")
self.assertTrue(video_license.is_present()) self.assertTrue(video_license.is_present())
self.assertEqual(video_license.text[0], "Some Rights Reserved") self.assertIn("Some Rights Reserved", video_license.text[0])
...@@ -369,6 +369,9 @@ FEATURES = { ...@@ -369,6 +369,9 @@ FEATURES = {
# enable beacons for lms onload event statistics # enable beacons for lms onload event statistics
'ENABLE_ONLOAD_BEACON': False, 'ENABLE_ONLOAD_BEACON': False,
# Toggle platform-wide course licensing
'LICENSING': False,
# Certificates Web/HTML Views # Certificates Web/HTML Views
'CERTIFICATES_HTML_VIEW': False, 'CERTIFICATES_HTML_VIEW': False,
......
...@@ -115,6 +115,9 @@ FEATURES['MILESTONES_APP'] = True ...@@ -115,6 +115,9 @@ FEATURES['MILESTONES_APP'] = True
########################### Entrance Exams ################################# ########################### Entrance Exams #################################
FEATURES['ENTRANCE_EXAMS'] = True FEATURES['ENTRANCE_EXAMS'] = True
################################ COURSE LICENSES ################################
FEATURES['LICENSING'] = True
########################## Courseware Search ####################### ########################## Courseware Search #######################
FEATURES['ENABLE_COURSEWARE_SEARCH'] = True FEATURES['ENABLE_COURSEWARE_SEARCH'] = True
......
...@@ -207,9 +207,14 @@ ${fragment.foot_html()} ...@@ -207,9 +207,14 @@ ${fragment.foot_html()}
</div> </div>
</div> </div>
<div class="container-footer"> <div class="container-footer">
% if settings.FEATURES.get("LICENSING", False) and getattr(course, "license", None): % if settings.FEATURES.get("LICENSING", False):
<div class="course-license"> <div class="course-license">
% if getattr(course, "license", None):
<%include file="../license.html" args="license=course.license" /> <%include file="../license.html" args="license=course.license" />
% else:
## Default course license: All Rights Reserved, if none is explicitly set.
<%include file="../license.html" args="license='all-rights-reserved'" />
% endif
</div> </div>
% endif % endif
</div> </div>
......
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