Merge pull request #7315 from edx/db/creative-commons
Allow custom licensing for course content
This commit is contained in:
@@ -88,6 +88,16 @@ class CoursewarePage(CoursePage):
|
||||
|
||||
return True
|
||||
|
||||
@property
|
||||
def course_license(self):
|
||||
"""
|
||||
Returns the course license text, if present. Else returns None.
|
||||
"""
|
||||
element = self.q(css="#content .container-footer .course-license")
|
||||
if element.is_present():
|
||||
return element.text[0]
|
||||
return None
|
||||
|
||||
def get_active_subsection_url(self):
|
||||
"""
|
||||
return the url of the active subsection in the left nav
|
||||
|
||||
@@ -487,6 +487,19 @@ class XBlockWrapper(PageObject):
|
||||
"""
|
||||
type_in_codemirror(self, index, text, find_prefix='$("{}").find'.format(self.editor_selector))
|
||||
|
||||
def set_license(self, license_type):
|
||||
"""
|
||||
Uses the UI to set the course's license to the given license_type (str)
|
||||
"""
|
||||
css_selector = (
|
||||
"ul.license-types li[data-license={license_type}] button"
|
||||
).format(license_type=license_type)
|
||||
self.wait_for_element_presence(
|
||||
css_selector,
|
||||
"{license_type} button is present".format(license_type=license_type)
|
||||
)
|
||||
self.q(css=css_selector).click()
|
||||
|
||||
def save_settings(self):
|
||||
"""
|
||||
Click on settings Save button.
|
||||
|
||||
@@ -579,6 +579,13 @@ class CourseOutlinePage(CoursePage, CourseOutlineContainer):
|
||||
"""
|
||||
return self.children(CourseOutlineChild)
|
||||
|
||||
@property
|
||||
def license(self):
|
||||
"""
|
||||
Returns the course license text, if present. Else returns None.
|
||||
"""
|
||||
return self.q(css=".license-value").first.text[0]
|
||||
|
||||
|
||||
class CourseOutlineModal(object):
|
||||
MODAL_SELECTOR = ".wrapper-modal-window"
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# coding: utf-8
|
||||
"""
|
||||
Course Schedule and Details Settings page.
|
||||
"""
|
||||
from __future__ import unicode_literals
|
||||
from bok_choy.promise import EmptyPromise
|
||||
|
||||
from .course_page import CoursePage
|
||||
@@ -17,6 +19,13 @@ class SettingsPage(CoursePage):
|
||||
def is_browser_on_page(self):
|
||||
return self.q(css='body.view-settings').present
|
||||
|
||||
def refresh_and_wait_for_load(self):
|
||||
"""
|
||||
Refresh the page and wait for all resources to load.
|
||||
"""
|
||||
self.browser.refresh()
|
||||
self.wait_for_page()
|
||||
|
||||
def get_elements(self, css_selector):
|
||||
self.wait_for_element_presence(
|
||||
css_selector,
|
||||
@@ -70,6 +79,50 @@ class SettingsPage(CoursePage):
|
||||
'Entrance exam minimum score percent is invisible'
|
||||
)
|
||||
|
||||
@property
|
||||
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"
|
||||
self.wait_for_element_presence(
|
||||
license_types_css,
|
||||
"license type buttons are present",
|
||||
)
|
||||
selected = self.q(css=license_types_css + " button.is-selected")
|
||||
if selected.is_present():
|
||||
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
|
||||
|
||||
@course_license.setter
|
||||
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"
|
||||
self.wait_for_element_presence(
|
||||
license_types_css,
|
||||
"license type buttons are present",
|
||||
)
|
||||
button_xpath = (
|
||||
"//section[contains(@class, 'license')]"
|
||||
"//ul[contains(@class, 'license-types')]"
|
||||
"//li[contains(@class, 'license-type')]"
|
||||
"//button[contains(text(),'{license_name}')]"
|
||||
).format(license_name=license_name)
|
||||
button = self.q(xpath=button_xpath)
|
||||
if not button.present:
|
||||
raise Exception("Invalid license name: {name}".format(name=license_name))
|
||||
button.click()
|
||||
|
||||
def save_changes(self, wait_for_confirmation=True):
|
||||
"""
|
||||
Clicks save button, waits for confirmation unless otherwise specified
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
# coding: utf-8
|
||||
"""
|
||||
Acceptance tests for Studio's Setting pages
|
||||
"""
|
||||
from __future__ import unicode_literals
|
||||
from nose.plugins.attrib import attr
|
||||
|
||||
from base_studio_test import StudioCourseTest
|
||||
from bok_choy.promise import EmptyPromise
|
||||
from ...fixtures.course import XBlockFixtureDesc
|
||||
from ..helpers import create_user_partition_json
|
||||
from ...pages.studio.overview import CourseOutlinePage
|
||||
from ...pages.studio.settings import SettingsPage
|
||||
from ...pages.studio.settings_advanced import AdvancedSettingsPage
|
||||
from ...pages.studio.settings_group_configurations import GroupConfigurationsPage
|
||||
from ...pages.lms.courseware import CoursewarePage
|
||||
from unittest import skip
|
||||
from textwrap import dedent
|
||||
from xmodule.partitions.partitions import Group
|
||||
@@ -397,3 +402,77 @@ class AdvancedSettingsValidationTest(StudioCourseTest):
|
||||
expected_fields = self.advanced_settings.expected_settings_names
|
||||
displayed_fields = self.advanced_settings.displayed_settings_names
|
||||
self.assertEquals(set(displayed_fields), set(expected_fields))
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
class ContentLicenseTest(StudioCourseTest):
|
||||
"""
|
||||
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()
|
||||
self.outline_page = CourseOutlinePage(
|
||||
self.browser,
|
||||
self.course_info['org'],
|
||||
self.course_info['number'],
|
||||
self.course_info['run']
|
||||
)
|
||||
self.settings_page = SettingsPage(
|
||||
self.browser,
|
||||
self.course_info['org'],
|
||||
self.course_info['number'],
|
||||
self.course_info['run']
|
||||
)
|
||||
self.lms_courseware = CoursewarePage(
|
||||
self.browser,
|
||||
self.course_id,
|
||||
)
|
||||
self.settings_page.visit()
|
||||
|
||||
def test_empty_license(self):
|
||||
"""
|
||||
When I visit the Studio settings page,
|
||||
I see that the course license is "All Rights Reserved" by default.
|
||||
Then I visit the LMS courseware page,
|
||||
and I see that the default course license is displayed.
|
||||
"""
|
||||
self.assertEqual(self.settings_page.course_license, "All Rights Reserved")
|
||||
self.lms_courseware.visit()
|
||||
self.assertEqual(self.lms_courseware.course_license, "© All Rights Reserved")
|
||||
|
||||
def test_arr_license(self):
|
||||
"""
|
||||
When I visit the Studio settings page,
|
||||
and I set the course license to "All Rights Reserved",
|
||||
and I refresh the page,
|
||||
I see that the course license is "All Rights Reserved".
|
||||
Then I visit the LMS courseware page,
|
||||
and I see that the course license is "All Rights Reserved".
|
||||
"""
|
||||
self.settings_page.course_license = "All Rights Reserved"
|
||||
self.settings_page.save_changes()
|
||||
self.settings_page.refresh_and_wait_for_load()
|
||||
self.assertEqual(self.settings_page.course_license, "All Rights Reserved")
|
||||
|
||||
self.lms_courseware.visit()
|
||||
self.assertEqual(self.lms_courseware.course_license, "© All Rights Reserved")
|
||||
|
||||
def test_cc_license(self):
|
||||
"""
|
||||
When I visit the Studio settings page,
|
||||
and I set the course license to "Creative Commons",
|
||||
and I refresh the page,
|
||||
I see that the course license is "Creative Commons".
|
||||
Then I visit the LMS courseware page,
|
||||
and I see that the course license is "Some Rights Reserved".
|
||||
"""
|
||||
self.settings_page.course_license = "Creative Commons"
|
||||
self.settings_page.save_changes()
|
||||
self.settings_page.refresh_and_wait_for_load()
|
||||
self.assertEqual(self.settings_page.course_license, "Creative Commons")
|
||||
|
||||
self.lms_courseware.visit()
|
||||
# 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)
|
||||
|
||||
116
common/test/acceptance/tests/video/test_video_license.py
Normal file
116
common/test/acceptance/tests/video/test_video_license.py
Normal file
@@ -0,0 +1,116 @@
|
||||
# coding: utf-8
|
||||
"""
|
||||
Acceptance tests for licensing of the Video module
|
||||
"""
|
||||
from __future__ import unicode_literals
|
||||
from nose.plugins.attrib import attr
|
||||
from ..studio.base_studio_test import StudioCourseTest
|
||||
|
||||
#from ..helpers import UniqueCourseTest
|
||||
from ...pages.studio.overview import CourseOutlinePage
|
||||
from ...pages.lms.courseware import CoursewarePage
|
||||
from ...fixtures.course import XBlockFixtureDesc
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
class VideoLicenseTest(StudioCourseTest):
|
||||
"""
|
||||
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()
|
||||
|
||||
self.lms_courseware = CoursewarePage(
|
||||
self.browser,
|
||||
self.course_id,
|
||||
)
|
||||
self.studio_course_outline = CourseOutlinePage(
|
||||
self.browser,
|
||||
self.course_info['org'],
|
||||
self.course_info['number'],
|
||||
self.course_info['run']
|
||||
)
|
||||
|
||||
# used by StudioCourseTest.setUp()
|
||||
def populate_course_fixture(self, course_fixture):
|
||||
"""
|
||||
Create a course with a single chapter.
|
||||
That chapter has a single section.
|
||||
That section has a single vertical.
|
||||
That vertical has a single video element.
|
||||
"""
|
||||
video_block = XBlockFixtureDesc('video', "Test Video")
|
||||
vertical = XBlockFixtureDesc('vertical', "Test Vertical")
|
||||
vertical.add_children(video_block)
|
||||
sequential = XBlockFixtureDesc('sequential', "Test Section")
|
||||
sequential.add_children(vertical)
|
||||
chapter = XBlockFixtureDesc('chapter', "Test Chapter")
|
||||
chapter.add_children(sequential)
|
||||
self.course_fixture.add_children(chapter)
|
||||
|
||||
def test_empty_license(self):
|
||||
"""
|
||||
When I visit the LMS courseware,
|
||||
I can see that the video is present
|
||||
but it has no license displayed by default.
|
||||
"""
|
||||
self.lms_courseware.visit()
|
||||
video = self.lms_courseware.q(css=".vert .xblock .video")
|
||||
self.assertTrue(video.is_present())
|
||||
video_license = self.lms_courseware.q(css=".vert .xblock.xmodule_VideoModule .xblock-license")
|
||||
self.assertFalse(video_license.is_present())
|
||||
|
||||
def test_arr_license(self):
|
||||
"""
|
||||
When I edit a video element in Studio,
|
||||
I can set an "All Rights Reserved" license on that video element.
|
||||
When I visit the LMS courseware,
|
||||
I can see that the video is present
|
||||
and that it has "All Rights Reserved" displayed for the license.
|
||||
"""
|
||||
self.studio_course_outline.visit()
|
||||
subsection = self.studio_course_outline.section_at(0).subsection_at(0)
|
||||
subsection.expand_subsection()
|
||||
unit = subsection.unit_at(0)
|
||||
container_page = unit.go_to()
|
||||
container_page.edit()
|
||||
video = [xb for xb in container_page.xblocks if xb.name == "Test Video"][0]
|
||||
video.edit().open_advanced_tab()
|
||||
video.set_license('all-rights-reserved')
|
||||
video.save_settings()
|
||||
container_page.publish_action.click()
|
||||
|
||||
self.lms_courseware.visit()
|
||||
video = self.lms_courseware.q(css=".vert .xblock .video")
|
||||
self.assertTrue(video.is_present())
|
||||
video_license = self.lms_courseware.q(css=".vert .xblock.xmodule_VideoModule .xblock-license")
|
||||
self.assertTrue(video_license.is_present())
|
||||
self.assertEqual(video_license.text[0], "© All Rights Reserved")
|
||||
|
||||
def test_cc_license(self):
|
||||
"""
|
||||
When I edit a video element in Studio,
|
||||
I can set a "Creative Commons" license on that video element.
|
||||
When I visit the LMS courseware,
|
||||
I can see that the video is present
|
||||
and that it has "Some Rights Reserved" displayed for the license.
|
||||
"""
|
||||
self.studio_course_outline.visit()
|
||||
subsection = self.studio_course_outline.section_at(0).subsection_at(0)
|
||||
subsection.expand_subsection()
|
||||
unit = subsection.unit_at(0)
|
||||
container_page = unit.go_to()
|
||||
container_page.edit()
|
||||
video = [xb for xb in container_page.xblocks if xb.name == "Test Video"][0]
|
||||
video.edit().open_advanced_tab()
|
||||
video.set_license('creative-commons')
|
||||
video.save_settings()
|
||||
container_page.publish_action.click()
|
||||
|
||||
self.lms_courseware.visit()
|
||||
video = self.lms_courseware.q(css=".vert .xblock .video")
|
||||
self.assertTrue(video.is_present())
|
||||
video_license = self.lms_courseware.q(css=".vert .xblock.xmodule_VideoModule .xblock-license")
|
||||
self.assertTrue(video_license.is_present())
|
||||
self.assertIn("Some Rights Reserved", video_license.text[0])
|
||||
@@ -1,4 +1,4 @@
|
||||
<course course_image="just_a_test.jpg">
|
||||
<course course_image="just_a_test.jpg" license="creative-commons: BY">
|
||||
<textbook title="Textbook" book_url="https://s3.amazonaws.com/edx-textbooks/guttag_computation_v3/"/>
|
||||
<chapter url_name="Overview">
|
||||
<videosequence url_name="Toy_Videos">
|
||||
@@ -10,7 +10,7 @@
|
||||
<html url_name="badlink"/>
|
||||
<html url_name="with_styling"/>
|
||||
<html url_name="just_img"/>
|
||||
<video url_name="Video_Resources" youtube_id_1_0="1bK-WdDi6Qw" display_name="Video Resources"/>
|
||||
<video url_name="Video_Resources" youtube_id_1_0="1bK-WdDi6Qw" display_name="Video Resources" license="all-rights-reserved"/>
|
||||
</videosequence>
|
||||
<video url_name="Welcome" youtube_id_1_0="p2Q6BrNhdh8" display_name="Welcome"/>
|
||||
<video url_name="video_123456789012" youtube_id_1_0="p2Q6BrNhdh8" display_name='Test Video'/>
|
||||
|
||||
Reference in New Issue
Block a user