PageRenderTime 31ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/src/main/java/com/atlassian/jira/collector/plugin/rest/TemplateResource.java

https://bitbucket.org/knecht_andreas/jira-issue-collector-plugin/
Java | 427 lines | 382 code | 37 blank | 8 comment | 35 complexity | 312bd757e69323b4f5acb4a56767a400 MD5 | raw file
  1. package com.atlassian.jira.collector.plugin.rest;
  2. import com.atlassian.crowd.embedded.api.User;
  3. import com.atlassian.jira.bc.ServiceOutcome;
  4. import com.atlassian.jira.bc.issue.IssueService;
  5. import com.atlassian.jira.bc.issue.label.LabelService;
  6. import com.atlassian.jira.collector.plugin.components.Collector;
  7. import com.atlassian.jira.config.properties.APKeys;
  8. import com.atlassian.jira.config.properties.ApplicationProperties;
  9. import com.atlassian.jira.issue.AttachmentManager;
  10. import com.atlassian.jira.issue.Issue;
  11. import com.atlassian.jira.issue.IssueInputParameters;
  12. import com.atlassian.jira.issue.IssueInputParametersImpl;
  13. import com.atlassian.jira.issue.attachment.TemporaryAttachment;
  14. import com.atlassian.jira.project.Project;
  15. import com.atlassian.jira.project.ProjectManager;
  16. import com.atlassian.jira.rest.v1.util.CacheControl;
  17. import com.atlassian.jira.security.JiraAuthenticationContext;
  18. import com.atlassian.jira.security.PermissionManager;
  19. import com.atlassian.jira.security.Permissions;
  20. import com.atlassian.jira.user.util.UserUtil;
  21. import com.atlassian.jira.util.JiraVelocityUtils;
  22. import com.atlassian.jira.util.velocity.VelocityRequestContextFactory;
  23. import com.atlassian.jira.web.action.issue.TemporaryAttachmentsMonitor;
  24. import com.atlassian.jira.web.util.AttachmentException;
  25. import com.atlassian.jira.collector.plugin.components.Template;
  26. import com.atlassian.jira.collector.plugin.components.TemplateStore;
  27. import com.atlassian.jira.collector.plugin.components.CollectorActivityHelper;
  28. import com.atlassian.jira.collector.plugin.components.CollectorService;
  29. import com.atlassian.plugin.webresource.UrlMode;
  30. import com.atlassian.plugin.webresource.WebResourceManager;
  31. import com.atlassian.plugins.rest.common.security.AnonymousAllowed;
  32. import com.atlassian.templaterenderer.TemplateRenderer;
  33. import org.apache.commons.lang.StringUtils;
  34. import org.apache.log4j.Logger;
  35. import org.ofbiz.core.entity.GenericEntityException;
  36. import java.io.IOException;
  37. import java.io.StringWriter;
  38. import java.util.Collection;
  39. import java.util.HashMap;
  40. import java.util.List;
  41. import java.util.Map;
  42. import javax.servlet.http.HttpServletRequest;
  43. import javax.ws.rs.Consumes;
  44. import javax.ws.rs.FormParam;
  45. import javax.ws.rs.GET;
  46. import javax.ws.rs.POST;
  47. import javax.ws.rs.Path;
  48. import javax.ws.rs.PathParam;
  49. import javax.ws.rs.Produces;
  50. import javax.ws.rs.QueryParam;
  51. import javax.ws.rs.core.Context;
  52. import javax.ws.rs.core.MediaType;
  53. import javax.ws.rs.core.Response;
  54. @Path ("template")
  55. @Produces ( { MediaType.APPLICATION_JSON })
  56. @Consumes ( { MediaType.APPLICATION_JSON })
  57. @AnonymousAllowed
  58. public class TemplateResource
  59. {
  60. private static final Logger log = Logger.getLogger(TemplateResource.class);
  61. private final CollectorService collectorService;
  62. private final JiraAuthenticationContext authenticationContext;
  63. private final TemplateRenderer templateRenderer;
  64. private final WebResourceManager webResourceManager;
  65. private final IssueService issueService;
  66. private final UserUtil userUtil;
  67. private final VelocityRequestContextFactory velocityRequestContextFactory;
  68. private final TemplateStore templateStore;
  69. private final PermissionManager permissionManager;
  70. private final ProjectManager projectManager;
  71. private final ApplicationProperties applicationProperties;
  72. private final LabelService labelService;
  73. private final AttachmentManager attachmentManager;
  74. @Context
  75. private HttpServletRequest request;
  76. public TemplateResource(final CollectorService collectorService, final JiraAuthenticationContext authenticationContext,
  77. final TemplateRenderer templateRenderer, final WebResourceManager webResourceManager, final IssueService issueService,
  78. final UserUtil userUtil, final VelocityRequestContextFactory velocityRequestContextFactory,
  79. final TemplateStore templateStore, final PermissionManager permissionManager, final ProjectManager projectManager,
  80. final ApplicationProperties applicationProperties, final LabelService labelService, final AttachmentManager attachmentManager)
  81. {
  82. this.collectorService = collectorService;
  83. this.authenticationContext = authenticationContext;
  84. this.templateRenderer = templateRenderer;
  85. this.webResourceManager = webResourceManager;
  86. this.issueService = issueService;
  87. this.userUtil = userUtil;
  88. this.velocityRequestContextFactory = velocityRequestContextFactory;
  89. this.templateStore = templateStore;
  90. this.permissionManager = permissionManager;
  91. this.projectManager = projectManager;
  92. this.applicationProperties = applicationProperties;
  93. this.labelService = labelService;
  94. this.attachmentManager = attachmentManager;
  95. }
  96. @GET
  97. @Path ("{templateId}")
  98. @Produces ( { MediaType.TEXT_HTML })
  99. public Response renderTemplate(@PathParam ("templateId") String templateId)
  100. {
  101. final Template template = templateStore.getTemplate(templateId);
  102. final HashMap<String, Object> context = new HashMap<String, Object>();
  103. context.put("showContactForm", true);
  104. final String out = renderTemplate(template, context);
  105. if (out == null)
  106. {
  107. return createErrorResponse("collector.plugin.collector.unknown.error");
  108. }
  109. return Response.ok(out).cacheControl(CacheControl.NO_CACHE).build();
  110. }
  111. @GET
  112. @Path ("form/{collectorId}")
  113. @Produces ( { MediaType.TEXT_HTML })
  114. public Response getForm(@PathParam ("collectorId") String collectorId, @QueryParam ("webInfo") String webInfo)
  115. {
  116. final ServiceOutcome<Collector> collector = collectorService.getCollector(collectorId);
  117. if (collector.getReturnedValue() == null)
  118. {
  119. return createErrorResponse("collector.plugin.collector.not.found");
  120. }
  121. if (!collector.getReturnedValue().isEnabled())
  122. {
  123. return createErrorResponse("collector.plugin.collector.disabled.msg");
  124. }
  125. //clear previous entries first!
  126. TemporaryAttachmentsMonitorLocator.getAttachmentsMonitor(request, collector.getReturnedValue().getId()).clearEntriesForIssue(TemporaryAttachmentsResource.UNKNOWN_ISSUE_ID);
  127. final Map<String, Object> context = new HashMap<String, Object>();
  128. context.put("collector", collector.getReturnedValue());
  129. boolean showContactForm = !useLoggedInUser(collector.getReturnedValue());
  130. context.put("showContactForm", showContactForm);
  131. if (!showContactForm)
  132. {
  133. context.put("user", authenticationContext.getLoggedInUser());
  134. }
  135. if (StringUtils.isNotBlank(webInfo))
  136. {
  137. context.put("webInfo", webInfo);
  138. }
  139. final String out = renderTemplate(collector.getReturnedValue().getTemplate(), context);
  140. if (out == null)
  141. {
  142. return createErrorResponse("collector.plugin.collector.unknown.error");
  143. }
  144. return Response.ok(out).cacheControl(CacheControl.NO_CACHE).build();
  145. }
  146. public enum Rating
  147. {
  148. AWESOME("collector.plugin.template.awesome", ":D"),
  149. GOOD("collector.plugin.template.good", ":)"),
  150. MEH("collector.plugin.template.meh", ";)"),
  151. BAD("collector.plugin.template.bad", ":("),
  152. HORRIBLE("collector.plugin.template.horrible", "(n)");
  153. private final String i18nKey;
  154. private final String smiley;
  155. Rating(String i18nKey, String smiley)
  156. {
  157. this.i18nKey = i18nKey;
  158. this.smiley = smiley;
  159. }
  160. public String getI18nKey()
  161. {
  162. return i18nKey;
  163. }
  164. public String getSmiley()
  165. {
  166. return smiley;
  167. }
  168. }
  169. @POST
  170. @Path ("feedback/{collectorId}")
  171. @Consumes ( { MediaType.APPLICATION_FORM_URLENCODED })
  172. @Produces ( { MediaType.TEXT_HTML })
  173. public Response createFeedback(@PathParam ("collectorId") String collectorId,
  174. @FormParam ("description-good") String descriptionGood,
  175. @FormParam ("description-bad") String descriptionBad,
  176. @FormParam ("rating") String rating,
  177. @FormParam ("fullname") String fullname,
  178. @FormParam ("email") String email,
  179. @FormParam ("webInfo") String webInfo,
  180. @FormParam ("filetoconvert") List<Long> screenshotIds)
  181. {
  182. final ServiceOutcome<Collector> outcome = collectorService.getCollector(collectorId);
  183. if (outcome.getReturnedValue() == null)
  184. {
  185. return createErrorResponse("collector.plugin.collector.not.found");
  186. }
  187. if (!outcome.getReturnedValue().isEnabled())
  188. {
  189. return createErrorResponse("collector.plugin.collector.disabled.msg");
  190. }
  191. //validate the rating!
  192. try
  193. {
  194. if(StringUtils.isNotBlank(rating))
  195. {
  196. Rating.valueOf(rating);
  197. }
  198. else
  199. {
  200. return createErrorResponse("collector.plugin.collector.unknown.error");
  201. }
  202. }
  203. catch (IllegalArgumentException e)
  204. {
  205. return createErrorResponse("collector.plugin.collector.unknown.error");
  206. }
  207. final Rating ratingValue = Rating.valueOf(rating);
  208. final Map<String, Object> context = new HashMap<String, Object>();
  209. context.put("ratingValue", ratingValue);
  210. context.put("descriptionGoodHtml", descriptionGood);
  211. context.put("descriptionBadHtml", descriptionBad);
  212. final String description = render("templates/feedback-wiki-markup.vm", context);
  213. return createIssue(outcome.getReturnedValue(), getSummary(descriptionGood + " " + descriptionBad), description, fullname, email, webInfo, screenshotIds);
  214. }
  215. @POST
  216. @Path ("form/{collectorId}")
  217. @Consumes ( { MediaType.APPLICATION_FORM_URLENCODED })
  218. @Produces ( { MediaType.TEXT_HTML })
  219. public Response createIssue(@PathParam ("collectorId") String collectorId,
  220. @FormParam ("description") String description,
  221. @FormParam ("fullname") String fullname,
  222. @FormParam ("email") String email,
  223. @FormParam ("webInfo") String webInfo,
  224. @FormParam ("filetoconvert") List<Long> screenshotIds)
  225. {
  226. final ServiceOutcome<Collector> outcome = collectorService.getCollector(collectorId);
  227. if (outcome.getReturnedValue() == null)
  228. {
  229. return createErrorResponse("collector.plugin.collector.not.found");
  230. }
  231. if (!outcome.getReturnedValue().isEnabled())
  232. {
  233. return createErrorResponse("collector.plugin.collector.disabled.msg");
  234. }
  235. return createIssue(outcome.getReturnedValue(), getSummary(description), description, fullname, email, webInfo, screenshotIds);
  236. }
  237. private Response createIssue(final Collector collector, final String summary, final String description, final String fullname, final String email, final String webInfo, final List<Long> screenshotIds)
  238. {
  239. final User remoteUser = authenticationContext.getLoggedInUser();
  240. final User reporter = getReporter(collector, remoteUser, fullname, email);
  241. final IssueInputParameters params = new IssueInputParametersImpl();
  242. params.setReporterId(reporter.getName());
  243. if (!applicationProperties.getOption(APKeys.JIRA_OPTION_ALLOWUNASSIGNED))
  244. {
  245. params.setAssigneeId(reporter.getName());
  246. }
  247. params.setEnvironment(webInfo);
  248. String descriptionIncludingReporter = description;
  249. if (StringUtils.isNotBlank(fullname))
  250. {
  251. descriptionIncludingReporter += "\n\n" + authenticationContext.getI18nHelper().getText("collector.plugin.template.contact.name", fullname);
  252. }
  253. if (StringUtils.isNotBlank(email))
  254. {
  255. descriptionIncludingReporter += "\n" + authenticationContext.getI18nHelper().getText("collector.plugin.template.contact.email", email);
  256. }
  257. params.setDescription(descriptionIncludingReporter);
  258. params.setSummary(summary);
  259. params.setIssueTypeId(collector.getIssueTypeId().toString());
  260. params.setProjectId(collector.getProjectId());
  261. //this is kinda dodgy but the issue service does actually use the authentication context as well as the user
  262. //that's passed in :(.
  263. authenticationContext.setLoggedInUser(reporter);
  264. try
  265. {
  266. final IssueService.CreateValidationResult validationResult = issueService.validateCreate(reporter, params);
  267. if (validationResult.isValid())
  268. {
  269. final IssueService.IssueResult result = issueService.create(reporter, validationResult);
  270. //label the issue
  271. final Issue issue = result.getIssue();
  272. final LabelService.AddLabelValidationResult labelValidationResult = labelService.validateAddLabel(reporter, issue.getId(),
  273. CollectorActivityHelper.COLLECTOR_LABEL_PREFIX + collector.getId());
  274. if (labelValidationResult.isValid())
  275. {
  276. labelService.addLabel(reporter, labelValidationResult, false);
  277. }
  278. //add attachments (if there were any!)
  279. final TemporaryAttachmentsMonitor attachmentsMonitor = TemporaryAttachmentsMonitorLocator.getAttachmentsMonitor(request, collector.getId());
  280. final Collection<TemporaryAttachment> attachments = attachmentsMonitor.getByIssueId(TemporaryAttachmentsResource.UNKNOWN_ISSUE_ID);
  281. if (!attachments.isEmpty())
  282. {
  283. try
  284. {
  285. attachmentManager.convertTemporaryAttachments(reporter, issue, screenshotIds, attachmentsMonitor);
  286. }
  287. catch (AttachmentException e)
  288. {
  289. log.error("Error attaching files", e);
  290. }
  291. catch (GenericEntityException e)
  292. {
  293. log.error("Generic Error attaching files", e);
  294. }
  295. }
  296. final Map<String, Object> context = new HashMap<String, Object>();
  297. context.put("webResourcesHtml", webResourceManager.getResourceTags("com.atlassian.jira.collector.plugin.jira-issue-collector-plugin:form-collector", UrlMode.ABSOLUTE));
  298. String feedbackLocation = velocityRequestContextFactory.getJiraVelocityRequestContext().getCanonicalBaseUrl() + "/browse/" + issue.getKey();
  299. //if the user did not submit a contact form and has permission to browse an issue show the link!
  300. boolean canBrowseIssue = permissionManager.hasPermission(Permissions.BROWSE, issue, reporter) && StringUtils.isBlank(fullname) && StringUtils.isBlank(email);
  301. if (canBrowseIssue)
  302. {
  303. context.put("thanksHtml", authenticationContext.getI18nHelper().getText("collector.plugin.template.thanks.recorded",
  304. "<a target=\"_blank\" href=\"" + feedbackLocation + "\">" + feedbackLocation + "</a>"));
  305. }
  306. else
  307. {
  308. context.put("thanksHtml", authenticationContext.getI18nHelper().getText("collector.plugin.template.thanks.no.permission"));
  309. }
  310. final String out = render("templates/collector/thanks.vm", context);
  311. if (out == null)
  312. {
  313. return createErrorResponse("collector.plugin.collector.unknown.error");
  314. }
  315. return Response.ok(out).cacheControl(CacheControl.NO_CACHE).build();
  316. }
  317. else
  318. {
  319. return createErrorResponse("collector.plugin.collector.error.creating.issue");
  320. }
  321. }
  322. finally
  323. {
  324. authenticationContext.setLoggedInUser(remoteUser);
  325. }
  326. }
  327. private User getReporter(final Collector collector, final User remoteUser, final String fullname, final String email)
  328. {
  329. User reporter = userUtil.getUserObject(collector.getReporter());
  330. //only use the loggedin user if they have permission to create and if no contact form was filled in!
  331. if (useLoggedInUser(collector) && StringUtils.isBlank(fullname) && StringUtils.isBlank(email))
  332. {
  333. reporter = remoteUser;
  334. }
  335. return reporter;
  336. }
  337. private Response createErrorResponse(final String messageKey)
  338. {
  339. final Map<String, Object> context = new HashMap<String, Object>();
  340. context.put("message", authenticationContext.getI18nHelper().getText(messageKey));
  341. context.put("webResourcesHtml", webResourceManager.getResourceTags("com.atlassian.jira.collector.plugin.jira-issue-collector-plugin:form-collector", UrlMode.ABSOLUTE));
  342. render("templates/collector/disabled.vm", context);
  343. return Response.ok(render("templates/collector/disabled.vm", context)).cacheControl(CacheControl.NO_CACHE).build();
  344. }
  345. private String renderTemplate(Template template, final Map<String, Object> context)
  346. {
  347. context.put("webResourcesHtml", webResourceManager.getResourceTags("com.atlassian.jira.collector.plugin.jira-issue-collector-plugin:form-collector", UrlMode.ABSOLUTE));
  348. context.put("canocialBaseurl", velocityRequestContextFactory.getJiraVelocityRequestContext().getCanonicalBaseUrl());
  349. return render(template.getTemplatePath(), context);
  350. }
  351. private String render(String templatePath, final Map<String, Object> context)
  352. {
  353. final StringWriter out = new StringWriter();
  354. try
  355. {
  356. context.putAll(JiraVelocityUtils.createVelocityParams(authenticationContext));
  357. templateRenderer.render(templatePath, context, out);
  358. }
  359. catch (IOException e)
  360. {
  361. log.error("Error rendering template", e);
  362. return null;
  363. }
  364. return out.toString();
  365. }
  366. private String getSummary(final String description)
  367. {
  368. final String[] split = StringUtils.split(description);
  369. final int minWords = Math.min(split.length, 12);
  370. String summary = StringUtils.join(split, " ", 0, minWords);
  371. if (minWords == 12)
  372. {
  373. summary += "...";
  374. }
  375. return summary;
  376. }
  377. private boolean useLoggedInUser(final Collector collector)
  378. {
  379. if (authenticationContext.getLoggedInUser() == null)
  380. {
  381. return false;
  382. }
  383. if (!collector.isUseCredentials())
  384. {
  385. return false;
  386. }
  387. final Project projectObj = projectManager.getProjectObj(collector.getProjectId());
  388. return permissionManager.hasPermission(Permissions.CREATE_ISSUE, projectObj, authenticationContext.getLoggedInUser());
  389. }
  390. }