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'])
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))