PageRenderTime 50ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/src/main/java/com/verhas/jira/plugin/merger/MergeIssuesFunction.java

https://bitbucket.org/verhasi/issue-merger-for-jira
Java | 271 lines | 236 code | 29 blank | 6 comment | 41 complexity | 5e05203e1b553491126d069436570830 MD5 | raw file
Possible License(s): LGPL-3.0
  1. package com.verhas.jira.plugin.merger;
  2. import com.atlassian.extras.common.log.Logger;
  3. import com.atlassian.jira.bc.JiraServiceContext;
  4. import com.atlassian.jira.bc.JiraServiceContextImpl;
  5. import com.atlassian.jira.bc.issue.IssueService;
  6. import com.atlassian.jira.bc.issue.attachment.AttachmentService;
  7. import com.atlassian.jira.bc.issue.comment.CommentService;
  8. import com.atlassian.jira.bc.issue.visibility.Visibilities;
  9. import com.atlassian.jira.bc.user.UserService;
  10. import com.atlassian.jira.exception.CreateException;
  11. import com.atlassian.jira.exception.RemoveException;
  12. import com.atlassian.jira.issue.*;
  13. import com.atlassian.jira.issue.attachment.Attachment;
  14. import com.atlassian.jira.issue.comments.Comment;
  15. import com.atlassian.jira.issue.comments.MutableComment;
  16. import com.atlassian.jira.issue.fields.CustomField;
  17. import com.atlassian.jira.issue.fields.FieldManager;
  18. import com.atlassian.jira.issue.link.IssueLink;
  19. import com.atlassian.jira.issue.link.IssueLinkManager;
  20. import com.atlassian.jira.security.JiraAuthenticationContext;
  21. import com.atlassian.jira.user.ApplicationUser;
  22. import com.atlassian.jira.user.util.UserManager;
  23. import com.atlassian.jira.util.ErrorCollection;
  24. import com.atlassian.jira.util.SimpleErrorCollection;
  25. import com.atlassian.jira.web.util.AttachmentException;
  26. import com.atlassian.jira.workflow.function.issue.AbstractJiraFunctionProvider;
  27. import com.opensymphony.module.propertyset.PropertySet;
  28. import com.opensymphony.workflow.WorkflowException;
  29. import java.util.*;
  30. import org.ofbiz.core.entity.GenericEntityException;
  31. import java.io.IOException;
  32. import java.util.List;
  33. /**
  34. * Created by IntelliJ IDEA. User: verhasi Date: 12/29/11 Time: 7:12 PM To
  35. * change this template use File | Settings | File Templates.
  36. */
  37. public class MergeIssuesFunction extends AbstractJiraFunctionProvider {
  38. private static final Logger.Log log = Logger.getInstance(MergeIssuesFunction.class);
  39. private static final boolean DONTDISPATCHEVENT = false;
  40. private static final String PREFIX = "JIRAMERGE";
  41. private static final String POSTFIX = ".attachment";
  42. public static final String MERGE_INTO_ISSUE = "Merge into issue";
  43. private final UserManager userManager;
  44. private final IssueService issueService;
  45. private final CustomFieldManager customFieldManager;
  46. private final FieldManager fieldManager;
  47. private final AttachmentManager attachmentManager;
  48. private final AttachmentService attachmentService;
  49. private final CommentService commentService;
  50. private final IssueLinkManager issueLinkManager;
  51. private final ApplicationUser loggedInUser;
  52. public MergeIssuesFunction(JiraAuthenticationContext authContext,
  53. IssueService issueService,
  54. CustomFieldManager customFieldManager,
  55. FieldManager fieldManager,
  56. AttachmentManager attachmentManager,
  57. CommentService commentService,
  58. AttachmentService attachmentService,
  59. UserService userService,
  60. UserManager userManager,
  61. IssueLinkManager issueLinkManager) {
  62. this.issueService = issueService;
  63. this.customFieldManager = customFieldManager;
  64. this.fieldManager = fieldManager;
  65. this.attachmentManager = attachmentManager;
  66. this.commentService = commentService;
  67. this.attachmentService = attachmentService;
  68. this.userManager = userManager;
  69. this.loggedInUser = authContext.getLoggedInUser();
  70. this.issueLinkManager = issueLinkManager;
  71. }
  72. //args delete or not;customfield name;multiple target merge enabled
  73. @Override
  74. public void execute(Map transientVars, Map args, PropertySet ps) throws WorkflowException {
  75. log.debug("start of com.verhas.jira.plugin.merger.MergeIssuesFunction.execute");
  76. MutableIssue sourceIssue = getIssue(transientVars);
  77. //TODO make customfield's name parameter from transient configuration
  78. Collection<CustomField> customFields = customFieldManager.getCustomFieldObjectsByName(MERGE_INTO_ISSUE);
  79. if (null == customFields || customFields.isEmpty()) {
  80. throw new WorkflowException("Custom field 'Merge into issue' not found");
  81. }
  82. CustomField customField = customFields.iterator().next();
  83. if (null == sourceIssue.getCustomFieldValue(customField)) {
  84. throw new WorkflowException("Merge into issue is mandatory!");
  85. }
  86. Object mergeInto = sourceIssue.getCustomFieldValue(customField);
  87. if (mergeInto instanceof List) {
  88. List targetIssues = (List) mergeInto;
  89. if (targetIssues.isEmpty()) {
  90. throw new WorkflowException("Merge into issue is mandatory!");
  91. }
  92. mergeInto = targetIssues.get(targetIssues.size() - 1);
  93. }
  94. MutableIssue targetIssue = issueService.getIssue(loggedInUser, mergeInto.toString()).getIssue();
  95. if (!sourceIssue.isEditable() || !targetIssue.isEditable()) {
  96. throw new WorkflowException("Both the source and the target issues must be editable!");
  97. }
  98. try {
  99. mergeAttachments(sourceIssue, targetIssue);
  100. mergeComments(sourceIssue, targetIssue);
  101. mergeDescription(sourceIssue, targetIssue);
  102. mergeLinks(sourceIssue, targetIssue);
  103. log.debug("normal finish of com.verhas.jira.plugin.merger.MergeIssuesFunction.execute");
  104. } catch (AttachmentException e) {
  105. throw new WorkflowException(e);
  106. } catch (IOException e) {
  107. throw new WorkflowException(e);
  108. } catch (GenericEntityException e) {
  109. throw new WorkflowException(e);
  110. } catch (RemoveException e) {
  111. throw new WorkflowException(e);
  112. } finally {
  113. log.debug("finish of com.verhas.jira.plugin.merger.MergeIssuesFunction.execute");
  114. }
  115. }
  116. private void mergeComments(MutableIssue sourceIssue, MutableIssue targetIssue) throws WorkflowException {
  117. ErrorCollection errorCollection = new SimpleErrorCollection();
  118. for (Comment comment : commentService.getCommentsForUser(null, sourceIssue)) {
  119. Comment newComment = commentService.create(
  120. comment.getAuthorApplicationUser(),
  121. commentService.validateCommentCreate(comment.getAuthorApplicationUser(),
  122. new CommentService.CommentParameters.CommentParametersBuilder()
  123. .issue(targetIssue)
  124. .body(comment.getBody())
  125. .visibility(Visibilities.fromGroupAndRoleId(comment.getGroupLevel(), comment.getRoleLevelId()))
  126. .created(comment.getCreated())
  127. .build()),
  128. DONTDISPATCHEVENT);
  129. MutableComment mutableComment = commentService.getMutableComment(newComment.getAuthorApplicationUser(), newComment.getId(),errorCollection);
  130. if (!errorCollection.hasAnyErrors()) {
  131. mutableComment.setUpdated(comment.getUpdated());
  132. mutableComment.setUpdateAuthor(comment.getUpdateAuthorApplicationUser());
  133. ApplicationUser commentAuthor = (comment.getAuthorApplicationUser());
  134. JiraServiceContext jiraServiceContext = null;
  135. for (ApplicationUser user : Arrays.asList(commentAuthor, loggedInUser)) {
  136. if (commentService.hasPermissionToDelete(new JiraServiceContextImpl(user), comment.getId())) {
  137. jiraServiceContext = new JiraServiceContextImpl(user, errorCollection);
  138. break;
  139. }
  140. }
  141. if (null != jiraServiceContext) {
  142. commentService.delete(jiraServiceContext, comment, false);
  143. for (String errorMessage : errorCollection.getErrorMessages()) {
  144. log.error("Tried to delete comment but " + errorMessage);
  145. }
  146. } else {
  147. log.debug("Neither the comment author (" + commentAuthor + ") nor the logged in user (" + loggedInUser + ") has permission to delete the comment.");
  148. }
  149. }
  150. }
  151. if (errorCollection.hasAnyErrors()) {
  152. throw new WorkflowException(errorCollection.toString());
  153. }
  154. }
  155. private void mergeAttachments(MutableIssue sourceIssue, MutableIssue targetIssue) throws AttachmentException, IOException, GenericEntityException, RemoveException {
  156. if (attachmentManager.attachmentsEnabled()) {
  157. Map<Long,com.atlassian.fugue.Either<AttachmentError,Attachment>> copyResult = attachmentManager.copyAttachments(sourceIssue,null,targetIssue.getKey());
  158. for (Attachment attachment : sourceIssue.getAttachments()) {
  159. if(copyResult.get(attachment.getId())!=null) {
  160. if (copyResult.get(attachment.getId()).isRight()) {
  161. deleteAttachment(attachment);
  162. } else {
  163. log.error(copyResult.get(attachment.getId()).left().get().getLogMessage());
  164. }
  165. } else {
  166. log.error("Attachment was not copied!? id="+attachment.getId());
  167. }
  168. }
  169. }
  170. }
  171. private void deleteAttachment(Attachment attachment) throws RemoveException {
  172. ErrorCollection errorCollection = new SimpleErrorCollection();
  173. ApplicationUser attachmentAuthor = attachment.getAuthorObject();
  174. JiraServiceContext jiraServiceContext = null;
  175. for (ApplicationUser user : Arrays.asList(attachmentAuthor, loggedInUser)) {
  176. if (attachmentService.canDeleteAttachment(new JiraServiceContextImpl(user), attachment.getId())) {
  177. jiraServiceContext = new JiraServiceContextImpl(user, errorCollection);
  178. break;
  179. }
  180. }
  181. if (null != jiraServiceContext) {
  182. attachmentService.delete(jiraServiceContext, attachment.getId());
  183. for (String errorMessage : errorCollection.getErrorMessages()) {
  184. log.error("Tried to delete attachment but " + errorMessage);
  185. }
  186. } else {
  187. log.debug("Neither the attachment author (" + attachmentAuthor + ") nor the logged in user (" + loggedInUser + ") has permission to delete the attachment.");
  188. }
  189. }
  190. private void mergeDescription(MutableIssue sourceIssue, MutableIssue targetIssue) {
  191. IssueInputParameters targetIssueInputParameters = issueService.newIssueInputParameters();
  192. String targetDescriptionExtension = "{panel:borderStyle=solid|bgColor=#FFFFCE}Merged from " + sourceIssue.getKey()
  193. + " - " + sourceIssue.getSummary() + "{panel}" + sourceIssue.getDescription();
  194. targetIssueInputParameters.setDescription(targetIssue.getDescription() + "\n" + targetDescriptionExtension);
  195. IssueService.UpdateValidationResult updateValidationResult = issueService.validateUpdate(loggedInUser, targetIssue.getId(), targetIssueInputParameters);
  196. IssueService.IssueResult updateResult = null;
  197. if (updateValidationResult.isValid()) {
  198. updateResult = issueService.update(loggedInUser, updateValidationResult);
  199. if (updateResult != null && updateResult.isValid()) {
  200. sourceIssue.setDescription("{panel:borderStyle=solid|bgColor=#FFFFCE}h1. Merged to " + targetIssue.getKey() + "{panel}");
  201. }
  202. } else {
  203. StringBuilder errors = new StringBuilder();
  204. for (String error : updateValidationResult.getErrorCollection().getErrorMessages()) {
  205. errors.append("#").append(error);
  206. }
  207. log.debug("failed target issue update as " + errors);
  208. }
  209. }
  210. private void mergeLinks(MutableIssue sourceIssue, MutableIssue targetIssue) {
  211. if (issueLinkManager.isLinkingEnabled()) {
  212. for (IssueLink inwardLink : issueLinkManager.getInwardLinks(sourceIssue.getId())) {
  213. try {
  214. if (!inwardLink.getSourceObject().equals(sourceIssue)) {
  215. cloneLink(inwardLink, inwardLink.getSourceObject(), targetIssue);
  216. issueLinkManager.removeIssueLink(inwardLink, loggedInUser);
  217. }
  218. } catch (CreateException ex) {
  219. log.error("Unable to create new issueLink", ex);
  220. }
  221. }
  222. for (IssueLink outwardLink : issueLinkManager.getOutwardLinks(sourceIssue.getId())) {
  223. try {
  224. if (!outwardLink.getDestinationObject().equals(targetIssue)) {
  225. cloneLink(outwardLink, targetIssue, outwardLink.getDestinationObject());
  226. issueLinkManager.removeIssueLink(outwardLink, loggedInUser);
  227. }
  228. } catch (CreateException ex) {
  229. log.error("Unable to create new issueLink", ex);
  230. }
  231. }
  232. }
  233. }
  234. private void cloneLink(IssueLink oldLink, Issue newSourceIssue, Issue newTargetIssue) throws CreateException {
  235. issueLinkManager.createIssueLink(
  236. newSourceIssue.getId(),
  237. newTargetIssue.getId(),
  238. oldLink.getLinkTypeId(),
  239. oldLink.getSequence(),
  240. loggedInUser);
  241. }
  242. }