diff --git a/cms/djangoapps/contentstore/errors.py b/cms/djangoapps/contentstore/errors.py index 54f425d27d5e..b1ad51f87398 100644 --- a/cms/djangoapps/contentstore/errors.py +++ b/cms/djangoapps/contentstore/errors.py @@ -5,8 +5,6 @@ COURSE_ALREADY_EXIST = _('Aborting import because a course with this id: {} already exists.') COURSE_PERMISSION_DENIED = _('Permission denied. You do not have write access to this course.') -ERROR_WHILE_READING = _('Error while reading {}. Check file for XML errors.') -FAILED_TO_IMPORT_MODULE = _('Failed to import module: {} at location: {}') FILE_MISSING = _('Could not find the {0} file in the package.') FILE_NOT_FOUND = _('Uploaded Tar file not found. Try again.') INVALID_FILE_TYPE = _('We only support uploading a .tar.gz file.') diff --git a/cms/djangoapps/contentstore/exceptions.py b/cms/djangoapps/contentstore/exceptions.py index 380a2a637332..bf33bad8f94c 100644 --- a/cms/djangoapps/contentstore/exceptions.py +++ b/cms/djangoapps/contentstore/exceptions.py @@ -1,45 +1,15 @@ """ A common module for managing exceptions. Helps to avoid circular references """ -from .errors import ERROR_WHILE_READING, FAILED_TO_IMPORT_MODULE - - -class CourseImportException(Exception): - """Base exception class for course import workflows.""" - - def __init__(self): - super().__init__(self.description) # pylint: disable=no-member - - -class ErrorReadingFileException(CourseImportException): - """ - Raised when error occurs while trying to read a file. - """ - - def __init__(self, filename, **kwargs): - self.description = ERROR_WHILE_READING.format(filename) - super().__init__(**kwargs) - - -class ModuleFailedToImport(CourseImportException): - """ - Raised when a module is failed to import. - """ - - def __init__(self, display_name, location, **kwargs): - self.description = FAILED_TO_IMPORT_MODULE.format(display_name, location) - super().__init__(**kwargs) class AssetNotFoundException(Exception): """ Raised when asset not found """ - pass # lint-amnesty, pylint: disable=unnecessary-pass class AssetSizeTooLargeException(Exception): """ Raised when the size of an uploaded asset exceeds the maximum size limit. """ - pass # lint-amnesty, pylint: disable=unnecessary-pass diff --git a/cms/djangoapps/contentstore/signals/handlers.py b/cms/djangoapps/contentstore/signals/handlers.py index b82caf05dc3b..c70a801ca71f 100644 --- a/cms/djangoapps/contentstore/signals/handlers.py +++ b/cms/djangoapps/contentstore/signals/handlers.py @@ -70,6 +70,15 @@ def listen_for_course_publish(sender, course_key, **kwargs): # pylint: disable= update_search_index.delay(course_key_str, datetime.now(UTC).isoformat()) +@receiver(SignalHandler.course_deleted) +def listen_for_course_delete(sender, course_key, **kwargs): # pylint: disable=unused-argument + """ + Catches the signal that a course has been deleted + and removes its entry from the Course About Search index. + """ + CourseAboutSearchIndexer.remove_deleted_items(course_key) + + @receiver(SignalHandler.library_updated) def listen_for_library_update(sender, library_key, **kwargs): # pylint: disable=unused-argument """ diff --git a/cms/djangoapps/contentstore/tasks.py b/cms/djangoapps/contentstore/tasks.py index 82ae009702db..9186ad7fd4f0 100644 --- a/cms/djangoapps/contentstore/tasks.py +++ b/cms/djangoapps/contentstore/tasks.py @@ -61,9 +61,8 @@ from xmodule.modulestore.django import modulestore from xmodule.modulestore.exceptions import DuplicateCourseError, InvalidProctoringProvider, ItemNotFoundError from xmodule.modulestore.xml_exporter import export_course_to_xml, export_library_to_xml -from xmodule.modulestore.xml_importer import import_course_from_xml, import_library_from_xml +from xmodule.modulestore.xml_importer import CourseImportException, import_course_from_xml, import_library_from_xml -from .exceptions import CourseImportException from .outlines import update_outline_from_modulestore from .outlines_regenerate import CourseOutlineRegenerate from .toggles import bypass_olx_failure_enabled diff --git a/cms/djangoapps/contentstore/views/tests/test_import_export.py b/cms/djangoapps/contentstore/views/tests/test_import_export.py index ed1df9665c72..38e0388b7c7d 100644 --- a/cms/djangoapps/contentstore/views/tests/test_import_export.py +++ b/cms/djangoapps/contentstore/views/tests/test_import_export.py @@ -28,7 +28,6 @@ from user_tasks.models import UserTaskStatus from cms.djangoapps.contentstore import errors as import_error -from cms.djangoapps.contentstore.exceptions import ErrorReadingFileException, ModuleFailedToImport from cms.djangoapps.contentstore.storage import course_import_export_storage from cms.djangoapps.contentstore.tests.test_libraries import LibraryTestCase from cms.djangoapps.contentstore.tests.utils import CourseTestCase @@ -45,7 +44,13 @@ from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory, LibraryFactory from xmodule.modulestore.tests.utils import SPLIT_MODULESTORE_SETUP, TEST_DATA_DIR, MongoContentstoreBuilder from xmodule.modulestore.xml_exporter import export_course_to_xml, export_library_to_xml -from xmodule.modulestore.xml_importer import CourseImportManager, import_course_from_xml, import_library_from_xml +from xmodule.modulestore.xml_importer import ( + CourseImportManager, + ErrorReadingFileException, + import_course_from_xml, + import_library_from_xml, + ModuleFailedToImport, +) TASK_LOGGER = 'cms.djangoapps.contentstore.tasks.LOGGER' TEST_DATA_CONTENTSTORE = copy.deepcopy(settings.CONTENTSTORE) diff --git a/common/lib/xmodule/xmodule/library_sourced_block.py b/common/lib/xmodule/xmodule/library_sourced_block.py index fa3d845cd0e1..d96741d6e447 100644 --- a/common/lib/xmodule/xmodule/library_sourced_block.py +++ b/common/lib/xmodule/xmodule/library_sourced_block.py @@ -13,7 +13,6 @@ from webob import Response from web_fragments.fragment import Fragment -from cms.lib.xblock.runtime import handler_url from xmodule.studio_editable import StudioEditableBlock as EditableChildrenMixin from xmodule.validation import StudioValidation, StudioValidationMessage @@ -78,7 +77,7 @@ def studio_view(self, context): ) fragment.content = loader.render_django_template('templates/library-sourced-block-studio-view.html', { 'react_content': react_content, - 'save_url': handler_url(self, 'submit_studio_edits'), + 'save_url': self.runtime.handler_url(self, 'submit_studio_edits'), }) fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/library_source_block.js')) diff --git a/common/lib/xmodule/xmodule/modulestore/xml_importer.py b/common/lib/xmodule/xmodule/modulestore/xml_importer.py index ba3c80275d66..07b184cf6cf1 100644 --- a/common/lib/xmodule/xmodule/modulestore/xml_importer.py +++ b/common/lib/xmodule/xmodule/modulestore/xml_importer.py @@ -29,6 +29,7 @@ from abc import abstractmethod import xblock +from django.utils.translation import ugettext as _ from lxml import etree from opaque_keys.edx.keys import UsageKey from opaque_keys.edx.locator import LibraryLocator @@ -37,7 +38,6 @@ from xblock.fields import Reference, ReferenceList, ReferenceValueDict, Scope from xblock.runtime import DictKeyValueStore, KvsFieldData -from cms.djangoapps.contentstore.exceptions import ErrorReadingFileException, ModuleFailedToImport from common.djangoapps.util.monitoring import monitor_import_failure from xmodule.assetstore import AssetMetadata from xmodule.contentstore.content import StaticContent @@ -61,6 +61,39 @@ DEFAULT_STATIC_CONTENT_SUBDIR = 'static' +class CourseImportException(Exception): + """ + Base exception class for course import workflows. + """ + + def __init__(self): + super().__init__(self.description) # pylint: disable=no-member + + +class ErrorReadingFileException(CourseImportException): + """ + Raised when error occurs while trying to read a file. + """ + + MESSAGE_TEMPLATE = _('Error while reading {}. Check file for XML errors.') + + def __init__(self, filename, **kwargs): + self.description = self.MESSAGE_TEMPLATE.format(filename) + super().__init__(**kwargs) + + +class ModuleFailedToImport(CourseImportException): + """ + Raised when a module is failed to import. + """ + + MESSAGE_TEMPLATE = _('Failed to import module: {} at location: {}') + + def __init__(self, display_name, location, **kwargs): + self.description = self.MESSAGE_TEMPLATE.format(display_name, location) + super().__init__(**kwargs) + + class LocationMixin(XBlockMixin): """ Adds a `location` property to an :class:`XBlock` so it is more compatible diff --git a/openedx/core/djangoapps/content/course_overviews/signals.py b/openedx/core/djangoapps/content/course_overviews/signals.py index 487c4e81cd1d..336724002a36 100644 --- a/openedx/core/djangoapps/content/course_overviews/signals.py +++ b/openedx/core/djangoapps/content/course_overviews/signals.py @@ -41,10 +41,6 @@ def _listen_for_course_delete(sender, course_key, **kwargs): # pylint: disable= invalidates the corresponding CourseOverview cache entry if one exists. """ CourseOverview.objects.filter(id=course_key).delete() - # import CourseAboutSearchIndexer inline due to cyclic import - from cms.djangoapps.contentstore.courseware_index import CourseAboutSearchIndexer - # Delete course entry from Course About Search_index - CourseAboutSearchIndexer.remove_deleted_items(course_key) def _check_for_course_changes(previous_course_overview, updated_course_overview):