diff --git a/cms/djangoapps/contentstore/tests/test_libraries.py b/cms/djangoapps/contentstore/tests/test_libraries.py index dc6d8b976d..f6b8905dcc 100644 --- a/cms/djangoapps/contentstore/tests/test_libraries.py +++ b/cms/djangoapps/contentstore/tests/test_libraries.py @@ -847,3 +847,27 @@ class TestOverrides(LibraryTestCase): self.assertEqual(self.problem_in_course.display_name, new_display_name) self.assertEqual(self.problem_in_course.weight, new_weight) self.assertEqual(self.problem_in_course.data, new_data_value) + + +class TestIncompatibleModuleStore(LibraryTestCase): + """ + Tests for proper validation errors with an incompatible course modulestore. + """ + def setUp(self): + super(TestIncompatibleModuleStore, self).setUp() + # Create a course in an incompatible modulestore. + with modulestore().default_store(ModuleStoreEnum.Type.mongo): + self.course = CourseFactory.create() + + # Add a LibraryContent block to the course: + self.lc_block = self._add_library_content_block(self.course, self.lib_key) + + def test_incompatible_modulestore(self): + """ + Verifies that, if a user is using a modulestore that doesn't support libraries, + a validation error will be produced. + """ + validation = self.lc_block.validate() + self.assertEqual(validation.summary.type, validation.summary.ERROR) + self.assertIn( + "This course does not support content libraries.", validation.summary.text) diff --git a/common/lib/xmodule/xmodule/library_content_module.py b/common/lib/xmodule/xmodule/library_content_module.py index e87d8fcbbe..2d9aa6fbc7 100644 --- a/common/lib/xmodule/xmodule/library_content_module.py +++ b/common/lib/xmodule/xmodule/library_content_module.py @@ -434,6 +434,18 @@ class LibraryContentDescriptor(LibraryContentFields, MakoModuleDescriptor, XmlDe validation = super(LibraryContentDescriptor, self).validate() if not isinstance(validation, StudioValidation): validation = StudioValidation.copy(validation) + library_tools = self.runtime.service(self, "library_tools") + if not (library_tools and library_tools.can_use_library_content(self)): + validation.set_summary( + StudioValidationMessage( + StudioValidationMessage.ERROR, + _( + u"This course does not support content libraries. " + u"Contact your system administrator for more information." + ) + ) + ) + return validation if not self.source_libraries: validation.set_summary( StudioValidationMessage( diff --git a/common/lib/xmodule/xmodule/library_tools.py b/common/lib/xmodule/xmodule/library_tools.py index aebe2aef98..0297e4fbbb 100644 --- a/common/lib/xmodule/xmodule/library_tools.py +++ b/common/lib/xmodule/xmodule/library_tools.py @@ -96,6 +96,12 @@ class LibraryToolsService(object): assert isinstance(descriptor, CapaDescriptor) return capa_type in descriptor.problem_types + def can_use_library_content(self, block): + """ + Determines whether a modulestore holding a course_id supports libraries. + """ + return self.store.check_supports(block.location.course_key, 'copy_from_template') + def update_children(self, dest_block, user_id, user_perms=None): """ This method is to be used when any of the libraries that a LibraryContentModule diff --git a/common/lib/xmodule/xmodule/modulestore/__init__.py b/common/lib/xmodule/xmodule/modulestore/__init__.py index 8d92a3579f..4d7a6e2df3 100644 --- a/common/lib/xmodule/xmodule/modulestore/__init__.py +++ b/common/lib/xmodule/xmodule/modulestore/__init__.py @@ -441,6 +441,17 @@ class ModuleStoreAssetBase(object): ret_assets.append(new_asset) return ret_assets + # pylint: disable=unused-argument + def check_supports(self, course_key, method): + """ + Verifies that a modulestore supports a particular method. + + Some modulestores may differ based on the course_key, such + as mixed (since it has to find the underlying modulestore), + so it's required as part of the method signature. + """ + return hasattr(self, method) + class ModuleStoreAssetWriteInterface(ModuleStoreAssetBase): """ diff --git a/common/lib/xmodule/xmodule/modulestore/mixed.py b/common/lib/xmodule/xmodule/modulestore/mixed.py index 96471c9915..b3c775619b 100644 --- a/common/lib/xmodule/xmodule/modulestore/mixed.py +++ b/common/lib/xmodule/xmodule/modulestore/mixed.py @@ -850,6 +850,17 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase): store = self._verify_modulestore_support(xblock.location.course_key, 'has_changes') return store.has_changes(xblock) + def check_supports(self, course_key, method): + """ + Verifies that the modulestore for a particular course supports a feature. + Returns True/false based on this. + """ + try: + self._verify_modulestore_support(course_key, method) + return True + except NotImplementedError: + return False + def _verify_modulestore_support(self, course_key, method): """ Finds and returns the store that contains the course for the given location, and verifying