Beispiel #1
0
    def delete_item(self, location, user_id, revision=None, **kwargs):
        """
        Delete the given item from persistence. kwargs allow modulestore specific parameters.

        Args:
            location: UsageKey of the item to be deleted
            user_id: id of the user deleting the item
            revision:
                None - deletes the item and its subtree, and updates the parents per description above
                ModuleStoreEnum.RevisionOption.published_only - removes only Published versions
                ModuleStoreEnum.RevisionOption.all - removes both Draft and Published parents
                    currently only provided by contentstore.views.item.orphan_handler
                Otherwise, raises a ValueError.
        """
        with self.bulk_operations(location.course_key):
            if revision == ModuleStoreEnum.RevisionOption.published_only:
                branches_to_delete = [ModuleStoreEnum.BranchName.published]
            elif revision == ModuleStoreEnum.RevisionOption.all:
                branches_to_delete = [ModuleStoreEnum.BranchName.published, ModuleStoreEnum.BranchName.draft]
            elif revision is None:
                branches_to_delete = [ModuleStoreEnum.BranchName.draft]
            else:
                raise UnsupportedRevisionError(
                    [
                        None,
                        ModuleStoreEnum.RevisionOption.published_only,
                        ModuleStoreEnum.RevisionOption.all
                    ]
                )

            for branch in branches_to_delete:
                branched_location = location.for_branch(branch)
                parent_loc = self.get_parent_location(branched_location)
                SplitMongoModuleStore.delete_item(self, branched_location, user_id)
                self._auto_publish_no_children(parent_loc, parent_loc.category, user_id, **kwargs)
Beispiel #2
0
    def _map_revision_to_branch(self, key, revision=None):
        """
        Maps RevisionOptions to BranchNames, inserting them into the key
        """
        if isinstance(key, (LibraryLocator, LibraryUsageLocator)):
            # Libraries don't yet have draft/publish support:
            draft_branch = ModuleStoreEnum.BranchName.library
            published_branch = ModuleStoreEnum.BranchName.library
        else:
            draft_branch = ModuleStoreEnum.BranchName.draft
            published_branch = ModuleStoreEnum.BranchName.published

        if revision == ModuleStoreEnum.RevisionOption.published_only:
            return key.for_branch(published_branch)
        elif revision == ModuleStoreEnum.RevisionOption.draft_only:
            return key.for_branch(draft_branch)
        elif revision is None:
            if key.branch is not None:
                return key
            elif self.get_branch_setting(
                    key) == ModuleStoreEnum.Branch.draft_preferred:
                return key.for_branch(draft_branch)
            else:
                return key.for_branch(published_branch)
        else:
            raise UnsupportedRevisionError()
Beispiel #3
0
    def has_item(self, usage_key, revision=None):
        """
        Returns True if location exists in this ModuleStore.

        Args:
            revision:
                ModuleStoreEnum.RevisionOption.published_only - checks for the published item only
                ModuleStoreEnum.RevisionOption.draft_only - checks for the draft item only
                None - uses the branch setting, as follows:
                    if branch setting is ModuleStoreEnum.Branch.published_only, checks for the published item only
                    if branch setting is ModuleStoreEnum.Branch.draft_preferred, checks whether draft or published item exists
        """
        def has_published():
            return super(DraftModuleStore, self).has_item(usage_key)

        def has_draft():
            return super(DraftModuleStore, self).has_item(as_draft(usage_key))

        if revision == ModuleStoreEnum.RevisionOption.draft_only:
            return has_draft()
        elif (revision == ModuleStoreEnum.RevisionOption.published_only
              or self.get_branch_setting()
              == ModuleStoreEnum.Branch.published_only):
            return has_published()
        elif revision is None:
            key = usage_key.to_deprecated_son(prefix='_id.')
            del key['_id.revision']
            return self.collection.find(key).count() > 0
        else:
            raise UnsupportedRevisionError()
