1
2
3
4 package com.atlassian.jira.jelly.tag.issue;
5
6 import java.util.Collection;
7 import java.util.HashMap;
8 import java.util.HashSet;
9 import java.util.Iterator;
10 import java.util.List;
11 import java.util.Map;
12 import java.util.Set;
13
14 import org.apache.commons.jelly.JellyTagException;
15 import org.apache.commons.jelly.MissingAttributeException;
16 import org.apache.commons.jelly.XMLOutput;
17 import org.ofbiz.core.entity.GenericEntityException;
18 import org.ofbiz.core.entity.GenericValue;
19
20 import com.atlassian.core.user.UserUtils;
21 import com.atlassian.jira.issue.Issue;
22 import com.atlassian.jira.issue.IssueFactory;
23 import com.atlassian.jira.issue.IssueManager;
24 import com.atlassian.jira.issue.MutableIssue;
25 import com.atlassian.jira.issue.customfields.impl.FieldValidationException;
26 import com.atlassian.jira.issue.fields.CustomField;
27 import com.atlassian.jira.issue.fields.FieldManager;
28 import com.atlassian.jira.issue.fields.OrderableField;
29 import com.atlassian.jira.issue.fields.screen.FieldScreen;
30 import com.atlassian.jira.jelly.JiraDynaBeanTagSupport;
31 import com.atlassian.jira.jelly.tag.JellyTagConstants;
32 import com.atlassian.jira.jelly.tag.JellyUtils;
33 import com.atlassian.jira.security.JiraAuthenticationContext;
34 import com.atlassian.jira.security.JiraAuthenticationContextImpl;
35 import com.atlassian.jira.util.EasyList;
36 import com.atlassian.jira.util.JiraUtils;
37 import com.atlassian.jira.workflow.JiraWorkflow;
38 import com.atlassian.jira.workflow.WorkflowActionsBean;
39 import com.atlassian.jira.workflow.WorkflowException;
40 import com.atlassian.jira.workflow.WorkflowManager;
41 import com.atlassian.jira.workflow.WorkflowTransitionUtil;
42 import com.atlassian.jira.workflow.WorkflowTransitionUtilAddOnImpl;
43 import com.opensymphony.user.EntityNotFoundException;
44 import com.opensymphony.user.User;
45 import com.opensymphony.util.TextUtils;
46 import com.opensymphony.workflow.loader.ActionDescriptor;
47
48 public class TransitionWorkflowExtended extends JiraDynaBeanTagSupport implements CustomFieldValuesAwareTag
49 {
50
51 private static final String KEY_USER = "user";
52 private static final String KEY_ISSUE_KEY = "key";
53 private static final String KEY_WORKFLOW_ACTION = "workflowAction";
54
55 private static final String KEY_COMMENT = WorkflowTransitionUtil.FIELD_COMMENT;
56 private static final String KEY_COMMENT_LEVEL = WorkflowTransitionUtil.FIELD_COMMENT_LEVEL;
57
58 public static final Set attributes = new HashSet();
59
60 static
61 {
62 attributes.add(KEY_USER);
63 attributes.add(KEY_ISSUE_KEY);
64 attributes.add(KEY_WORKFLOW_ACTION);
65 attributes.add(KEY_COMMENT);
66 attributes.add(KEY_COMMENT_LEVEL);
67 }
68
69 private final JiraAuthenticationContext authenticationContext;
70 private final IssueManager issueManager;
71 private final WorkflowManager workflowManager;
72 private final FieldManager fieldManager;
73 private final IssueFactory issueFactory;
74
75 public TransitionWorkflowExtended(JiraAuthenticationContext authenticationContext, IssueManager issueManager, WorkflowManager workflowManager, FieldManager fieldManager, IssueFactory issueFactory)
76 {
77 this.authenticationContext = authenticationContext;
78 this.issueManager = issueManager;
79 this.workflowManager = workflowManager;
80 this.fieldManager = fieldManager;
81 this.issueFactory = issueFactory;
82 }
83
84 public void doTag(XMLOutput xmlOutput) throws MissingAttributeException, JellyTagException
85 {
86 if (getBody() != null)
87 {
88 getBody().run(getContext(), xmlOutput);
89 }
90
91 validateAttributes();
92
93 User user = getUser();
94
95
96 MutableIssue issue = getIssue();
97
98 int actionId = getActionId(issue).getId();
99
100 Map params = getTransitionParameters(issue);
101
102
103 User previousUser = authenticationContext.getUser();
104
105 try
106 {
107
108 authenticationContext.setUser(user);
109 JiraAuthenticationContextImpl.clearRequestCache();
110
111
112
113 WorkflowTransitionUtil workflowTransitionUtil = (WorkflowTransitionUtil) JiraUtils.loadComponent(WorkflowTransitionUtilAddOnImpl.class);
114 workflowTransitionUtil.setIssue(issue);
115 workflowTransitionUtil.setAction(actionId);
116 workflowTransitionUtil.setParams(params);
117
118
119 JellyUtils.processErrorCollection(workflowTransitionUtil.validate());
120
121
122 JellyUtils.processErrorCollection(workflowTransitionUtil.progress());
123 }
124 finally
125 {
126
127 authenticationContext.setUser(previousUser);
128 JiraAuthenticationContextImpl.clearRequestCache();
129 }
130
131 }
132
133 private ActionDescriptor getActionId(Issue issue) throws JellyTagException
134 {
135 try
136 {
137 JiraWorkflow workflow = workflowManager.getWorkflow(issue.getGenericValue());
138
139 try
140 {
141 int actionId = Integer.parseInt(getWorkflowActionProperty());
142 ActionDescriptor action = workflow.getDescriptor().getAction(actionId);
143 if (action == null)
144 throw new JellyTagException("Invalid action id '" + getWorkflowActionProperty() + "'.");
145
146 return action;
147 }
148 catch (NumberFormatException e)
149 {
150 return getActionIdByName(workflow);
151 }
152 }
153 catch (WorkflowException e)
154 {
155 throw new JellyTagException("Error occurred while retrieving workflow for issue with key '" + getKey() + "'", e);
156 }
157 }
158
159 private MutableIssue getIssue() throws JellyTagException
160 {
161 try
162 {
163 GenericValue issue = issueManager.getIssue(getKey());
164 if (issue == null)
165 {
166 throw new JellyTagException("Cannot retrieve issue with key '" + getKey() + "'");
167 }
168
169 return getIssueObject(issue);
170 }
171 catch (GenericEntityException e)
172 {
173 throw new JellyTagException("Error occurred while retrieving issue with key '" + getKey() + "'", e);
174 }
175 }
176
177 public MutableIssue getIssueObject(GenericValue issueGV)
178 {
179 return issueFactory.getIssue(issueGV);
180 }
181
182 private Map getTransitionParameters(Issue issue) throws JellyTagException
183 {
184 Map params = new HashMap();
185
186 if (paramSpecified(KEY_COMMENT))
187 {
188 params.put(WorkflowTransitionUtil.FIELD_COMMENT, getComment());
189
190 if (paramSpecified(KEY_COMMENT_LEVEL))
191 {
192 params.put(WorkflowTransitionUtil.FIELD_COMMENT_LEVEL, getCommentLevel());
193 }
194 }
195
196 for (Iterator iterator = getProperties().keySet().iterator(); iterator.hasNext();)
197 {
198 String attributeName = (String) iterator.next();
199
200 if (!attributes.contains(attributeName))
201 {
202
203 if (fieldManager.isOrderableField(attributeName))
204 {
205
206 OrderableField orderableField = fieldManager.getOrderableField(attributeName);
207
208 if (fieldManager.isCustomField(orderableField))
209 {
210 Map actionParams = new HashMap();
211 for (Iterator iterator1 = getProperties().keySet().iterator(); iterator1.hasNext();)
212 {
213 String key = (String) iterator1.next();
214 if (key.startsWith(orderableField.getId()))
215 {
216 List values = (List) getProperties().get(key);
217 actionParams.put(key, values.toArray(new String[values.size()]));
218 }
219 }
220
221 orderableField.populateFromParams(params, actionParams);
222 }
223 else
224 {
225 try
226 {
227 orderableField.populateParamsFromString(params, (String) getProperties().get(attributeName), issue);
228 }
229 catch (FieldValidationException e)
230 {
231 throw new JellyTagException(e.getMessage(), e);
232 }
233 }
234 }
235 else
236 {
237
238 throw new JellyTagException("Invalid attribute '" + attributeName + "'.");
239 }
240 }
241 }
242
243 return params;
244 }
245
246 private User getUser() throws JellyTagException
247 {
248 String username;
249 if (paramSpecified(KEY_USER))
250 {
251 username = getUsername();
252 }
253 else
254 {
255
256 username = (String) getContext().getVariable(JellyTagConstants.USERNAME);
257 }
258
259 User user;
260 try
261 {
262 if (TextUtils.stringSet(username))
263 {
264 user = UserUtils.getUser(username);
265 }
266 else
267 {
268 user = null;
269 }
270 }
271 catch (EntityNotFoundException e)
272 {
273 throw new JellyTagException("The user '" + username + "' cannot be found.", e);
274 }
275
276 return user;
277 }
278
279 private void validateAttributes() throws JellyTagException
280 {
281 if (!paramSpecified(KEY_ISSUE_KEY) || !TextUtils.stringSet(getKey()))
282 {
283 throw new MissingAttributeException(KEY_ISSUE_KEY);
284 }
285
286 if (!paramSpecified(KEY_WORKFLOW_ACTION) || !TextUtils.stringSet(getWorkflowActionProperty()))
287 {
288 throw new MissingAttributeException(KEY_WORKFLOW_ACTION);
289 }
290
291 Issue issue = getIssue();
292
293 ActionDescriptor actionDescriptor = getActionId(issue);
294 WorkflowActionsBean workflowActionsBean = new WorkflowActionsBean();
295 FieldScreen fieldScreen = workflowActionsBean.getFieldScreenForView(actionDescriptor);
296
297 for (Iterator iterator = getProperties().keySet().iterator(); iterator.hasNext();)
298 {
299 String attributeName = (String) iterator.next();
300
301 if (!attributes.contains(attributeName))
302 {
303 String fieldId = attributeName;
304 int index = attributeName.indexOf(":");
305 if (index > -1)
306 {
307 fieldId = attributeName.substring(0, index);
308 }
309
310
311 if (!fieldManager.isOrderableField(fieldId))
312 {
313 throw new JellyTagException("Invalid attribute '" + attributeName + "'.");
314 }
315 else if(fieldScreen == null)
316 {
317 throw new JellyTagException("Field '" + attributeName + "' can not be set on action with no screen");
318 }
319
320 else if (!fieldScreen.containsField(fieldId))
321 {
322 throw new JellyTagException("Field '" + attributeName + "' is not present on screen '" + fieldScreen.getName() + "'.");
323 }
324 }
325 }
326 }
327
328 private ActionDescriptor getActionIdByName(JiraWorkflow workflow) throws JellyTagException
329 {
330 String workflowActionProperty = getWorkflowActionProperty();
331
332 for (Iterator iterator = workflow.getAllActions().iterator(); iterator.hasNext();)
333 {
334 ActionDescriptor actionDescriptor = (ActionDescriptor) iterator.next();
335 if (workflowActionProperty.equalsIgnoreCase(actionDescriptor.getName()))
336 {
337 return actionDescriptor;
338 }
339 }
340
341 throw new JellyTagException("Invalid action name '" + workflowActionProperty + "'.");
342 }
343
344 private String getUsername()
345 {
346 return (String) getProperties().get(KEY_USER);
347 }
348
349 public String getKey()
350 {
351 return (String) getProperties().get(KEY_ISSUE_KEY);
352 }
353
354 public String getWorkflowActionProperty()
355 {
356 return (String) getProperties().get(KEY_WORKFLOW_ACTION);
357 }
358
359 private String getComment()
360 {
361 return (String) getProperties().get(KEY_COMMENT);
362 }
363
364 private String getCommentLevel()
365 {
366 return (String) getProperties().get(KEY_COMMENT_LEVEL);
367 }
368
369 private boolean paramSpecified(String paramName)
370 {
371 return getProperties().containsKey(paramName);
372 }
373
374 public void addCustomFieldValue(CustomField customField, String customFieldValue, String key)
375 {
376 final String customFieldId = customField.getId();
377 if (key != null)
378 {
379 getProperties().put(customFieldId + ":" + key, EasyList.build(customFieldValue));
380 }
381 else
382 {
383 if (paramSpecified(customFieldId))
384 {
385
386 Collection values = (Collection) getProperties().get(customFieldId);
387 values.add(customFieldValue);
388 }
389 else
390 {
391 getProperties().put(customFieldId, EasyList.build(customFieldValue));
392 }
393 }
394 }
395 }