Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions cms/djangoapps/contentstore/views/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,9 +231,9 @@ def container_handler(request, usage_key_string):
return HttpResponseBadRequest("Only supports HTML requests")


def get_component_templates(course):
def get_component_templates(courselike, library=False):
"""
Returns the applicable component templates that can be used by the specified course.
Returns the applicable component templates that can be used by the specified course or library.
"""
def create_template_dict(name, cat, boilerplate_name=None, is_common=False):
"""
Expand Down Expand Up @@ -264,7 +264,13 @@ def create_template_dict(name, cat, boilerplate_name=None, is_common=False):
categories = set()
# The component_templates array is in the order of "advanced" (if present), followed
# by the components in the order listed in COMPONENT_TYPES.
for category in COMPONENT_TYPES:
component_types = COMPONENT_TYPES[:]

# Libraries do not support discussions
if library:
component_types = [component for component in component_types if component != 'discussion']

for category in component_types:
templates_for_category = []
component_class = _load_mixed_class(category)
# add the default template with localized display name
Expand All @@ -278,7 +284,7 @@ def create_template_dict(name, cat, boilerplate_name=None, is_common=False):
if hasattr(component_class, 'templates'):
for template in component_class.templates():
filter_templates = getattr(component_class, 'filter_templates', None)
if not filter_templates or filter_templates(template, course):
if not filter_templates or filter_templates(template, courselike):
templates_for_category.append(
create_template_dict(
_(template['metadata'].get('display_name')),
Expand All @@ -303,11 +309,15 @@ def create_template_dict(name, cat, boilerplate_name=None, is_common=False):
"display_name": component_display_names[category]
})

# Libraries do not support advanced components at this time.
if library:
return component_templates

# Check if there are any advanced modules specified in the course policy.
# These modules should be specified as a list of strings, where the strings
# are the names of the modules in ADVANCED_COMPONENT_TYPES that should be
# enabled for the course.
course_advanced_keys = course.advanced_modules
course_advanced_keys = courselike.advanced_modules
advanced_component_templates = {"type": "advanced", "templates": [], "display_name": _("Advanced")}
advanced_component_types = _advanced_component_types()
# Set component types according to course policy file
Expand Down
7 changes: 7 additions & 0 deletions cms/djangoapps/contentstore/views/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,13 @@ def _create_item(request):
if not has_course_author_access(request.user, usage_key.course_key):
raise PermissionDenied()

if isinstance(usage_key, LibraryUsageLocator):
# Only these categories are supported at this time.
if category not in ['html', 'problem', 'video']:
return HttpResponseBadRequest(
"Category '%s' not supported for Libraries" % category, content_type='text/plain'
)

store = modulestore()
with store.bulk_operations(usage_key.course_key):
parent = store.get_item(usage_key)
Expand Down
2 changes: 1 addition & 1 deletion cms/djangoapps/contentstore/views/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ def library_blocks_view(library, response_format):
})

xblock_info = create_xblock_info(library, include_ancestor_info=False, graders=[])
component_templates = get_component_templates(library)
component_templates = get_component_templates(library, library=True)

return render_to_response('library.html', {
'context_library': library,
Expand Down
35 changes: 35 additions & 0 deletions cms/djangoapps/contentstore/views/tests/test_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -1469,6 +1469,41 @@ def validate_component_xblock_info(self, xblock_info, original_block):
self.assertIsNone(xblock_info.get('graders', None))


class TestLibraryXBlockCreation(ItemTest):
"""
Tests the adding of XBlocks to Library
"""
def test_add_xblock(self):
"""
Verify we can add an XBlock to a Library.
"""
lib = LibraryFactory.create()
self.create_xblock(parent_usage_key=lib.location, display_name='Test', category="html")
lib = self.store.get_library(lib.location.library_key)
self.assertTrue(lib.children)
xblock_locator = lib.children[0]
self.assertEqual(self.store.get_item(xblock_locator).display_name, 'Test')

def test_no_add_discussion(self):
"""
Verify we cannot add a discussion module to a Library.
"""
lib = LibraryFactory.create()
response = self.create_xblock(parent_usage_key=lib.location, display_name='Test', category='discussion')
self.assertEqual(response.status_code, 400)
lib = self.store.get_library(lib.location.library_key)
self.assertFalse(lib.children)

def test_no_add_advanced(self):
lib = LibraryFactory.create()
lib.advanced_modules = ['lti']
lib.save()
response = self.create_xblock(parent_usage_key=lib.location, display_name='Test', category='lti')
self.assertEqual(response.status_code, 400)
lib = self.store.get_library(lib.location.library_key)
self.assertFalse(lib.children)


class TestXBlockPublishingInfo(ItemTest):
"""
Unit tests for XBlock's outline handling.
Expand Down
14 changes: 14 additions & 0 deletions cms/djangoapps/contentstore/views/tests/test_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
More important high-level tests are in contentstore/tests/test_libraries.py
"""
from contentstore.tests.utils import AjaxEnabledTestClient, parse_json
from contentstore.views.component import get_component_templates
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import LibraryFactory
from mock import patch
Expand Down Expand Up @@ -183,3 +184,16 @@ def test_no_access(self):
lib = LibraryFactory.create()
response = self.client.get(make_url_for_lib(lib.location.library_key))
self.assertEqual(response.status_code, 403)

def test_get_component_templates(self):
"""
Verify that templates for adding discussion and advanced components to
content libraries are not provided.
"""
lib = LibraryFactory.create()
lib.advanced_modules = ['lti']
lib.save()
templates = [template['type'] for template in get_component_templates(lib, library=True)]
self.assertIn('problem', templates)
self.assertNotIn('discussion', templates)
self.assertNotIn('advanced', templates)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No newline at the end of file. :) I'm going to become pylint myself soon :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That does it. I need to find the setting in my IDE to automate this.

7 changes: 6 additions & 1 deletion common/test/acceptance/tests/studio/test_studio_library.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""
Acceptance tests for Content Libraries in Studio
"""

from .base_studio_test import StudioLibraryTest
from ...pages.studio.utils import add_component
from ...pages.studio.library import LibraryPage
Expand Down Expand Up @@ -102,3 +101,9 @@ def test_add_edit_xblock(self):
self.assertEqual(len(self.lib_page.xblocks), 1)
problem_block = self.lib_page.xblocks[0]
self.assertIn("Laura Roslin", problem_block.student_content)

def test_no_discussion_button(self):
"""
Ensure the UI is not loaded for adding discussions.
"""
self.assertFalse(self.browser.find_elements_by_css_selector('span.large-discussion-icon'))