Beispiel #4
0
    def has_item(self, usage_key, revision=None):  # lint-amnesty, pylint: disable=arguments-differ
        """
        Returns True if location exists in this ModuleStore.

        Args:
            revision:
                ModuleStoreEnum.RevisionOption.published_only - checks for the published item only
                ModuleStoreEnum.RevisionOption.draft_only - checks for the draft item only
                None - uses the branch setting, as follows:
                    if branch setting is ModuleStoreEnum.Branch.published_only, checks for the published item only
                    if branch setting is ModuleStoreEnum.Branch.draft_preferred, checks whether draft or published item exists  # lint-amnesty, pylint: disable=line-too-long
        """
        def has_published():
            return super(DraftModuleStore, self).has_item(usage_key)  # lint-amnesty, pylint: disable=super-with-arguments

        def has_draft():
            return super(DraftModuleStore, self).has_item(as_draft(usage_key))  # lint-amnesty, pylint: disable=super-with-arguments

        if revision == ModuleStoreEnum.RevisionOption.draft_only:
            return has_draft()
        elif (revision == ModuleStoreEnum.RevisionOption.published_only
              or self.get_branch_setting()
              == ModuleStoreEnum.Branch.published_only):
            return has_published()
        elif revision is None:
            key = usage_key.to_deprecated_son(prefix='_id.')
            del key['_id.revision']
            return self.collection.count_documents(key) > 0
        else:
            raise UnsupportedRevisionError()
Beispiel #5
0
    def delete_item(self, location, user_id, revision=None, skip_auto_publish=False, **kwargs):
        """
        Delete the given item from persistence. kwargs allow modulestore specific parameters.

        Args:
            location: UsageKey of the item to be deleted
            user_id: id of the user deleting the item
            revision:
                None - deletes the item and its subtree, and updates the parents per description above
                ModuleStoreEnum.RevisionOption.published_only - removes only Published versions
                ModuleStoreEnum.RevisionOption.all - removes both Draft and Published parents
                    currently only provided by contentstore.views.item.orphan_handler
                Otherwise, raises a ValueError.
        """
        with self.bulk_operations(location.course_key):
            if isinstance(location, LibraryUsageLocator):
                branches_to_delete = [ModuleStoreEnum.BranchName.library]  # Libraries don't yet have draft/publish support
            elif revision == ModuleStoreEnum.RevisionOption.published_only:
                branches_to_delete = [ModuleStoreEnum.BranchName.published]
            elif revision == ModuleStoreEnum.RevisionOption.all:
                branches_to_delete = [ModuleStoreEnum.BranchName.published, ModuleStoreEnum.BranchName.draft]
            elif revision is None:
                branches_to_delete = [ModuleStoreEnum.BranchName.draft]
                draft_location = location.for_branch(ModuleStoreEnum.BranchName.draft)
                try:
                    item = self.get_item(draft_location)
                    if getattr(item, 'has_children', False):
                        # If item have has_published_version then delete published children also.
                        if self.has_published_version(item):
                            branches_to_delete.insert(0, ModuleStoreEnum.BranchName.published)
                except ItemNotFoundError:
                    # Raises ValueError as in function description
                    raise ValueError("Cannot delete a block that does not exist")
            else:
                raise UnsupportedRevisionError(
                    [
                        None,
                        ModuleStoreEnum.RevisionOption.published_only,
                        ModuleStoreEnum.RevisionOption.all
                    ]
                )

            self._flag_publish_event(location.course_key)
            for branch in branches_to_delete:
                branched_location = location.for_branch(branch)
                parent_loc = self.get_parent_location(branched_location)
                SplitMongoModuleStore.delete_item(self, branched_location, user_id)
                # publish parent w/o child if deleted element is direct only (not based on type of parent)
                # publish vertical to behave more like the old mongo/draft modulestore - TNL-2593
                if (
                        branch == ModuleStoreEnum.BranchName.draft and
                        branched_location.block_type in (DIRECT_ONLY_CATEGORIES + ['vertical']) and
                        parent_loc and
                        not skip_auto_publish
                ):
                    # will publish if its not an orphan
                    self.publish(parent_loc.version_agnostic(), user_id, blacklist=EXCLUDE_ALL, **kwargs)
Beispiel #6
0
 def _map_revision_to_branch(self, key, revision=None):
     """
     Maps RevisionOptions to BranchNames, inserting them into the key
     """
     if revision == ModuleStoreEnum.RevisionOption.published_only:
         return key.for_branch(ModuleStoreEnum.BranchName.published)
     elif revision == ModuleStoreEnum.RevisionOption.draft_only:
         return key.for_branch(ModuleStoreEnum.BranchName.draft)
     elif revision is None:
         return key
     else:
         raise UnsupportedRevisionError()
