예제 #1
0
    def revert_to_published(self, location, user_id):
        """
        Reverts an item to its last published version (recursively traversing all of its descendants).
        If no published version exists, a VersionConflictError is thrown.

        If a published version exists but there is no draft version of this item or any of its descendants, this
        method is a no-op.

        :raises InvalidVersionError: if no published version exists for the location specified
        """
        if location.category in DIRECT_ONLY_CATEGORIES:
            return

        draft_course_key = location.course_key.for_branch(
            ModuleStoreEnum.BranchName.draft)
        with self.bulk_operations(draft_course_key):

            # get head version of Published branch
            published_course_structure = self._lookup_course(
                location.course_key.for_branch(
                    ModuleStoreEnum.BranchName.published)).structure
            published_block = self._get_block_from_structure(
                published_course_structure, BlockKey.from_usage_key(location))
            if published_block is None:
                raise InvalidVersionError(location)

            # create a new versioned draft structure
            draft_course_structure = self._lookup_course(
                draft_course_key).structure
            new_structure = self.version_structure(draft_course_key,
                                                   draft_course_structure,
                                                   user_id)

            # remove the block and its descendants from the new structure
            self._remove_subtree(BlockKey.from_usage_key(location),
                                 new_structure['blocks'])

            # copy over the block and its descendants from the published branch
            def copy_from_published(root_block_id):
                """
                copies root_block_id and its descendants from published_course_structure to new_structure
                """
                self._update_block_in_structure(
                    new_structure, root_block_id,
                    self._get_block_from_structure(published_course_structure,
                                                   root_block_id))
                block = self._get_block_from_structure(new_structure,
                                                       root_block_id)
                for child_block_id in block.fields.get('children', []):
                    copy_from_published(child_block_id)

            copy_from_published(BlockKey.from_usage_key(location))

            # update course structure and index
            self.update_structure(draft_course_key, new_structure)
            index_entry = self._get_index_if_valid(draft_course_key)
            if index_entry is not None:
                self._update_head(draft_course_key, index_entry,
                                  ModuleStoreEnum.BranchName.draft,
                                  new_structure['_id'])
예제 #2
0
    def has_changes(self, xblock):
        """
        Checks if the given block has unpublished changes
        :param xblock: the block to check
        :return: True if the draft and published versions differ
        """
        def get_course(branch_name):
            return self._lookup_course(
                xblock.location.course_key.for_branch(branch_name)).structure

        def get_block(course_structure, block_key):
            return self._get_block_from_structure(course_structure, block_key)

        draft_course = get_course(ModuleStoreEnum.BranchName.draft)
        published_course = get_course(ModuleStoreEnum.BranchName.published)

        def has_changes_subtree(block_key):
            draft_block = get_block(draft_course, block_key)
            if draft_block is None:  # temporary fix for bad pointers TNL-1141
                return True
            published_block = get_block(published_course, block_key)
            if published_block is None:
                return True

            # check if the draft has changed since the published was created
            if self._get_version(draft_block) != self._get_version(
                    published_block):
                return True

            # check the children in the draft
            if 'children' in draft_block.fields:
                return any([
                    has_changes_subtree(child_block_id)
                    for child_block_id in draft_block.fields['children']
                ])

            return False

        return has_changes_subtree(BlockKey.from_usage_key(xblock.location))