PageRenderTime 45ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/plugin/src/main/java/com/atlassian/confluence/plugin/copyspace/DefaultCopySpaceManager.java

https://bitbucket.org/atlassianlabs/confluence-copy-space-plugin
Java | 299 lines | 226 code | 40 blank | 33 comment | 31 complexity | 3d0b1ea2e8239e1427bc4dfa8aab3ce9 MD5 | raw file
  1. package com.atlassian.confluence.plugin.copyspace;
  2. import com.atlassian.bandana.BandanaManager;
  3. import com.atlassian.confluence.core.ContentPermissionManager;
  4. import com.atlassian.confluence.core.DefaultSaveContext;
  5. import com.atlassian.confluence.core.SaveContext;
  6. import com.atlassian.confluence.event.events.security.ContentPermissionEvent;
  7. import com.atlassian.confluence.pages.Comment;
  8. import com.atlassian.confluence.pages.CommentManager;
  9. import com.atlassian.confluence.pages.Page;
  10. import com.atlassian.confluence.pages.PageManager;
  11. import com.atlassian.confluence.pages.templates.PageTemplate;
  12. import com.atlassian.confluence.pages.templates.PageTemplateManager;
  13. import com.atlassian.confluence.search.ConfluenceIndexer;
  14. import com.atlassian.confluence.security.ContentPermission;
  15. import com.atlassian.confluence.security.ContentPermissionSet;
  16. import com.atlassian.confluence.security.SpacePermission;
  17. import com.atlassian.confluence.setup.bandana.ConfluenceBandanaContext;
  18. import com.atlassian.confluence.spaces.Space;
  19. import com.atlassian.confluence.spaces.SpaceManager;
  20. import com.atlassian.event.api.EventPublisher;
  21. import com.atlassian.user.User;
  22. import org.apache.log4j.Logger;
  23. import java.io.IOException;
  24. import java.util.HashMap;
  25. import java.util.List;
  26. import java.util.Map;
  27. import static com.atlassian.confluence.security.ContentPermission.createGroupPermission;
  28. import static com.atlassian.confluence.security.ContentPermission.createUserPermission;
  29. /**
  30. * Manager responsible for making a complete copy of a space.
  31. */
  32. public class DefaultCopySpaceManager implements CopySpaceManager {
  33. private static final Logger log = Logger.getLogger(DefaultCopySpaceManager.class);
  34. private static final String BANDANA_KEY_COPYING_SPACE_KEY = "copyspace.copier.spacekey";
  35. private static final SaveContext SAVE_CONTEXT_SUPPRESS_AUTOWATCH = DefaultSaveContext.builder()
  36. .suppressNotifications(true)
  37. .suppressAutowatch(true)
  38. .updateLastModifier(true)
  39. .build();
  40. private final EventPublisher eventPublisher;
  41. private final ConfluenceIndexer indexer;
  42. private final ContentPermissionManager contentPermissionManager;
  43. private final SpaceManager spaceManager;
  44. private final PageManager pageManager;
  45. private final LabelCopier labelCopier;
  46. private final PageTemplateManager pageTemplateManager;
  47. private final CommentManager commentManager;
  48. private final BandanaManager bandanaManager;
  49. private final LookAndFeelCopier lookAndFeelCopier;
  50. private final LogoCopier logoCopier;
  51. private final AttachmentCopier attachmentCopier;
  52. private final DecoratorCopier decoratorCopier;
  53. private final SidebarLinkCopier sidebarLinkCopier;
  54. public DefaultCopySpaceManager(EventPublisher eventPublisher, ConfluenceIndexer indexer,
  55. ContentPermissionManager contentPermissionManager, SpaceManager spaceManager,
  56. PageManager pageManager, DefaultLabelCopier labelCopier,
  57. PageTemplateManager pageTemplateManager, CommentManager commentManager,
  58. BandanaManager bandanaManager, LookAndFeelCopier lookAndFeelCopier,
  59. LogoCopier logoCopier, AttachmentCopier attachmentCopier,
  60. DecoratorCopier decoratorCopier,
  61. SidebarLinkCopier sidebarLinkCopier) {
  62. log.debug("CopySpaceManager.CopySpaceManager: " + System.currentTimeMillis() + " " + toString());
  63. this.eventPublisher = eventPublisher;
  64. this.indexer = indexer;
  65. this.contentPermissionManager = contentPermissionManager;
  66. this.spaceManager = spaceManager;
  67. this.pageManager = pageManager;
  68. this.labelCopier = labelCopier;
  69. this.pageTemplateManager = pageTemplateManager;
  70. this.commentManager = commentManager;
  71. this.bandanaManager = bandanaManager;
  72. this.lookAndFeelCopier = lookAndFeelCopier;
  73. this.logoCopier = logoCopier;
  74. this.attachmentCopier = attachmentCopier;
  75. this.decoratorCopier = decoratorCopier;
  76. this.sidebarLinkCopier = sidebarLinkCopier;
  77. }
  78. /* (non-Javadoc)
  79. * @see com.atlassian.confluence.plugin.copyspace.CopySpaceManager#copySpace(com.atlassian.confluence.spaces.Space,
  80. * java.lang.String, java.lang.String, com.atlassian.user.User, com.atlassian.confluence.plugin.copyspace.CopySpaceOptions)
  81. */
  82. public Space copySpace(Space originalSpace, String newKey, String newName, User user, CopySpaceOptions options) throws IOException, CopySpaceException {
  83. Space newSpace;
  84. // todo: Handle space being created after successful validation.
  85. newSpace = spaceManager.createSpace(newKey, newName, originalSpace.getDescription().getBodyAsString(), user);
  86. copySpacePermissions(originalSpace, newSpace, options.isKeepMetaData());
  87. final Page homePage = newSpace.getHomePage();
  88. if (homePage != null) // Based on CPSP-31 it sometimes can be.
  89. homePage.remove(pageManager);
  90. spaceManager.saveSpace(newSpace);
  91. if (options.isKeepMetaData())
  92. MetadataCopier.copyEntityMetadata(originalSpace, newSpace);
  93. lookAndFeelCopier.copyLookAndFeel(originalSpace, newSpace);
  94. decoratorCopier.copyDecorators(originalSpace, newSpace, options);
  95. logoCopier.copyLogo(originalSpace, newSpace, options, SAVE_CONTEXT_SUPPRESS_AUTOWATCH);
  96. labelCopier.copySpaceLabels(originalSpace, newSpace, options.isCopyPersonalLabels());
  97. copyPageTemplates(originalSpace, newSpace, options.isKeepMetaData(), options.isCopyPersonalLabels());
  98. List<Page> oldPages = pageManager.getPages(originalSpace, true);
  99. final SpaceCopyContext spaceCopyContext = new SpaceCopyContext();
  100. for (Page page : oldPages) {
  101. // copy the parentless pages and their children.
  102. // can't iterate straight through all pages as children must be added after their parent.
  103. if (page.getParent() == null)
  104. copyPagesRecursive(page, null, newSpace, options, spaceCopyContext);
  105. }
  106. sidebarLinkCopier.copySidebarLinks(originalSpace, newSpace, spaceCopyContext);
  107. for (SpaceCopyContext.ContentPair<Page> pair : spaceCopyContext.getCopiedPages()) {
  108. // Need to get this out as soon as possible before we mess with the id and therefore hashcode and therefore
  109. // break the map.
  110. Page original = pair.original;
  111. Page copy = pair.copy;
  112. rebuildAncestors(copy);
  113. eventPublisher.publish(new ContentPermissionEvent(this, copy, null));
  114. indexer.reIndex(copy);
  115. pageManager.saveContentEntity(copy, SAVE_CONTEXT_SUPPRESS_AUTOWATCH);
  116. if (options.isKeepMetaData())
  117. MetadataCopier.copyEntityMetadata(original, copy);
  118. }
  119. spaceManager.saveSpace(newSpace);
  120. for (SpaceCopyContext.ContentPair<Page> pair : spaceCopyContext.getCopiedPages()) {
  121. if (options.isKeepMetaData())
  122. MetadataCopier.copyEntityMetadata(pair.original, pair.copy);
  123. labelCopier.copyLabels(pair.original, pair.copy, options.isCopyPersonalLabels());
  124. }
  125. recordCopyingSpaceAgainstCopiedSpace(newSpace, originalSpace);
  126. return newSpace;
  127. }
  128. /**
  129. * Record in Bandana that <code>newSpace</code> was generated from <code>originalSpace</code>. Note that only
  130. * one space can be recorded as being the generator of a space, although many spaces can be generated by the
  131. * same space.
  132. *
  133. * @param newSpace the space to be marked as having been generated by a copy.
  134. * @param originalSpace the generating space to which the record points.
  135. */
  136. private void recordCopyingSpaceAgainstCopiedSpace(Space newSpace, Space originalSpace) {
  137. ConfluenceBandanaContext bandanaContext = new ConfluenceBandanaContext(newSpace);
  138. bandanaManager.setValue(bandanaContext, BANDANA_KEY_COPYING_SPACE_KEY, originalSpace.getKey());
  139. }
  140. /**
  141. * Helper method that retrieves the key of the space from which this one was created by copying.
  142. * If this space wasn't created by copying another space it returns null. I think it had to be
  143. * static because this manager isn't instantiated until after the {@link SpaceIsCopiedCondition} is autowired.
  144. *
  145. * @param bandanaManager from which to look up the copying record.
  146. * @param space the space that may have been copied
  147. * @return the key of the space from which the given space was copied, or null if not applicable.
  148. */
  149. public static String getCopierSpaceKey(BandanaManager bandanaManager, Space space) {
  150. ConfluenceBandanaContext bandanaContext = new ConfluenceBandanaContext(space);
  151. return (String) bandanaManager.getValue(bandanaContext, BANDANA_KEY_COPYING_SPACE_KEY);
  152. }
  153. private void copySpacePermissions(Space originalSpace, Space newSpace, boolean keepMetaData) {
  154. List<SpacePermission> originalPermissions = originalSpace.getPermissions();
  155. for (SpacePermission originalPermission : originalPermissions) {
  156. SpacePermission copiedPermission = new SpacePermission(originalPermission);
  157. copiedPermission.setSpace(newSpace);
  158. newSpace.addPermission(copiedPermission);
  159. if (keepMetaData)
  160. MetadataCopier.copyEntityMetadata(originalPermission, copiedPermission);
  161. }
  162. }
  163. private void copyPagesRecursive(Page original, Page newParent, Space newSpace, CopySpaceOptions options,
  164. SpaceCopyContext spaceCopyContext) throws CopySpaceException {
  165. Page copy;
  166. copy = copyPage(original, newSpace);
  167. spaceCopyContext.addPagePair(new SpaceCopyContext.ContentPair<>(original, copy));
  168. copy.setSpace(newSpace);
  169. if (original.equals(original.getSpace().getHomePage()))
  170. newSpace.setHomePage(copy);
  171. if (newParent != null)
  172. newParent.addChild(copy);
  173. if (options.isCopyAttachments())
  174. attachmentCopier.copyAttachments(original, copy, options, SAVE_CONTEXT_SUPPRESS_AUTOWATCH, spaceCopyContext);
  175. if (options.isCopyComments())
  176. copyPageComments(original, copy);
  177. // now deal with children and recurse
  178. // Grab all child pages, and copy them. User is space admin so restrictions are irrelevant.
  179. List<Page> children = original.getChildren();
  180. for (Page child : children) {
  181. copyPagesRecursive(child, copy, newSpace, options, spaceCopyContext);
  182. }
  183. }
  184. private void copyPageComments(Page from, Page to) {
  185. List<Comment> originalComments = from.getComments();
  186. Map<Long, Comment> oldIdToCopiedComment = new HashMap<>();
  187. for (Comment oldComment : originalComments) {
  188. Comment newParent = null;
  189. if (oldComment.getParent() != null) {
  190. Long oldId = oldComment.getParent().getId();
  191. newParent = oldIdToCopiedComment.get(oldId);
  192. if (newParent != null) {
  193. log.warn("Comments are out of creation date order. Old parent id = " + oldId +
  194. ", old child id = " + oldComment.getId());
  195. }
  196. }
  197. Comment newComment = commentManager.addCommentToObject(to, newParent, oldComment.getBodyContent().getBody());
  198. // Note that we ignore the keep meta-data option for comments.
  199. MetadataCopier.copyEntityMetadata(oldComment, newComment);
  200. // I'm hoping that will get magically committed when they go over the transaction boundary.
  201. oldIdToCopiedComment.put(oldComment.getId(), newComment);
  202. }
  203. }
  204. private Page copyPage(Page original, Space newSpace) {
  205. Page copy;
  206. copy = new Page();
  207. copy.setTitle(original.getTitle());
  208. copy.setBodyContent(original.getBodyContent());
  209. copy.setPosition(original.getPosition());
  210. copy.setSpace(newSpace);
  211. pageManager.saveContentEntity(copy, SAVE_CONTEXT_SUPPRESS_AUTOWATCH);
  212. copyContentPermissionSets(original, copy);
  213. return copy;
  214. }
  215. public void copyContentPermissionSets(Page from, Page to) {
  216. ContentPermissionSet permissionSet = from.getContentPermissionSet(ContentPermission.VIEW_PERMISSION);
  217. copyContentPermissionSet(permissionSet, to);
  218. permissionSet = from.getContentPermissionSet(ContentPermission.EDIT_PERMISSION);
  219. copyContentPermissionSet(permissionSet, to);
  220. }
  221. private void copyContentPermissionSet(ContentPermissionSet permissionSet, Page copy) {
  222. if (permissionSet == null)
  223. return;
  224. for (ContentPermission originalPermission : permissionSet) {
  225. ContentPermission newPermission;
  226. if (originalPermission.isUserPermission())
  227. newPermission = createUserPermission(originalPermission.getType(), originalPermission.getUserSubject());
  228. else
  229. newPermission = createGroupPermission(originalPermission.getType(), originalPermission.getGroupName());
  230. contentPermissionManager.addContentPermission(newPermission, copy);
  231. }
  232. }
  233. private void copyPageTemplates(Space originalSpace, Space newSpace, boolean keepMetaData, boolean copyPersonalLabels) {
  234. List<PageTemplate> pageTemplates = originalSpace.getPageTemplates();
  235. for (PageTemplate pageTemplate : pageTemplates) {
  236. PageTemplate copy = copyPageTemplate(pageTemplate);
  237. newSpace.addPageTemplate(copy);
  238. pageTemplateManager.savePageTemplate(copy, null);
  239. labelCopier.copyLabels(pageTemplate, copy, copyPersonalLabels);
  240. if (keepMetaData)
  241. MetadataCopier.copyEntityMetadata(pageTemplate, copy);
  242. }
  243. }
  244. private PageTemplate copyPageTemplate(PageTemplate originalPageTemplate) {
  245. PageTemplate template = new PageTemplate();
  246. template.setName(originalPageTemplate.getName());
  247. template.setDescription(originalPageTemplate.getDescription());
  248. template.setContent(originalPageTemplate.getContent());
  249. template.setBodyType(originalPageTemplate.getBodyType());
  250. return template;
  251. }
  252. private void rebuildAncestors(Page page) {
  253. page.getAncestors().clear();
  254. if (page.getParent() != null) {
  255. page.getAncestors().addAll(page.getParent().getAncestors());
  256. page.getAncestors().add(page.getParent());
  257. }
  258. }
  259. }