Beispiel #7
0
    def get_items(self, course_key, revision=None, **kwargs):
        """
        Performance Note: This is generally a costly operation, but useful for wildcard searches.

        Returns:
            list of XModuleDescriptor instances for the matching items within the course with
            the given course_key

        NOTE: don't use this to look for courses as the course_key is required. Use get_courses instead.

        Args:
            course_key (CourseKey): the course identifier
            revision:
                ModuleStoreEnum.RevisionOption.published_only - returns only Published items
                ModuleStoreEnum.RevisionOption.draft_only - returns only Draft items
                None - uses the branch setting, as follows:
                    if the branch setting is ModuleStoreEnum.Branch.published_only,
                        returns only Published items
                    if the branch setting is ModuleStoreEnum.Branch.draft_preferred,
                        returns either Draft or Published, preferring Draft items.
        """
        def base_get_items(key_revision):
            return super(DraftModuleStore,
                         self).get_items(course_key,
                                         key_revision=key_revision,
                                         **kwargs)

        def draft_items():
            return [
                wrap_draft(item)
                for item in base_get_items(MongoRevisionKey.draft)
            ]

        def published_items(draft_items):
            # filters out items that are not already in draft_items
            draft_items_locations = {item.location for item in draft_items}
            return [
                item for item in base_get_items(MongoRevisionKey.published)
                if item.location not in draft_items_locations
            ]

        if revision == ModuleStoreEnum.RevisionOption.draft_only:
            return draft_items()
        elif revision == ModuleStoreEnum.RevisionOption.published_only \
                or self.get_branch_setting() == ModuleStoreEnum.Branch.published_only:
            return published_items([])
        elif revision is None:
            draft_items = draft_items()
            return draft_items + published_items(draft_items)
        else:
            raise UnsupportedRevisionError()
Beispiel #8
0
    def delete_item(self, location, user_id, revision=None, **kwargs):
        """
        Delete the given item from persistence. kwargs allow modulestore specific parameters.

        Args:
            location: UsageKey of the item to be deleted
            user_id: id of the user deleting the item
            revision:
                None - deletes the item and its subtree, and updates the parents per description above
                ModuleStoreEnum.RevisionOption.published_only - removes only Published versions
                ModuleStoreEnum.RevisionOption.all - removes both Draft and Published parents
                    currently only provided by contentstore.views.item.orphan_handler
                Otherwise, raises a ValueError.
        """
        with self.bulk_operations(location.course_key):
            if isinstance(location, LibraryUsageLocator):
                branches_to_delete = [
                    ModuleStoreEnum.BranchName.library
                ]  # Libraries don't yet have draft/publish support
            elif revision == ModuleStoreEnum.RevisionOption.published_only:
                branches_to_delete = [ModuleStoreEnum.BranchName.published]
            elif revision == ModuleStoreEnum.RevisionOption.all:
                branches_to_delete = [
                    ModuleStoreEnum.BranchName.published,
                    ModuleStoreEnum.BranchName.draft
                ]
            elif revision is None:
                branches_to_delete = [ModuleStoreEnum.BranchName.draft]
            else:
                raise UnsupportedRevisionError([
                    None, ModuleStoreEnum.RevisionOption.published_only,
                    ModuleStoreEnum.RevisionOption.all
                ])

            for branch in branches_to_delete:
                branched_location = location.for_branch(branch)
                parent_loc = self.get_parent_location(branched_location)
                SplitMongoModuleStore.delete_item(self, branched_location,
                                                  user_id)
                # publish parent w/o child if deleted element is direct only (not based on type of parent)
                if branch == ModuleStoreEnum.BranchName.draft and branched_location.block_type in DIRECT_ONLY_CATEGORIES:
                    self.publish(parent_loc.version_agnostic(),
                                 user_id,
                                 blacklist=EXCLUDE_ALL,
                                 **kwargs)

        # Remove this location from the courseware search index so that searches
        # will refrain from showing it as a result
        CoursewareSearchIndexer.add_to_search_index(self,
                                                    location,
                                                    delete=True)
Beispiel #9
0
    def delete_item(self, location, user_id, revision=None, skip_auto_publish=False, **kwargs):  # lint-amnesty, pylint: disable=arguments-differ
        """
        Delete the given item from persistence. kwargs allow modulestore specific parameters.

        Args:
            location: UsageKey of the item to be deleted
            user_id: id of the user deleting the item
            revision:
                None - deletes the item and its subtree, and updates the parents per description above
                ModuleStoreEnum.RevisionOption.published_only - removes only Published versions
                ModuleStoreEnum.RevisionOption.all - removes both Draft and Published parents
                    currently only provided by contentstore.views.item.orphan_handler
                Otherwise, raises a ValueError.
        """
        allowed_revisions = [
            None,
            ModuleStoreEnum.RevisionOption.published_only,
            ModuleStoreEnum.RevisionOption.all
        ]
        if revision not in allowed_revisions:
            raise UnsupportedRevisionError(allowed_revisions)

        autopublish_parent = False
        with self.bulk_operations(location.course_key):
            if isinstance(location, LibraryUsageLocator):
                branches_to_delete = [ModuleStoreEnum.BranchName.library]  # Libraries don't yet have draft/publish support  # lint-amnesty, pylint: disable=line-too-long
            elif location.block_type in DIRECT_ONLY_CATEGORIES:
                branches_to_delete = [ModuleStoreEnum.BranchName.published, ModuleStoreEnum.BranchName.draft]
            elif revision == ModuleStoreEnum.RevisionOption.all:
                branches_to_delete = [ModuleStoreEnum.BranchName.published, ModuleStoreEnum.BranchName.draft]
            else:
                if revision == ModuleStoreEnum.RevisionOption.published_only:
                    branches_to_delete = [ModuleStoreEnum.BranchName.published]
                elif revision is None:
                    branches_to_delete = [ModuleStoreEnum.BranchName.draft]
                    parent_loc = self.get_parent_location(location.for_branch(ModuleStoreEnum.BranchName.draft))
                    autopublish_parent = (
                        not skip_auto_publish and
                        parent_loc is not None and
                        parent_loc.block_type in DIRECT_ONLY_CATEGORIES
                    )

            self._flag_publish_event(location.course_key)
            for branch in branches_to_delete:
                branched_location = location.for_branch(branch)
                super().delete_item(branched_location, user_id)

            if autopublish_parent:
                self.publish(parent_loc.version_agnostic(), user_id, blacklist=EXCLUDE_ALL, **kwargs)
Beispiel #10
0
    def delete_item(self, location, user_id, revision=None, **kwargs):
        """
        Delete an item from this modulestore.
        The method determines which revisions to delete. It disconnects and deletes the subtree.
        In general, it assumes deletes only occur on drafts except for direct_only. The only exceptions
        are internal calls like deleting orphans (during publishing as well as from delete_orphan view).
        To indicate that all versions should be deleted, pass the keyword revision=ModuleStoreEnum.RevisionOption.all.

        * Deleting a DIRECT_ONLY_CATEGORIES block, deletes both draft and published children and removes from parent.
        * Deleting a specific version of block whose parent is of DIRECT_ONLY_CATEGORIES, only removes it from parent if
        the other version of the block does not exist. Deletes only children of same version.
        * Other deletions remove from parent of same version and subtree of same version

        Args:
            location: UsageKey of the item to be deleted
            user_id: id of the user deleting the item
            revision:
                None - deletes the item and its subtree, and updates the parents per description above
                ModuleStoreEnum.RevisionOption.published_only - removes only Published versions
                ModuleStoreEnum.RevisionOption.all - removes both Draft and Published parents
                    currently only provided by contentstore.views.item.orphan_handler
                Otherwise, raises a ValueError.
        """
        self._verify_branch_setting(ModuleStoreEnum.Branch.draft_preferred)
        _verify_revision_is_published(location)

        is_item_direct_only = location.category in DIRECT_ONLY_CATEGORIES
        if is_item_direct_only or revision == ModuleStoreEnum.RevisionOption.published_only:
            parent_revision = MongoRevisionKey.published
        elif revision == ModuleStoreEnum.RevisionOption.all:
            parent_revision = ModuleStoreEnum.RevisionOption.all
        else:
            parent_revision = MongoRevisionKey.draft

        # remove subtree from its parent
        parent_locations = self._get_raw_parent_locations(
            location, key_revision=parent_revision)
        # if no parents, then we're trying to delete something which we should convert to draft
        if not parent_locations:
            # find the published parent, convert it to draft, then manipulate the draft
            parent_locations = self._get_raw_parent_locations(
                location, key_revision=MongoRevisionKey.published)
            # parent_locations will still be empty if the object was an orphan
            if parent_locations:
                draft_parent = self.convert_to_draft(parent_locations[0],
                                                     user_id)
                parent_locations = [draft_parent.location]
        # there could be 2 parents if
        #   Case 1: the draft item moved from one parent to another
        # Case 2: revision==ModuleStoreEnum.RevisionOption.all and the single
        # parent has 2 versions: draft and published
        for parent_location in parent_locations:
            # don't remove from direct_only parent if other versions of this still exists (this code
            # assumes that there's only one parent_location in this case)
            if not is_item_direct_only and parent_location.category in DIRECT_ONLY_CATEGORIES:
                # see if other version of to-be-deleted root exists
                query = location.to_deprecated_son(prefix='_id.')
                del query['_id.revision']
                if self.collection.find(query).count() > 1:
                    continue

            parent_block = super(DraftModuleStore,
                                 self).get_item(parent_location)
            parent_block.children.remove(location)
            parent_block.location = parent_location  # ensure the location is with the correct revision
            self.update_item(parent_block, user_id, child_update=True)
        self._flag_publish_event(location.course_key)

        if is_item_direct_only or revision == ModuleStoreEnum.RevisionOption.all:
            as_functions = [as_draft, as_published]
        elif revision == ModuleStoreEnum.RevisionOption.published_only:
            as_functions = [as_published]
        elif revision is None:
            as_functions = [as_draft]
        else:
            raise UnsupportedRevisionError([
                None, ModuleStoreEnum.RevisionOption.published_only,
                ModuleStoreEnum.RevisionOption.all
            ])
        self._delete_subtree(location, as_functions)
Beispiel #11
0
    def get_item(self,
                 usage_key,
                 depth=0,
                 revision=None,
                 using_descriptor_system=None,
                 **kwargs):
        """
        Returns an XModuleDescriptor instance for the item at usage_key.

        Args:
            usage_key: A :class:`.UsageKey` instance

            depth (int): An argument that some module stores may use to prefetch
                descendants of the queried modules for more efficient results later
                in the request. The depth is counted in the number of calls to
                get_children() to cache.  None indicates to cache all descendants.

            revision:
                ModuleStoreEnum.RevisionOption.published_only - returns only the published item.
                ModuleStoreEnum.RevisionOption.draft_only - returns only the draft item.
                None - uses the branch setting as follows:
                    if branch setting is ModuleStoreEnum.Branch.published_only, returns only the published item.
                    if branch setting is ModuleStoreEnum.Branch.draft_preferred, returns either draft or published item,
                        preferring draft.

                Note: If the item is in DIRECT_ONLY_CATEGORIES, then returns only the PUBLISHED
                version regardless of the revision.

            using_descriptor_system (CachingDescriptorSystem): The existing CachingDescriptorSystem
                to add data to, and to load the XBlocks from.

        Raises:
            xmodule.modulestore.exceptions.InsufficientSpecificationError
            if any segment of the usage_key is None except revision

            xmodule.modulestore.exceptions.ItemNotFoundError if no object
            is found at that usage_key
        """
        def get_published():
            return wrap_draft(
                super(DraftModuleStore, self).get_item(
                    usage_key,
                    depth=depth,
                    using_descriptor_system=using_descriptor_system,
                    for_parent=kwargs.get('for_parent'),
                ))

        def get_draft():
            return wrap_draft(
                super(DraftModuleStore, self).get_item(
                    as_draft(usage_key),
                    depth=depth,
                    using_descriptor_system=using_descriptor_system,
                    for_parent=kwargs.get('for_parent')))

        # return the published version if ModuleStoreEnum.RevisionOption.published_only is requested
        if revision == ModuleStoreEnum.RevisionOption.published_only:
            return get_published()

        # if the item is direct-only, there can only be a published version
        elif usage_key.category in DIRECT_ONLY_CATEGORIES:
            return get_published()

        # return the draft version (without any fallback to PUBLISHED) if DRAFT-ONLY is requested
        elif revision == ModuleStoreEnum.RevisionOption.draft_only:
            return get_draft()

        elif self.get_branch_setting(
        ) == ModuleStoreEnum.Branch.published_only:
            return get_published()

        elif revision is None:
            # could use a single query wildcarding revision and sorting by revision. would need to
            # use prefix form of to_deprecated_son
            try:
                # first check for a draft version
                return get_draft()
            except ItemNotFoundError:
                # otherwise, fall back to the published version
                return get_published()

        else:
            raise UnsupportedRevisionError()