>) valueConverter.getComparable(value1);
34 | } catch (NumberFormatException e) {
35 | log.warn("Wrong number format at [" + value1 + "]");
36 |
37 | return false;
38 | } catch (Exception e) {
39 | log.warn("Unable to get comparable from [" + value1 + "]", e);
40 |
41 | return false;
42 | }
43 |
44 | try {
45 | comp2 = valueConverter.getComparable(value2);
46 | } catch (NumberFormatException e) {
47 | log.warn("Wrong number format at [" + value2 + "]");
48 |
49 | return false;
50 | } catch (Exception e) {
51 | log.warn("Unable to get comparable from [" + value2 + "]", e);
52 |
53 | return false;
54 | }
55 |
56 | boolean result = comparingSnipet.compareObjects(comp1, comp2);
57 |
58 | if (log.isDebugEnabled()) {
59 | log.debug("Compare values [" + comp1 + "] and [" + comp2 + "] with result [" + result + "]");
60 | }
61 |
62 | return result;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/.hgtags:
--------------------------------------------------------------------------------
1 | 0a7cd9fe49faf3c8100a16563450568f40b29b5d version_0_7_2
2 | 26a4a4fb5a429466ddfc55dcf41a59f270023961 version_0_7_3
3 | 79dcb45a74d981d95b891cea2b739dc3ebc14707 version_0_7_4
4 | fc1f726673301d0949ad55b2dbdf831844484f3b version_0_7_5
5 | 0df20fa0665e2a3a854dcbacc6a90d3ef2767d94 version_0_7_6
6 | 3f4a1aec12f13a9e3da590676bd18ca0b9f8c3fe version_0_7_7
7 | 286ae7f63fd19afb89223b598c4410684e5de7e1 version_0_7_8
8 | 996e8ef08680708d2a6d79cabb10b29108680c56 version_0_7_9
9 | 3b576afe255675f9689faeadbb3545d07c536aab version_0_7_10
10 | f137f4a87ea2592cc719d4d38c5609501d071946 version_0_7_11
11 | 89bec19bfcac25d5371c0605994728c3d08dbdd4 version_0_7_12
12 | b9c4d488be7087ed7c843de87bdec22bb00b5fab version_0_7_13
13 | 36046f97abe93ae73ec60ecd6bf6d059208a245d version_0_7_14
14 | f024d1f9a03dc8498d35db672de3c5b4ccedd939 version_0_7_15
15 | f024d1f9a03dc8498d35db672de3c5b4ccedd939 version_0_7_15
16 | 0000000000000000000000000000000000000000 version_0_7_15
17 | 0000000000000000000000000000000000000000 version_0_7_15
18 | 2645a928fdd15484b1a62a4667ce2346749326d7 version_0_7_15
19 | 2645a928fdd15484b1a62a4667ce2346749326d7 version_0_7_15
20 | 0000000000000000000000000000000000000000 version_0_7_15
21 | 742af27d414ffbf25ff2c172b6a70937ffa32b43 version_0_7_15_1
22 | 8a8a5b1cfa5757c3b8834d96b3a9cfdc12aae0dc version_0_7_16
23 | 8a8a5b1cfa5757c3b8834d96b3a9cfdc12aae0dc version_0_7_16
24 | 0000000000000000000000000000000000000000 version_0_7_16
25 | 9037034dcbf90c3ed132e2a5cc17bf148a9cafe4 version_0_7_16_1
26 | 9037034dcbf90c3ed132e2a5cc17bf148a9cafe4 version_0_7_16_1
27 | 0000000000000000000000000000000000000000 version_0_7_16_1
28 | 0000000000000000000000000000000000000000 version_0_7_16
29 | 43c6bac3fa0f52d971dd284094c4b1df86e7afbe version_0_7_16
30 | 571672286d251c518c7a746fafa1ad287ce4080c version_0_7_16_3
31 | f627393dec88cecebb3fee36ac116e864ea88eb8 0_7_16
32 | 43c6bac3fa0f52d971dd284094c4b1df86e7afbe version_0_7_16
33 | 0000000000000000000000000000000000000000 version_0_7_16
34 | f627393dec88cecebb3fee36ac116e864ea88eb8 0_7_16
35 | 0000000000000000000000000000000000000000 0_7_16
36 | 571672286d251c518c7a746fafa1ad287ce4080c version_0_7_16_3
37 | 0000000000000000000000000000000000000000 version_0_7_16_3
38 | 0000000000000000000000000000000000000000 version_0_7_16
39 | b30ec5c479d5692da4c9d04ef646ac5a8df8b828 version_0_7_16
40 | 0310d81f765a1dbeb386d681adfa59c6d69d3629 version_0_7_16_5
41 |
--------------------------------------------------------------------------------
/src/main/resources/templates/jira/workflow/validator/windowsdate-validator-edit.vm:
--------------------------------------------------------------------------------
1 |
2 | |
3 | $i18n.getText("windowsdate-validator-edit.date_to_validate"):
4 | |
5 |
6 |
15 | $i18n.getText("windowsdate-validator-edit.choose_date_field")
16 | |
17 |
18 |
19 | |
20 | $i18n.getText("windowsdate-validator-edit.day_frame.label"):
21 | |
22 |
23 | #if (${val-windowsDays})
24 |
25 | #else
26 |
27 | #end
28 | $i18n.getText("windowsdate-validator-edit.day_frame.description")
29 | |
30 |
31 |
32 | |
33 | $i18n.getText("windowsdate-validator-edit.in_relation_to.label"):
34 | |
35 |
36 |
45 | $i18n.getText("windowsdate-validator-edit.in_relation_to.description")
46 | |
47 |
48 |
49 | |
50 |
51 | $i18n.getText("windowsdate-validator-edit.infobox.text")
52 |
53 | |
54 |
55 |
--------------------------------------------------------------------------------
/src/main/resources/templates/jira/workflow/validator/datecompare-validator-input.vm:
--------------------------------------------------------------------------------
1 |
2 | |
3 | $i18n.getText("datecompare-validator.date.label"):
4 | |
5 |
6 |
11 | $i18n.getText("datecompare-validator.date.description")
12 | |
13 |
14 |
15 | |
16 | $i18n.getText("datecompare-validator.condition.label"):
17 | |
18 |
19 |
24 | $i18n.getText("datecompare-validator.condition.description")
25 | |
26 |
27 |
28 | |
29 | $i18n.getText("datecompare-validator.compare.label"):
30 | |
31 |
32 |
37 | $i18n.getText("datecompare-validator.compare.description")
38 | |
39 |
40 |
41 | |
42 | $i18n.getText("datecompare-validator.timepart.label"):
43 | |
44 |
45 |
50 | $i18n.getText("datecompare-validator.timepart.description")
51 | |
52 |
53 |
--------------------------------------------------------------------------------
/src/main/resources/templates/jira/workflow/validator/dateexpressioncompare-validator-input.vm:
--------------------------------------------------------------------------------
1 |
2 | |
3 | $i18n.getText("dateexpressioncompare-validator.date.label"):
4 | |
5 |
6 |
11 | $i18n.getText("dateexpressioncompare-validator.date.description")
12 | |
13 |
14 |
15 | |
16 | $i18n.getText("datecompare-validator.condition.label"):
17 | |
18 |
19 |
24 | $i18n.getText("datecompare-validator.condition.description")
25 | |
26 |
27 |
28 | |
29 | $i18n.getText("datecompare-validator.compare.label"):
30 | |
31 |
32 |
33 | $i18n.getText("dateexpressioncompare-validator.compare.description")
34 | |
35 |
36 |
37 | |
38 | $i18n.getText("datecompare-validator.timepart.label"):
39 | |
40 |
41 |
46 | $i18n.getText("datecompare-validator.timepart.description")
47 | |
48 |
49 |
50 | |
51 | $i18n.getText("dateexpressioncompare-validator.infobox.text")
52 | |
53 |
54 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jsu/transitionssummary/Transition.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsu.transitionssummary;
2 |
3 | import java.sql.Timestamp;
4 |
5 | import com.atlassian.jira.component.ComponentAccessor;
6 | import com.atlassian.jira.util.I18nHelper;
7 |
8 | /**
9 | * @author Gustavo Martin
10 | *
11 | * This class represent a Workflow Transition. And it will keep, temporarily, all its values.
12 | * It obtains the values, reading the Change History.
13 | *
14 | */
15 | public class Transition {
16 | private final I18nHelper i18nHelper;
17 |
18 | private String changedBy;
19 | private Timestamp changedAt;
20 | private StatusDelegate fromStatus;
21 | private StatusDelegate toStatus;
22 | private Timestamp startAt;
23 | private Long duration;
24 |
25 | public Transition (final I18nHelper i18nHelper) {
26 | this.startAt = null;
27 | this.i18nHelper = i18nHelper;
28 | }
29 |
30 | public Long getDurationInMillis(){
31 | return this.duration;
32 | }
33 |
34 | private void setDuration(){
35 | Long retVal = new Long("-1");
36 |
37 | // It calculates the duration since the transition began until the next one is executed.
38 | if (this.startAt != null) {
39 | retVal = this.changedAt.getTime() - this.startAt.getTime();
40 | }
41 |
42 | this.duration = retVal;
43 | }
44 |
45 | public Timestamp getChangedAt() {
46 | return changedAt;
47 | }
48 | public void setChangedAt(Timestamp changedAt) {
49 | this.changedAt = changedAt;
50 | }
51 | public String getChangedBy() {
52 | return changedBy;
53 | }
54 | public void setChangedBy(String changedBy) {
55 | this.changedBy = changedBy;
56 | }
57 | public StatusDelegate getFromStatus() {
58 | return fromStatus;
59 | }
60 | public void setFromStatus(Long fromStatus) {
61 | this.fromStatus = new StatusDelegate(ComponentAccessor.getConstantsManager().getStatusObject(String.valueOf(fromStatus)),i18nHelper);
62 | }
63 | public StatusDelegate getToStatus() {
64 | return toStatus;
65 | }
66 | public void setToStatus(Long toStatus) {
67 | this.toStatus = new StatusDelegate(ComponentAccessor.getConstantsManager().getStatusObject(String.valueOf(toStatus)),i18nHelper);
68 | }
69 | public Timestamp getStartAt() {
70 | return startAt;
71 | }
72 | public void setStartAt(Timestamp startAt) {
73 | this.startAt = startAt;
74 | setDuration();
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/main/resources/templates/jira/workflow/function/copyvaluefromfield-function-edit.vm:
--------------------------------------------------------------------------------
1 |
2 | |
3 | $i18n.getText("copyvaluefromfield-function.source.label"):
4 | |
5 |
6 |
15 | $i18n.getText("copyvaluefromfield-function.source.description")
16 | |
17 |
18 |
19 | |
20 | $i18n.getText("copyvaluefromfield-function.destination.label"):
21 | |
22 |
23 |
32 | $i18n.getText("copyvaluefromfield-function.destination.description")
33 | |
34 |
35 |
36 | | $i18n.getText("copyvaluefromfield-function.option.label"): |
37 |
38 | $i18n.getText("copyvaluefromfield-function.optionsame.label")
43 | $i18n.getText("copyvaluefromfield-function.optionparent.label")
48 | |
49 |
50 |
51 | |
52 | $i18n.getText("copyvaluefromfield-function.option.description")
53 | |
54 |
--------------------------------------------------------------------------------
/src/main/resources/templates/jira/issuetabpanel/transitionssummary/transitions-summary-view.vm:
--------------------------------------------------------------------------------
1 | #disable_html_escaping()
2 | #macro(statusWithIcon $status)
3 | #set ($iconurl = $status.getIconUrl())
4 |
5 |
6 | #if ($iconurl.startsWith('http://') || $iconurl.startsWith('https://'))
7 |
8 | #else
9 |
10 | #end
11 | |
12 |
13 | $textutils.htmlEncode(${status.nameTranslation})
14 | |
15 | #end
16 |
17 | #macro(headerCell $caption $width)
18 | $i18n.getText($caption) |
19 | #end
20 |
21 |
22 |
23 | #headerCell("transitions-summary-view.transition" "34%")
24 | #headerCell("transitions-summary-view.timespent" "15%")
25 | #headerCell("transitions-summary-view.occurence_nb" "15%")
26 | #headerCell("transitions-summary-view.last_executer" "18%")
27 | #headerCell("transitions-summary-view.last_occurence_date" "18%")
28 |
29 |
30 | #foreach ($tran in ${action.getTransitions()})
31 |
32 |
33 |
34 |
35 | #statusWithIcon ($tran.fromStatus)
36 |
37 |
38 | |
39 | #statusWithIcon ($tran.toStatus)
40 |
41 |
42 | |
43 |
44 | $textutils.htmlEncode(${tran.durationAsString})
45 | |
46 |
47 | ${tran.timesToTransition}
48 | |
49 |
50 | #if (${action.isUserExists(${tran.lastUpdater})})
51 | #authorlinkname(${tran.lastUpdater})
52 | #else
53 | $textutils.htmlEncode(${tran.lastUpdater})
54 | #end
55 | |
56 |
57 | $textutils.htmlEncode(${tran.lastUpdateAsString})
58 | |
59 |
60 | #end
61 |
62 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jsu/helpers/FormattableDuration.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsu.helpers;
2 |
3 | /**
4 | * Simple wrapper around a duration in milliseconds. Used for {@link com.googlecode.jsu.customfields.TimeInSourceStatusCFType}
5 | * and {@link com.googlecode.jsu.transitionssummary.TransitionSummary}.
6 | */
7 | public class FormattableDuration {
8 | private final long millis;
9 |
10 | public FormattableDuration(final long millis) {
11 | this.millis = millis;
12 | }
13 |
14 | public FormattableDuration(final String text) {
15 | if(text!=null) {
16 | millis = Long.parseLong(text);
17 | } else {
18 | millis = 0;
19 | }
20 | }
21 |
22 | public long getMillis() {
23 | return millis;
24 | }
25 |
26 | public String getFormatted() {
27 | return getFormatted(millis);
28 | }
29 |
30 | /**
31 | * @return a nice String format of the duration.
32 | */
33 | public static String getFormatted(long duration) {
34 | String retVal;
35 |
36 | if(duration!=0){
37 | Long days = duration / 86400000;
38 | Long restDay = duration % 86400000;
39 |
40 | Long hours = restDay / 3600000;
41 | Long resthours = restDay % 3600000;
42 |
43 | Long minutes = resthours / 60000;
44 | Long restMinutes = resthours % 60000;
45 |
46 | Long seconds = restMinutes / 1000;
47 |
48 | // If it has been days, it does not have sense to show the seconds.
49 | retVal = days.equals(new Long("0"))?"":String.valueOf(days) + "d ";
50 | retVal = retVal + (hours.equals(new Long("0"))?"":String.valueOf(hours) + "h ");
51 | retVal = retVal + (minutes.equals(new Long("0"))?"":String.valueOf(minutes) + "m ");
52 | if((days.equals(new Long("0"))) && (hours.equals(new Long("0")))){
53 | retVal = retVal + (seconds.equals(new Long("0"))?"":String.valueOf(seconds) + "s");
54 | }
55 |
56 | }else{
57 | retVal = "0s";
58 | }
59 |
60 | return retVal;
61 | }
62 |
63 | @Override
64 | public String toString() {
65 | return Long.toString(millis);
66 | }
67 |
68 | @Override
69 | public boolean equals(Object o) {
70 | if (this == o) return true;
71 | if (o == null || getClass() != o.getClass()) return false;
72 |
73 | FormattableDuration that = (FormattableDuration) o;
74 |
75 | if (millis != that.millis) return false;
76 |
77 | return true;
78 | }
79 |
80 | @Override
81 | public int hashCode() {
82 | return (int) (millis ^ (millis >>> 32));
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/main/resources/com/googlecode/jsu/i18n_ru.properties:
--------------------------------------------------------------------------------
1 | # atlassian-plugin.xml texts
2 | customfield-type.locationtextfield.name=\u0410\u0434\u0440\u0435\u0441
3 | customfield-type.locationtextfield.description=\u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C\u0441\u043A\u043E\u0435 \u043F\u043E\u043B\u0435 \u0434\u043B\u044F \u0432\u0432\u043E\u0434\u0430 \u0430\u0434\u0440\u0435\u0441\u0430 \u0438 \u043E\u0442\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u044F \u0435\u0433\u043E \u043D\u0430 \u043A\u0430\u0440\u0442\u0435 Google.
4 |
5 | customfield-type.locationselect.name=\u0421\u043F\u0438\u0441\u043E\u043A \u0430\u0434\u0440\u0435\u0441\u043E\u0432
6 | customfield-type.locationselect.description=\u041F\u043E\u043B\u0435 \u0434\u043B\u044F \u0432\u0432\u043E\u0434\u0430 \u0441\u043F\u0438\u0441\u043A\u0430 \u0430\u0434\u0440\u0435\u0441\u043E\u0432 \u0438 \u043E\u0442\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u044F \u0438\u0445 \u043D\u0430 \u043A\u0430\u0440\u0442\u0435 Google.
7 |
8 | issue-tabpanel.transitions-summary-tabpanel.description=\u041F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442 \u0441\u043A\u043E\u043B\u044C\u043A\u043E \u0432\u0440\u0435\u043C\u0435\u043D\u0438 \u0431\u044B\u043B\u043E \u043F\u043E\u0442\u0440\u0430\u0447\u0435\u043D\u043E \u043D\u0430 \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u0435 \u043A\u0430\u0436\u0434\u043E\u0433\u043E \u043F\u0435\u0440\u0435\u0445\u043E\u0434\u0430 \u0432 \u043F\u0440\u043E\u0446\u0435\u0441\u0441\u0435.
9 |
10 | # velocity templates and java classes texts
11 | view-location.hide_map=\u0421\u043A\u0440\u044B\u0442\u044C \u043A\u0430\u0440\u0442\u0443
12 | view-location.location_not_found=\u0410\u0434\u0440\u0435\u0441 \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D
13 |
14 | transitions-summary-view.last_executer=\u041F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u0439 \u0438\u0441\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C
15 | transitions-summary-view.last_occurence_date=\u0414\u0430\u0442\u0430 \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0433\u043E \u043F\u0435\u0440\u0435\u0445\u043E\u0434\u0430
16 | transitions-summary-view.name=\u041F\u0435\u0440\u0435\u0445\u043E\u0434\u044B
17 | transitions-summary-view.occurence_nb=\u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u043F\u0435\u0440\u0435\u0445\u043E\u0434\u043E\u0432
18 | transitions-summary-view.timespent=\u041F\u0440\u043E\u0431\u044B\u043B \u0432 \u0438\u0441\u0445\u043E\u0434\u043D\u043E\u043C \u0441\u0442\u0430\u0442\u0443\u0441\u0435
19 | transitions-summary-view.transition=\u041F\u0435\u0440\u0435\u0445\u043E\u0434
20 | transitions-summary-view.not_yet_executed=\u041F\u0435\u0440\u0435\u0445\u043E\u0434\u043E\u0432 \u043F\u0440\u043E\u0446\u0435\u0441\u0441\u0430 \u043F\u043E\u043A\u0430 \u043D\u0435 \u0431\u044B\u043B\u043E.
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jsu/annotation/MapFieldProcessor.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsu.annotation;
2 |
3 | import java.lang.annotation.Annotation;
4 | import java.lang.reflect.Field;
5 | import java.lang.reflect.InvocationTargetException;
6 | import java.lang.reflect.Method;
7 | import java.util.Map;
8 |
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 |
12 | /**
13 | * @author Alexey Abashev
14 | */
15 | public class MapFieldProcessor extends AbstractVisitor {
16 | private final Logger log = LoggerFactory.getLogger(MapFieldProcessor.class);
17 |
18 | private final Class extends Annotation> annotation;
19 | private final Map values;
20 |
21 | public MapFieldProcessor(Class extends Annotation> annotation, Map values) {
22 | super();
23 |
24 | this.annotation = annotation;
25 | this.values = values;
26 | }
27 |
28 | public Class extends Annotation> getAnnotation() {
29 | return annotation;
30 | }
31 |
32 | /* (non-Javadoc)
33 | * @see com.googlecode.jsu.annotation.AbstractVisitor#visitField(java.lang.reflect.Field)
34 | */
35 | public void visitField(Object source, Field field, Annotation sourceAnnon) {
36 | String fieldName = getAnnotationValue(sourceAnnon);
37 |
38 | if ((fieldName == null) || ("".equals(fieldName))) {
39 | fieldName = field.getName();
40 | }
41 |
42 | try {
43 | boolean access = field.isAccessible();
44 |
45 | field.setAccessible(true);
46 | field.set(source, values.get(fieldName));
47 | field.setAccessible(access);
48 | } catch (IllegalArgumentException e) {
49 | log.error("Unable to set class field - " + fieldName, e);
50 | } catch (IllegalAccessException e) {
51 | log.error("Unable to set class field - " + fieldName, e);
52 | }
53 | }
54 |
55 | protected String getAnnotationValue(Annotation annotation) {
56 | String result = null;
57 |
58 | try {
59 | Method valueMethod = annotation.getClass().getDeclaredMethod("value", new Class[] {});
60 |
61 | result = (String) valueMethod.invoke(annotation, new Object[] {});
62 | } catch (SecurityException e) {
63 | // Everything ok
64 | } catch (NoSuchMethodException e) {
65 | // Everything ok
66 | } catch (IllegalArgumentException e) {
67 | // Everything ok
68 | } catch (IllegalAccessException e) {
69 | // Everything ok
70 | } catch (InvocationTargetException e) {
71 | // Everything ok
72 | }
73 |
74 | return result;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jsu/transitionssummary/issuetabpanel/TransitionSummaryAction.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsu.transitionssummary.issuetabpanel;
2 |
3 | import com.atlassian.jira.plugin.issuetabpanel.AbstractIssueAction;
4 | import com.atlassian.jira.plugin.issuetabpanel.IssueTabPanelModuleDescriptor;
5 | import com.atlassian.jira.user.util.UserManager;
6 | import com.atlassian.jira.web.action.JiraWebActionSupport;
7 | import com.googlecode.jsu.transitionssummary.TransitionSummary;
8 | import org.ofbiz.core.util.UtilMisc;
9 |
10 | import java.sql.Timestamp;
11 | import java.util.Calendar;
12 | import java.util.Date;
13 | import java.util.List;
14 | import java.util.Map;
15 |
16 | /**
17 | * @author Gustavo Martin
18 | *
19 | * This is a valid Action, that it allows to visualize the Transition Summaries.
20 | */
21 | public class TransitionSummaryAction extends AbstractIssueAction {
22 | protected final IssueTabPanelModuleDescriptor descriptor;
23 | protected List tranSummaries;
24 | protected Timestamp timePerformed;
25 | protected final UserManager userManager;
26 |
27 | /**
28 | * @param tranSummaries List containing TransitionSummary objects.
29 | */
30 | public TransitionSummaryAction(List tranSummaries, IssueTabPanelModuleDescriptor descriptor, UserManager userManager){
31 | super(descriptor);
32 |
33 | this.tranSummaries = tranSummaries;
34 | this.descriptor = descriptor;
35 | this.timePerformed = new Timestamp(Calendar.getInstance().getTimeInMillis());
36 | this.userManager = userManager;
37 | }
38 |
39 | /**
40 | * @return a List
41 | *
42 | * It allows Velocity to obtain the List of Transition Summaries.
43 | */
44 | public List getTransitions() {
45 | return tranSummaries;
46 | }
47 |
48 | /* (non-Javadoc)
49 | * @see com.atlassian.jira.issue.action.IssueAction#getTimePerformed()
50 | */
51 | public Date getTimePerformed() {
52 | return this.timePerformed;
53 | }
54 |
55 | /* (non-Javadoc)
56 | * @see com.atlassian.jira.issue.action.IssueAction#getHtml(com.atlassian.jira.web.action.JiraWebActionSupport)
57 | */
58 | public String getHtml(JiraWebActionSupport webAction) {
59 | @SuppressWarnings("unchecked")
60 | Map params = UtilMisc.toMap("webAction", webAction, "action", this);
61 |
62 | return descriptor.getHtml("view", params);
63 | }
64 |
65 | protected void populateVelocityParams(Map params) {
66 | params.put("action", this);
67 | }
68 |
69 |
70 | public boolean isUserExists(String username) {
71 | try {
72 | return userManager.getUserByName(username) != null;
73 | } catch (Throwable t) {
74 | return false;
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jsu/workflow/function/ClearFieldValuePostFunction.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsu.workflow.function;
2 |
3 | import java.util.Map;
4 |
5 | import com.atlassian.jira.component.ComponentAccessor;
6 | import com.atlassian.jira.issue.MutableIssue;
7 | import com.atlassian.jira.issue.fields.Field;
8 | import com.atlassian.jira.issue.util.IssueChangeHolder;
9 | import com.atlassian.jira.user.ApplicationUser;
10 | import com.atlassian.jira.util.I18nHelper;
11 | import com.googlecode.jsu.util.WorkflowUtils;
12 | import com.googlecode.jsu.workflow.WorkflowClearFieldValueFunctionPluginFactory;
13 | import com.opensymphony.module.propertyset.PropertySet;
14 | import com.opensymphony.workflow.WorkflowException;
15 |
16 | /**
17 | * This function clears field value.
18 | *
19 | * @author Alexey Abashev
20 | */
21 | public class ClearFieldValuePostFunction extends AbstractPreserveChangesPostFunction {
22 | private final WorkflowUtils workflowUtils;
23 | private final I18nHelper.BeanFactory beanFactory;
24 |
25 | public ClearFieldValuePostFunction(WorkflowUtils workflowUtils, I18nHelper.BeanFactory beanFactory) {
26 | this.workflowUtils = workflowUtils;
27 | this.beanFactory = beanFactory;
28 | }
29 |
30 | /* (non-Javadoc)
31 | * @see com.googlecode.jsu.workflow.function.AbstractPreserveChangesPostFunction#executeFunction(java.util.Map, java.util.Map, com.opensymphony.module.propertyset.PropertySet, com.atlassian.jira.issue.util.IssueChangeHolder)
32 | */
33 | @Override
34 | protected void executeFunction(
35 | Map transientVars, Map args,
36 | PropertySet ps, IssueChangeHolder holder
37 | ) throws WorkflowException {
38 | String fieldKey = args.get(WorkflowClearFieldValueFunctionPluginFactory.FIELD);
39 | Field field = workflowUtils.getFieldFromKey(fieldKey);
40 |
41 | final String fieldName = (field != null) ? field.getName() : "null";
42 |
43 | // It set the value to field.
44 | try {
45 | ApplicationUser currentUser = getCallerUser(transientVars, args);
46 | MutableIssue issue = getIssue(transientVars);
47 |
48 | if (log.isDebugEnabled()) {
49 | log.debug(String.format(
50 | "Clean field '%s - %s' in the issue [%s]",
51 | fieldKey, fieldName, issue.getKey()
52 | ));
53 | }
54 |
55 | workflowUtils.setFieldValue(currentUser, issue, fieldKey, null, holder);
56 | } catch (Exception e) {
57 | I18nHelper i18nh = this.beanFactory.getInstance(
58 | ComponentAccessor.getJiraAuthenticationContext().getUser().getDirectoryUser());
59 | String message = i18nh.getText("clearfieldvalue-function-view.unable_to_purge",fieldKey,fieldName);
60 |
61 | log.error(message, e);
62 |
63 | throw new WorkflowException(message);
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/resources/templates/jira/workflow/validator/dateexpressioncompare-validator-edit.vm:
--------------------------------------------------------------------------------
1 |
2 | |
3 | $i18n.getText("dateexpressioncompare-validator.date.label"):
4 | |
5 |
6 |
15 | $i18n.getText("dateexpressioncompare-validator.date.description")
16 | |
17 |
18 |
19 | |
20 | $i18n.getText("datecompare-validator.condition.label"):
21 | |
22 |
23 |
32 | $i18n.getText("datecompare-validator.condition.description")
33 | |
34 |
35 |
36 | |
37 | $i18n.getText("datecompare-validator.compare.label"):
38 | |
39 |
40 |
41 | $i18n.getText("dateexpressioncompare-validator.compare.description")
42 | |
43 |
44 |
45 | |
46 | $i18n.getText("datecompare-validator.timepart.label"):
47 | |
48 |
49 |
58 | $i18n.getText("datecompare-validator.timepart.description")
59 | |
60 |
61 |
62 | |
63 | $i18n.getText("dateexpressioncompare-validator.infobox.text")
64 | |
65 |
66 |
--------------------------------------------------------------------------------
/src/main/resources/templates/jira/workflow/validator/datecompare-validator-edit.vm:
--------------------------------------------------------------------------------
1 |
2 | |
3 | $i18n.getText("datecompare-validator.date.label"):
4 | |
5 |
6 |
15 | $i18n.getText("datecompare-validator.date.description")
16 | |
17 |
18 |
19 | |
20 | $i18n.getText("datecompare-validator.condition.label"):
21 | |
22 |
23 |
32 | $i18n.getText("datecompare-validator.condition.description")
33 | |
34 |
35 |
36 | |
37 | $i18n.getText("datecompare-validator.compare.label"):
38 | |
39 |
40 |
49 | $i18n.getText("datecompare-validator.compare.description")
50 | |
51 |
52 |
53 | |
54 | $i18n.getText("datecompare-validator.timepart.label"):
55 | |
56 |
57 |
66 | $i18n.getText("datecompare-validator.timepart.description")
67 | |
68 |
69 |
--------------------------------------------------------------------------------
/src/main/resources/templates/jira/workflow/condition/fieldvalue-condition-edit.vm:
--------------------------------------------------------------------------------
1 | #* @vtlvariable name="val-comparisonList" type="java.util.List" *#
2 | #* @vtlvariable name="val-fieldValue" type="java.lang.String" *#
3 | #* @vtlvariable name="val-conditionSelected" type="com.googlecode.jsu.helpers.ConditionType" *#
4 | #* @vtlvariable name="val-conditionList" type="java.util.List" *#
5 | #* @vtlvariable name="val-fieldSelected" type="com.atlassian.jira.issue.fields.Field" *#
6 | #* @vtlvariable name="val-fieldsList" type="java.util.List" *#
7 |
8 | |
9 | $i18n.getText("fieldvalue-condition-edit.field.label"):
10 | |
11 |
12 |
19 | $i18n.getText("fieldvalue-condition-edit.field.description")
20 | |
21 |
22 |
23 |
24 | |
25 | $i18n.getText("fieldvalue-condition-edit.condition.label"):
26 | |
27 |
28 |
35 | $i18n.getText("fieldvalue-condition-edit.condition.description")
36 | |
37 |
38 |
39 |
40 | |
41 | $i18n.getText("fieldvalue-condition-edit.value.label"):
42 | |
43 |
44 |
45 | $i18n.getText("fieldvalue-condition-edit.value.description")
46 | |
47 |
48 |
49 |
50 | |
51 | $i18n.getText("fieldvalue-condition-edit.comparison_type.label"):
52 | |
53 |
54 |
61 | $i18n.getText("fieldvalue-condition-edit.comparison.description")
62 | |
63 |
64 |
65 |
66 | |
67 |
68 | $i18n.getText("fieldvalue-condition-edit.infobox.text","=","!=","!=")
69 |
70 | |
71 |
72 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jsu/workflow/condition/UserIsInAnyRolesCondition.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsu.workflow.condition;
2 |
3 | import com.atlassian.crowd.embedded.api.User;
4 | import com.atlassian.jira.issue.Issue;
5 | import com.atlassian.jira.security.roles.ProjectRole;
6 | import com.atlassian.jira.security.roles.ProjectRoleManager;
7 | import com.atlassian.jira.user.ApplicationUser;
8 | import com.atlassian.jira.user.util.UserManager;
9 | import com.atlassian.jira.workflow.condition.AbstractJiraCondition;
10 | import com.googlecode.jsu.util.WorkflowUtils;
11 | import com.opensymphony.module.propertyset.PropertySet;
12 | import org.slf4j.Logger;
13 | import org.slf4j.LoggerFactory;
14 |
15 | import java.util.Collection;
16 | import java.util.Map;
17 |
18 | /**
19 | * This Condition validates if the current user is in any of the selected roles.
20 | */
21 | public class UserIsInAnyRolesCondition extends AbstractJiraCondition {
22 | private static final Logger LOG = LoggerFactory.getLogger(UserIsInAnyRolesCondition.class);
23 |
24 | private final WorkflowUtils workflowUtils;
25 | private final UserManager userManager;
26 | private final ProjectRoleManager projectRoleManager;
27 |
28 | public UserIsInAnyRolesCondition(WorkflowUtils workflowUtils, UserManager userManager, ProjectRoleManager projectRoleManager) {
29 | this.workflowUtils = workflowUtils;
30 | this.userManager = userManager;
31 | this.projectRoleManager = projectRoleManager;
32 | }
33 |
34 | /* (non-Javadoc)
35 | * @see com.opensymphony.workflow.Condition#passesCondition(java.util.Map, java.util.Map, com.opensymphony.module.propertyset.PropertySet)
36 | */
37 | public boolean passesCondition(Map transientVars, Map args, PropertySet ps) {
38 | // Obtains the current user.
39 | Issue issue=getIssue(transientVars);
40 | String caller = getCallerKey(transientVars,args);
41 |
42 | if (caller != null) { // null -> User not logged in
43 | ApplicationUser userLogged = workflowUtils.getApplicationUser(caller);
44 |
45 | // If there aren't roles selected, hidRolesList is equal to "".
46 | // And rolesSelected will be an empty collection.
47 | String strRolesSelected = (String) args.get("hidRolesList");
48 | Collection rolesSelected = workflowUtils.getRoles(strRolesSelected, WorkflowUtils.SPLITTER);
49 |
50 | for (ProjectRole role : rolesSelected) {
51 | try {
52 | if(projectRoleManager.isUserInProjectRole(userLogged, role, issue.getProjectObject())) {
53 | return true;
54 | }
55 | } catch (Exception e) {
56 | //see JSUTIL-68
57 | }
58 | }
59 | }
60 |
61 | return false;
62 | }
63 | /**
64 | * This ist deprecated because Atlassian API is not working with ApplicationUser
65 | * As soon as this is working this method can be deleted
66 | */
67 | @Deprecated
68 | private User convertApplicationUserToCrowdEmbeddedUser(ApplicationUser applicationUser){
69 | return userManager.getUserObject(applicationUser.getUsername());
70 | }
71 |
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jsu/workflow/condition/UserIsInAnyGroupsCondition.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsu.workflow.condition;
2 |
3 | import com.atlassian.crowd.embedded.api.CrowdService;
4 | import com.atlassian.crowd.embedded.api.Group;
5 | import com.atlassian.crowd.embedded.api.User;
6 | import com.atlassian.jira.user.ApplicationUser;
7 | import com.atlassian.jira.user.util.UserManager;
8 | import com.atlassian.jira.workflow.condition.AbstractJiraCondition;
9 | import com.googlecode.jsu.util.WorkflowUtils;
10 | import com.opensymphony.module.propertyset.PropertySet;
11 | import com.opensymphony.workflow.WorkflowContext;
12 | import org.slf4j.Logger;
13 | import org.slf4j.LoggerFactory;
14 |
15 | import java.util.Collection;
16 | import java.util.Map;
17 |
18 | /**
19 | * @author Gustavo Martin
20 | *
21 | * This Condition validates if the current user is in any of the selected groups.
22 | *
23 | */
24 | public class UserIsInAnyGroupsCondition extends AbstractJiraCondition {
25 | private static final Logger LOG = LoggerFactory.getLogger(UserIsInAnyGroupsCondition.class);
26 |
27 | private final WorkflowUtils workflowUtils;
28 | private final UserManager userManager;
29 | private final CrowdService crowdService;
30 |
31 | public UserIsInAnyGroupsCondition(WorkflowUtils workflowUtils, UserManager userManager, CrowdService crowdService) {
32 | this.workflowUtils = workflowUtils;
33 | this.userManager = userManager;
34 | this.crowdService = crowdService;
35 | }
36 |
37 | /* (non-Javadoc)
38 | * @see com.opensymphony.workflow.Condition#passesCondition(java.util.Map, java.util.Map, com.opensymphony.module.propertyset.PropertySet)
39 | */
40 | public boolean passesCondition(Map transientVars, Map args, PropertySet ps) {
41 | // Obtains the current user.
42 | WorkflowContext context = (WorkflowContext) transientVars.get("context");
43 | String caller = context.getCaller();
44 |
45 | if (caller != null) { // null -> User not logged in
46 | ApplicationUser userLogged = workflowUtils.getApplicationUser(caller);
47 |
48 | // If there aren't groups selected, hidGroupsList is equal to "".
49 | // And groupsSelected will be an empty collection.
50 | String strGroupsSelected = (String) args.get("hidGroupsList");
51 | Collection groupsSelected = workflowUtils.getGroups(strGroupsSelected, WorkflowUtils.SPLITTER);
52 |
53 | for (Group group : groupsSelected) {
54 | try {
55 | if (crowdService.isUserMemberOfGroup(convertApplicationUserToCrowdEmbeddedUser(userLogged), group)) {
56 | return true;
57 | }
58 | } catch (Exception e) {
59 | //see JSUTIL-68
60 | }
61 | }
62 | }
63 |
64 | return false;
65 | }
66 | /**
67 | * This ist deprecated because Atlassian API is not working with ApplicationUser
68 | * As soon as this is working this method can be deleted
69 | */
70 | @Deprecated
71 | private User convertApplicationUserToCrowdEmbeddedUser(ApplicationUser applicationUser){
72 | return userManager.getUserObject(applicationUser.getUsername());
73 | }
74 |
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jsu/helpers/checkers/ConverterString.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsu.helpers.checkers;
2 |
3 | import com.atlassian.crowd.embedded.api.User;
4 | import com.atlassian.jira.issue.label.Label;
5 | import com.atlassian.jira.user.ApplicationUser;
6 | import com.atlassian.jira.issue.customfields.option.Option;
7 | import org.apache.commons.lang.StringUtils;
8 | import org.ofbiz.core.entity.GenericEntity;
9 |
10 | import com.atlassian.jira.issue.IssueConstant;
11 | import com.atlassian.jira.project.Project;
12 |
13 |
14 | import java.lang.reflect.Method;
15 | import java.util.Collection;
16 |
17 | /**
18 | * @author Alexey Abashev
19 | */
20 | public class ConverterString implements ValueConverter {
21 | /* (non-Javadoc)
22 | * @see com.googlecode.jsu.helpers.checkers.ValueConverter#getComparable(java.lang.Object)
23 | */
24 | public Comparable> getComparable(Object object) {
25 | if (object == null) {
26 | return null;
27 | }
28 |
29 | String result = convert(object);
30 |
31 | if (StringUtils.isBlank(result)) {
32 | return null;
33 | }
34 |
35 | return result;
36 | }
37 |
38 |
39 | public String convert(Object value) {
40 | if (value == null || value instanceof String) {
41 | return (String) value;
42 | } else if (value instanceof IssueConstant) {
43 | return ((IssueConstant) value).getName();
44 | } else if (value instanceof Project) {
45 | return ((Project)value).getKey();
46 | } else if (value instanceof Collection && ((Collection) value).size() == 1) {
47 | return convert(((Collection) value).iterator().next());
48 | } else if (value instanceof Option) {
49 | return ((Option) value).getValue();
50 | } else if (value instanceof com.atlassian.jira.issue.fields.option.Option) {
51 | return ((com.atlassian.jira.issue.fields.option.Option) value).getName();
52 | } else if (value instanceof ApplicationUser) {
53 | return ((ApplicationUser) value).getName();
54 | //https://developer.atlassian.com/display/JIRADEV/Renamable+Users+in+JIRA+6.0
55 | } else if (value instanceof User) {
56 | return ((User) value).getName();
57 | } else if (value instanceof Label) {
58 | return ((Label) value).getLabel();
59 | } else if (value instanceof GenericEntity) {
60 | String s = ((GenericEntity) value).getString("name");
61 | if (StringUtils.isEmpty(s)) {
62 | s = ((GenericEntity) value).getString("id");
63 | if (StringUtils.isEmpty(s)) {
64 | s = value.toString();
65 | }
66 | }
67 | return s;
68 | } else {
69 | try {
70 | Method getName = value.getClass().getMethod("getName");
71 | return getName.invoke(value).toString();
72 | } catch (Exception e) { /* try getId() ... */ }
73 | try {
74 | Method getId = value.getClass().getMethod("getId");
75 | return getId.invoke(value).toString();
76 | } catch (Exception e) { /* use toString() ... */ }
77 | return value.toString();
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/main/resources/templates/jira/workflow/function/updateissuefield-function-input.vm:
--------------------------------------------------------------------------------
1 |
31 |
32 |
33 | |
34 | $i18n.getText("updateissuefield-function-input.field.label"):
35 | |
36 |
37 |
46 | $i18n.getText("updateissuefield-function-input.field.description")
47 | |
48 |
49 |
50 |
51 | |
52 | $i18n.getText("updateissuefield-function-input.value.label"):
53 | |
54 | #foreach ($field in ${fields})
55 |
56 |
57 | $i18n.getText("updateissuefield-function-input.value.description")
58 | |
59 | #end
60 |
61 |
62 |
63 | | $i18n.getText("updateissuefield-function-input.append.label"): |
64 |
65 |
70 | $i18n.getText("updateissuefield-function-input.append.description")
71 | |
72 |
73 |
74 |
75 |
81 |
82 | |
83 | $i18n.getText("updateissuefield-function-input.infobox.text")
84 | |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jsu/transitionssummary/issuetabpanel/TransitionsSummaryTabPanel.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsu.transitionssummary.issuetabpanel;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Collections;
5 | import java.util.List;
6 |
7 | import com.atlassian.crowd.embedded.api.User;
8 | import com.atlassian.jira.issue.Issue;
9 | import com.atlassian.jira.issue.tabpanels.GenericMessageAction;
10 | import com.atlassian.jira.plugin.issuetabpanel.IssueAction;
11 | import com.atlassian.jira.plugin.issuetabpanel.IssueTabPanel;
12 | import com.atlassian.jira.plugin.issuetabpanel.IssueTabPanelModuleDescriptor;
13 | import com.atlassian.jira.user.util.UserManager;
14 | import com.atlassian.jira.util.I18nHelper;
15 | import com.googlecode.jsu.transitionssummary.TransitionSummary;
16 | import com.googlecode.jsu.transitionssummary.TransitionsManager;
17 |
18 | /**
19 | * @author Gustavo Martin
20 | *
21 | * It will be add a new Issue Tab Panel.
22 | *
23 | */
24 | public class TransitionsSummaryTabPanel implements IssueTabPanel {
25 |
26 | protected IssueTabPanelModuleDescriptor descriptor;
27 |
28 | private final TransitionsManager transitionsManager;
29 | private final UserManager userManager;
30 | private final I18nHelper.BeanFactory beanFactory;
31 |
32 | public TransitionsSummaryTabPanel(TransitionsManager transitionsManager,
33 | UserManager userManager,
34 | I18nHelper.BeanFactory beanFactory) {
35 | this.transitionsManager = transitionsManager;
36 | this.userManager = userManager;
37 | this.beanFactory = beanFactory;
38 | }
39 |
40 | /* (non-Javadoc)
41 | * @see com.googlecode.jsu.issuetabpanel.IssueTabPanel#init(com.googlecode.jsu.issuetabpanel.IssueTabPanelModuleDescriptor)
42 | */
43 | public void init(IssueTabPanelModuleDescriptor descriptor) {
44 | this.descriptor = descriptor;
45 |
46 | }
47 |
48 | /* (non-Javadoc)
49 | * @see com.googlecode.jsu.issuetabpanel.IssueTabPanel#getActions(org.ofbiz.core.entity.GenericValue, com.opensymphony.user.User)
50 | */
51 | public List getActions(Issue issue, User remoteUser) {
52 | List retList = new ArrayList();
53 | List transitions = transitionsManager.getTransitionSummary(issue);
54 |
55 | // Allway adds only one record to the tab. This is thus, because if there are transition sumeries,
56 | // it exposes the List with all Transition Summaries. Then, velocity will be in charge of render it properly.
57 | if (transitions == null || transitions.isEmpty()) {
58 |
59 | GenericMessageAction action = new GenericMessageAction(
60 | this.beanFactory.getInstance(remoteUser).getText("transitions-summary-view.not_yet_executed"));
61 | retList.add(action);
62 | } else {
63 | Collections.sort(transitions, new TransitionSummaryComparator());
64 |
65 | retList.add(new TransitionSummaryAction(transitions, descriptor, userManager));
66 | }
67 |
68 | return retList;
69 | }
70 |
71 | /* (non-Javadoc)
72 | * @see com.googlecode.jsu.issuetabpanel.IssueTabPanel#showPanel(org.ofbiz.core.entity.GenericValue)
73 | */
74 | public boolean showPanel(Issue issue, User remoteUser) {
75 | return true;
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jsu/workflow/function/AbstractPreserveChangesPostFunction.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsu.workflow.function;
2 |
3 | import java.util.LinkedList;
4 | import java.util.List;
5 | import java.util.Map;
6 |
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 |
10 | import com.atlassian.jira.issue.history.ChangeItemBean;
11 | import com.atlassian.jira.issue.util.DefaultIssueChangeHolder;
12 | import com.atlassian.jira.issue.util.IssueChangeHolder;
13 | import com.atlassian.jira.workflow.function.issue.AbstractJiraFunctionProvider;
14 | import com.opensymphony.module.propertyset.PropertySet;
15 | import com.opensymphony.workflow.WorkflowException;
16 |
17 | /**
18 | * Abstract post-function with transparent change tracking.
19 | *
20 | * @author Alexey Abashev
21 | */
22 | abstract class AbstractPreserveChangesPostFunction extends AbstractJiraFunctionProvider {
23 | private static final String CHANGE_ITEMS = "changeItems";
24 |
25 | protected final Logger log = LoggerFactory.getLogger(this.getClass());
26 |
27 | /**
28 | * Mirror for execute method but with holder for changes
29 | *
30 | * @throws WorkflowException
31 | */
32 | protected abstract void executeFunction(
33 | Map transientVars, Map args,
34 | PropertySet ps, IssueChangeHolder holder
35 | ) throws WorkflowException;
36 |
37 | @SuppressWarnings("unchecked")
38 | public final void execute(Map transientVars, Map args, PropertySet ps) throws WorkflowException {
39 | IssueChangeHolder holder = createChangeHolder(transientVars);
40 |
41 | if (log.isDebugEnabled()) {
42 | log.debug(
43 | "Executing function with [transientVars=" +
44 | transientVars +
45 | ";args=" +
46 | args +
47 | ";ps=" +
48 | ps +
49 | "]"
50 | );
51 | }
52 |
53 | try {
54 | executeFunction(transientVars, args, ps, holder);
55 | } finally {
56 | releaseChangeHolder(holder, transientVars);
57 | }
58 | }
59 |
60 | /**
61 | * Create new holder with changes from transient vars
62 | */
63 | @SuppressWarnings("unchecked")
64 | private IssueChangeHolder createChangeHolder(Map transientVars) {
65 | List changeItems = (List) transientVars.get(CHANGE_ITEMS);
66 |
67 | if (changeItems == null) {
68 | changeItems = new LinkedList();
69 | }
70 |
71 | if (log.isDebugEnabled()) {
72 | log.debug("Create new holder with items - " + changeItems.toString());
73 | }
74 |
75 | IssueChangeHolder holder = new DefaultIssueChangeHolder();
76 |
77 | holder.setChangeItems(changeItems);
78 |
79 | return holder;
80 | }
81 |
82 | /**
83 | * Release holder for changes.
84 | */
85 | @SuppressWarnings("unchecked")
86 | private void releaseChangeHolder(IssueChangeHolder holder, Map transientVars) {
87 | List items = holder.getChangeItems();
88 |
89 | if (log.isDebugEnabled()) {
90 | log.debug("Release holder with items - " + items.toString());
91 | }
92 |
93 | transientVars.put(CHANGE_ITEMS, items);
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jsu/workflow/WorkflowClearFieldValueFunctionPluginFactory.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsu.workflow;
2 |
3 | import java.util.Collections;
4 | import java.util.HashMap;
5 | import java.util.List;
6 | import java.util.Map;
7 |
8 | import com.atlassian.jira.issue.fields.Field;
9 | import com.atlassian.jira.plugin.workflow.AbstractWorkflowPluginFactory;
10 | import com.atlassian.jira.plugin.workflow.WorkflowPluginFunctionFactory;
11 | import com.googlecode.jsu.util.FieldCollectionsUtils;
12 | import com.googlecode.jsu.util.WorkflowUtils;
13 | import com.opensymphony.workflow.loader.AbstractDescriptor;
14 |
15 | /**
16 | * @author Alexey Abashev
17 | */
18 | public class WorkflowClearFieldValueFunctionPluginFactory
19 | extends AbstractWorkflowPluginFactory
20 | implements WorkflowPluginFunctionFactory {
21 |
22 | public static final String FIELD = "field";
23 | public static final String SELECTED_FIELD = "selectedField";
24 | public static final String FIELD_LIST = "fieldList";
25 |
26 | private final FieldCollectionsUtils fieldCollectionsUtils;
27 | private final WorkflowUtils workflowUtils;
28 |
29 | public WorkflowClearFieldValueFunctionPluginFactory(FieldCollectionsUtils fieldCollectionsUtils,
30 | WorkflowUtils workflowUtils) {
31 | this.fieldCollectionsUtils = fieldCollectionsUtils;
32 | this.workflowUtils = workflowUtils;
33 | }
34 |
35 | /* (non-Javadoc)
36 | * @see com.googlecode.jsu.workflow.AbstractWorkflowPluginFactory#getVelocityParamsForEdit(java.util.Map, com.opensymphony.workflow.loader.AbstractDescriptor)
37 | */
38 | @SuppressWarnings("unchecked")
39 | protected void getVelocityParamsForEdit(Map velocityParams, AbstractDescriptor descriptor) {
40 | this.getVelocityParamsForInput(velocityParams);
41 |
42 | velocityParams.put(SELECTED_FIELD, workflowUtils.getFieldFromDescriptor(descriptor, FIELD));
43 | }
44 |
45 | /* (non-Javadoc)
46 | * @see com.googlecode.jsu.workflow.AbstractWorkflowPluginFactory#getVelocityParamsForInput(java.util.Map)
47 | */
48 | @SuppressWarnings("unchecked")
49 | protected void getVelocityParamsForInput(Map velocityParams) {
50 | List fields = fieldCollectionsUtils.getAllClearableFields();
51 |
52 | velocityParams.put(FIELD_LIST, Collections.unmodifiableList(fields));
53 | }
54 |
55 | /* (non-Javadoc)
56 | * @see com.googlecode.jsu.workflow.AbstractWorkflowPluginFactory#getVelocityParamsForView(java.util.Map, com.opensymphony.workflow.loader.AbstractDescriptor)
57 | */
58 | @SuppressWarnings("unchecked")
59 | protected void getVelocityParamsForView(Map velocityParams, AbstractDescriptor descriptor) {
60 | velocityParams.put(SELECTED_FIELD, workflowUtils.getFieldFromDescriptor(descriptor, FIELD));
61 | }
62 |
63 | /* (non-Javadoc)
64 | * @see com.googlecode.jsu.workflow.WorkflowPluginFactory#getDescriptorParams(java.util.Map)
65 | */
66 | @SuppressWarnings("unchecked")
67 | public Map getDescriptorParams(Map conditionParams) {
68 | Map params = new HashMap();
69 |
70 | try{
71 | String sourceField = extractSingleParam(conditionParams, FIELD);
72 |
73 | params.put(FIELD, sourceField);
74 | } catch(IllegalArgumentException e) {
75 | // Aggregate so that Transitions can be added.
76 | }
77 |
78 | return params;
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/main/resources/templates/jira/workflow/condition/userIsInAnyRoles-condition-edit.vm:
--------------------------------------------------------------------------------
1 |
2 | |
3 | $i18n.getText("userisinanyroles-condition-edit.title"):
4 | |
5 |
6 |
7 |
8 |
9 |
10 |
11 | $i18n.getText("userisinanyroles-condition-edit.available_roles.label"):
12 |
17 |
18 |
19 | |
20 |
21 |
22 | $i18n.getText("userisinanyroles-condition-edit.allowed_roles.label"):
23 |
30 |
31 | |
32 |
33 |
34 |
35 | |
36 |
37 | |
38 |
39 |
40 | |
41 |
42 |
43 | |
44 |
45 |
46 | |
47 |
48 | $i18n.getText("userisinanyroles-condition-edit.infobox.text")
49 |
50 | |
51 |
52 |
53 |
86 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jsu/workflow/condition/UserIsInCustomFieldCondition.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsu.workflow.condition;
2 |
3 | import static com.googlecode.jsu.workflow.WorkflowUserIsInCustomFieldConditionPluginFactory.getAllowUserInField;
4 |
5 | import java.util.Collection;
6 | import java.util.Map;
7 |
8 | import com.atlassian.jira.user.ApplicationUser;
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 |
12 | import com.atlassian.jira.issue.Issue;
13 | import com.atlassian.jira.issue.fields.Field;
14 | import com.atlassian.jira.user.util.UserManager;
15 | import com.atlassian.jira.workflow.condition.AbstractJiraCondition;
16 | import com.googlecode.jsu.util.WorkflowUtils;
17 | import com.opensymphony.module.propertyset.PropertySet;
18 | import com.opensymphony.workflow.WorkflowContext;
19 |
20 | /**
21 | * This Condition validates if the current user is in any of the selected groups.
22 | * @author Anton Afanassiev
23 | */
24 | public class UserIsInCustomFieldCondition extends AbstractJiraCondition {
25 | private final Logger log = LoggerFactory.getLogger(UserIsInCustomFieldCondition.class);
26 |
27 | private final UserManager userManager;
28 | private final WorkflowUtils workflowUtils;
29 |
30 | public UserIsInCustomFieldCondition(UserManager userManager, WorkflowUtils workflowUtils) {
31 | this.userManager = userManager;
32 | this.workflowUtils = workflowUtils;
33 | }
34 |
35 | /* (non-Javadoc)
36 | * @see com.opensymphony.workflow.Condition#passesCondition(java.util.Map, java.util.Map, com.opensymphony.module.propertyset.PropertySet)
37 | */
38 | public boolean passesCondition(Map transientVars, Map args, PropertySet ps) {
39 | boolean allowUser = false;
40 |
41 | // Obtains the current user.
42 | WorkflowContext context = (WorkflowContext) transientVars.get("context");
43 | ApplicationUser userLogged = workflowUtils.getApplicationUser(context.getCaller());
44 |
45 | if (userLogged == null) {
46 | log.warn("Unable to check condition");
47 |
48 | return false;
49 | }
50 |
51 | // If there aren't groups selected, hidGroupsList is equal to "".
52 | // And groupsSelected will be an empty collection.
53 | String fieldKey = (String) args.get("fieldsList");
54 |
55 | boolean allowUserInField = getAllowUserInField(args);
56 |
57 | Field field = workflowUtils.getFieldFromKey(fieldKey);
58 | Issue issue = getIssue(transientVars);
59 |
60 | Object fieldValue = workflowUtils.getFieldValueFromIssue(issue, field);
61 |
62 | if (fieldValue != null) {
63 | if (fieldValue instanceof Collection) {
64 | // support for MultiUser lists. user must be member of that list to pass condition
65 | for (Object value : (Collection) fieldValue) {
66 | allowUser = compareValues(value, userLogged, allowUserInField);
67 |
68 | if (allowUser == allowUserInField) {
69 | break;
70 | }
71 | }
72 | } else {
73 | allowUser = compareValues(fieldValue, userLogged, allowUserInField);
74 | }
75 | } else {
76 | allowUser = !allowUserInField;
77 | }
78 |
79 | return allowUser;
80 | }
81 |
82 | private boolean compareValues(Object fieldValue, ApplicationUser user, boolean allowUserInField) {
83 | boolean result = !allowUserInField;
84 |
85 | if (fieldValue instanceof String) {
86 | if (fieldValue.equals(user.getUsername())) {
87 | result = allowUserInField;
88 | }
89 | } else {
90 | if (fieldValue.equals(user)) {
91 | result = allowUserInField;
92 | }
93 | }
94 |
95 | return result;
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/main/resources/templates/jira/workflow/condition/userIsInAnyGroups-condition-edit.vm:
--------------------------------------------------------------------------------
1 |
2 | |
3 | $i18n.getText("userisinanygroups-condition-edit.title"):
4 | |
5 |
6 |
7 |
8 |
9 |
10 |
11 | $i18n.getText("userisinanygroups-condition-edit.available_groups.label"):
12 |
17 |
18 |
19 | |
20 |
21 |
22 | $i18n.getText("userisinanygroups-condition-edit.allowed_groups.label"):
23 |
30 |
31 | |
32 |
33 |
34 |
35 | |
36 |
37 | |
38 |
39 |
40 | |
41 |
42 |
43 | |
44 |
45 |
46 | |
47 |
48 | $i18n.getText("userisinanygroups-condition-edit.infobox.text")
49 |
50 | |
51 |
52 |
53 |
91 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jsu/transitionssummary/TransitionSummary.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsu.transitionssummary;
2 |
3 | import java.sql.Timestamp;
4 | import java.util.ArrayList;
5 | import java.util.List;
6 |
7 | import com.atlassian.jira.datetime.DateTimeFormatter;
8 | import com.googlecode.jsu.helpers.FormattableDuration;
9 |
10 | /**
11 | * @author Gustavo Martin
12 | *
13 | * This class represents the summary of a set of Transitions.
14 | *
15 | * Allowing to obtain the total duration, how many times it happened through her,
16 | * and who/when it was the last update.
17 | *
18 | */
19 | public class TransitionSummary {
20 | private String id;
21 | private StatusDelegate fromStatus;
22 | private StatusDelegate toStatus;
23 | private Long duration;
24 | private String lastUpdater;
25 | private Timestamp lastUpdate;
26 | private List transitions = new ArrayList();
27 | private DateTimeFormatter userFormatter;
28 |
29 | /**
30 | * @param id an external ID generate.
31 | */
32 | public TransitionSummary(
33 | String id,
34 | StatusDelegate fromStatus,
35 | StatusDelegate toStatus,
36 | DateTimeFormatter userFormatter
37 | ) {
38 | setId(id);
39 | setFromStatus(fromStatus);
40 | setToStatus(toStatus);
41 | setDuration(new Long("0"));
42 | this.userFormatter = userFormatter;
43 | }
44 |
45 | /**
46 | * @param tran a simple Transition.
47 | *
48 | * Allows to add a transition and recalculate the summary values.
49 | */
50 | public void addTransition(Transition tran){
51 | transitions.add(tran);
52 |
53 | setLastUpdater(tran.getChangedBy());
54 | setLastupdate(tran.getChangedAt());
55 |
56 | addTime(tran.getDurationInMillis());
57 | }
58 |
59 | /**
60 | * @return a nice String format of the duration.
61 | */
62 | public String getDurationAsString(){
63 | return FormattableDuration.getFormatted(this.getDurationInMillis());
64 | }
65 |
66 | public int getTimesToTransition(){
67 | return transitions.size();
68 | }
69 |
70 | private void addTime(Long timeInMillis){
71 | setDuration(getDurationInMillis() + timeInMillis);
72 | }
73 |
74 | public String getId() {
75 | return id;
76 | }
77 |
78 | public StatusDelegate getFromStatus() {
79 | return fromStatus;
80 | }
81 |
82 | public StatusDelegate getToStatus() {
83 | return toStatus;
84 | }
85 |
86 | /**
87 | * @return a nice formatted date as String.
88 | */
89 | public String getLastUpdateAsString(){
90 | return this.userFormatter.format(lastUpdate);
91 | }
92 |
93 | /**
94 | * @return the lastUpdate
95 | */
96 | public Timestamp getLastUpdate() {
97 | return lastUpdate;
98 | }
99 |
100 | public String getLastUpdater() {
101 | return lastUpdater;
102 | }
103 |
104 | private void setId(String id) {
105 | this.id = id;
106 | }
107 |
108 | public Long getDurationInMillis() {
109 | return duration;
110 | }
111 |
112 | private void setFromStatus(StatusDelegate fromStatus) {
113 | this.fromStatus = fromStatus;
114 | }
115 |
116 | private void setToStatus(StatusDelegate toStatus) {
117 | this.toStatus = toStatus;
118 | }
119 |
120 | private void setLastupdate(Timestamp lastupdate) {
121 | this.lastUpdate = lastupdate;
122 | }
123 |
124 | private void setLastUpdater(String lastUpdater) {
125 | this.lastUpdater = lastUpdater;
126 | }
127 |
128 | private void setDuration(Long duration) {
129 | this.duration = duration;
130 | }
131 |
132 | }
133 |
--------------------------------------------------------------------------------
/src/test/java/it/com/googlecode/jsu/workflow/function/AbstractTestBase.java:
--------------------------------------------------------------------------------
1 | package it.com.googlecode.jsu.workflow.function;
2 |
3 | import com.atlassian.jira.functest.framework.FuncTestCase;
4 | import com.atlassian.jira.functest.framework.suite.Category;
5 | import com.atlassian.jira.functest.framework.suite.WebTest;
6 | import com.atlassian.jira.testkit.client.restclient.IssueClient;
7 | import com.atlassian.jira.testkit.client.restclient.IssueTransitionsMeta;
8 | import com.atlassian.jira.testkit.client.restclient.TransitionsClient;
9 | import com.atlassian.jira.testkit.client.restclient.WatchersClient;
10 | import com.atlassian.jira.testkit.client.util.TimeBombLicence;
11 |
12 | import java.text.SimpleDateFormat;
13 |
14 | @WebTest({ Category.FUNC_TEST, Category.REST })
15 | public abstract class AbstractTestBase extends FuncTestCase {
16 | protected IssueClient issueClient;
17 | protected TransitionsClient transitionsClient;
18 | protected WatchersClient watchersClient;
19 |
20 | protected static final String STATUS_IN_PROGRESS = "In Progress";
21 | protected static final String STATUS_RESOLVED = "Resolved";
22 |
23 | protected static final long FIELD_DATE_TIME_ID = 10109L;
24 | protected static final long FIELD_DATE_PICKER_ID = 10100L;
25 |
26 | protected static final String FIELD_LOCATION_TEXT = "customfield_10001";
27 | protected static final String FIELD_GROUP_PICKER = "customfield_10110";
28 | protected static final String FIELD_USER_PICKER = "customfield_10002";
29 | protected static final String FIELD_MULTI_USER = "customfield_10113";
30 | protected static final String FIELD_SELECT_LIST = "customfield_10106";
31 | protected static final String FIELD_FREE_TEXT = "customfield_10101";
32 | protected static final String FIELD_LOCATION_SELECT = "customfield_10000";
33 | protected static final String FIELD_MULTI_USER1 = "customfield_10200";
34 | protected static final String FIELD_DATE_TIME = "customfield_" + FIELD_DATE_TIME_ID;
35 | protected static final String FIELD_MULTI_SELECT = "customfield_10103";
36 | protected static final String FIELD_MULTI_GROUP = "customfield_10112";
37 | protected static final String FIELD_READONLY_TEXT = "customfield_10115";
38 | protected static final String FIELD_CASCADING_SELECT = "customfield_10108";
39 | protected static final String FIELD_RADIO_BUTTONS = "customfield_10105";
40 | protected static final String FIELD_DATE_PICKER = "customfield_" + FIELD_DATE_PICKER_ID;
41 | protected static final String FIELD_TEXT_FIELD = "customfield_10107";
42 | protected static final String FIELD_LABELS = "customfield_10111";
43 | protected static final String FIELD_MULTI_CHECKBOXES = "customfield_10102";
44 | protected static final String FIELD_VERSION_PICKER = "customfield_10118";
45 |
46 | protected static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
47 | protected static final SimpleDateFormat DATE_TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
48 |
49 | @Override
50 | protected void setUpTest() {
51 | super.setUpTest();
52 | administration.restoreDataWithLicense("test1.xml", TimeBombLicence.LICENCE_FOR_TESTING);
53 | issueClient = new IssueClient(getEnvironmentData());
54 | transitionsClient = new TransitionsClient(getEnvironmentData());
55 | watchersClient = new WatchersClient(getEnvironmentData());
56 | }
57 |
58 | protected boolean hasTransition(String issueKey, String transitionId, String user) {
59 | transitionsClient.loginAs(user);
60 | return hasTransition(issueKey, transitionId);
61 | }
62 |
63 | protected boolean hasTransition(String issueKey, String transitionId) {
64 | IssueTransitionsMeta meta = transitionsClient.get(issueKey);
65 | for(IssueTransitionsMeta.Transition transition:meta.transitions) {
66 | if(transition.id==Integer.parseInt(transitionId)) {
67 | return true;
68 | }
69 | }
70 | return false;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jsu/workflow/validator/RegexpFieldValidator.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsu.workflow.validator;
2 |
3 | import com.atlassian.jira.component.ComponentAccessor;
4 | import com.atlassian.jira.issue.fields.Field;
5 | import com.atlassian.jira.util.I18nHelper;
6 | import com.googlecode.jsu.annotation.Argument;
7 | import com.googlecode.jsu.util.FieldCollectionsUtils;
8 | import com.googlecode.jsu.util.WorkflowUtils;
9 | import com.opensymphony.workflow.InvalidInputException;
10 | import com.opensymphony.workflow.WorkflowException;
11 | import org.slf4j.Logger;
12 | import org.slf4j.LoggerFactory;
13 |
14 | import java.util.ArrayList;
15 | import java.util.Iterator;
16 | import java.util.List;
17 |
18 | /**
19 | * This validator verifies that a given field its contents are not empty and match agains
20 | * a regular expression.
21 | */
22 | public class RegexpFieldValidator extends GenericValidator {
23 | private static final Logger log = LoggerFactory.getLogger(RegexpFieldValidator.class);
24 |
25 | @Argument("fieldSelected")
26 | private String validateField;
27 |
28 | @Argument("expressionSelected")
29 | private String expression;
30 |
31 | private final I18nHelper.BeanFactory beanFactory;
32 |
33 | public RegexpFieldValidator(FieldCollectionsUtils fieldCollectionsUtils,
34 | WorkflowUtils workflowUtils,
35 | I18nHelper.BeanFactory beanFactory
36 | ) {
37 | super(fieldCollectionsUtils, workflowUtils);
38 |
39 | this.beanFactory = beanFactory;
40 | }
41 |
42 | /* (non-Javadoc)
43 | * @see com.opensymphony.workflow.Validator#validate(java.util.Map, java.util.Map, com.opensymphony.module.propertyset.PropertySet)
44 | */
45 | protected void validate() throws InvalidInputException, WorkflowException {
46 | Field field = workflowUtils.getFieldFromKey(validateField);
47 |
48 | // check that field contents match against regular expression, split into single ones if list type
49 | if((field != null) && (expression != null)) {
50 | Object objValue = workflowUtils.getFieldValueFromIssue(getIssue(), field);
51 |
52 | String completeValue = "";
53 | ArrayList list = new ArrayList();
54 | if(objValue!=null) {
55 | completeValue = objValue.toString();
56 | if(objValue instanceof List) {
57 | List l = (List)objValue;
58 | for(Object o:l) {
59 | list.add(o.toString());
60 | }
61 | } else {
62 | list.add(objValue.toString());
63 | }
64 | } else {
65 | list.add("");
66 | }
67 |
68 | String lastValue = "";
69 | boolean result = true;
70 | Iterator it = list.iterator();
71 | while(result && it.hasNext()) {
72 | lastValue = it.next();
73 | result = lastValue.matches(expression);
74 | }
75 |
76 | if (log.isDebugEnabled()) {
77 | log.debug(
78 | "Validate field \"" + field.getName() +
79 | "\" and expression \"" + expression +
80 | "\" with value [" + completeValue + "] with result " + result
81 | );
82 | }
83 |
84 | if(!result) {
85 | I18nHelper i18nh = this.beanFactory.getInstance(
86 | ComponentAccessor.getJiraAuthenticationContext().getUser().getDirectoryUser());
87 | String msg = i18nh.getText("regexpfield-validator-view.not_matching",field.getName(),lastValue,expression);
88 | this.setExceptionMessage(
89 | field,
90 | msg,
91 | msg
92 | );
93 | }
94 | } else {
95 | log.error("Unable to find field with id [" + validateField + "]");
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jsu/workflow/WorkflowRegexpFieldValidatorPluginFactory.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsu.workflow;
2 |
3 | import com.atlassian.jira.issue.fields.Field;
4 | import com.atlassian.jira.plugin.workflow.AbstractWorkflowPluginFactory;
5 | import com.atlassian.jira.plugin.workflow.WorkflowPluginValidatorFactory;
6 | import com.googlecode.jsu.util.FieldCollectionsUtils;
7 | import com.googlecode.jsu.util.WorkflowUtils;
8 | import com.opensymphony.workflow.loader.AbstractDescriptor;
9 | import com.opensymphony.workflow.loader.ValidatorDescriptor;
10 |
11 | import java.util.HashMap;
12 | import java.util.List;
13 | import java.util.Map;
14 |
15 | /**
16 | * This class defines the parameters available for Regexp Field Validator.
17 | */
18 | public class WorkflowRegexpFieldValidatorPluginFactory
19 | extends AbstractWorkflowPluginFactory
20 | implements WorkflowPluginValidatorFactory {
21 |
22 | public static final String PARAM_VALIDATE_FIELD = "fieldSelected";
23 | public static final String PARAM_EXPRESSION = "expressionSelected";
24 |
25 | private static final String VAL_VALIDATE_FIELD = "val-" + PARAM_VALIDATE_FIELD;
26 | private static final String VAL_EXPRESSION = "val-" + PARAM_EXPRESSION;
27 |
28 | private final FieldCollectionsUtils fieldCollectionsUtils;
29 | private final WorkflowUtils workflowUtils;
30 |
31 | public WorkflowRegexpFieldValidatorPluginFactory(FieldCollectionsUtils fieldCollectionsUtils,WorkflowUtils workflowUtils) {
32 | this.fieldCollectionsUtils = fieldCollectionsUtils;
33 | this.workflowUtils = workflowUtils;
34 | }
35 |
36 | /* (non-Javadoc)
37 | * @see com.googlecode.jsu.workflow.AbstractWorkflowPluginFactory#getVelocityParamsForInput(java.util.Map)
38 | */
39 | protected void getVelocityParamsForInput(Map velocityParams) {
40 | List allTextFields = fieldCollectionsUtils.getAllRegexpFields();
41 |
42 | velocityParams.put("val-fieldList", allTextFields);
43 | }
44 |
45 | /* (non-Javadoc)
46 | * @see com.googlecode.jsu.workflow.AbstractWorkflowPluginFactory#getVelocityParamsForEdit(java.util.Map, com.opensymphony.workflow.loader.AbstractDescriptor)
47 | */
48 | protected void getVelocityParamsForEdit(Map velocityParams, AbstractDescriptor descriptor) {
49 | getVelocityParamsForInput(velocityParams);
50 | getVelocityParamsForView(velocityParams,descriptor);
51 | }
52 |
53 | /* (non-Javadoc)
54 | * @see com.googlecode.jsu.workflow.AbstractWorkflowPluginFactory#getVelocityParamsForView(java.util.Map, com.opensymphony.workflow.loader.AbstractDescriptor)
55 | */
56 | protected void getVelocityParamsForView(Map velocityParams, AbstractDescriptor descriptor) {
57 | ValidatorDescriptor validatorDescriptor = (ValidatorDescriptor) descriptor;
58 | @SuppressWarnings("unchecked")
59 | Map args = validatorDescriptor.getArgs();
60 |
61 | String validateField = (String) args.get(PARAM_VALIDATE_FIELD);
62 | String expression = (String) args.get(PARAM_EXPRESSION);
63 |
64 | velocityParams.put(VAL_VALIDATE_FIELD, workflowUtils.getFieldFromKey(validateField));
65 | velocityParams.put(VAL_EXPRESSION, expression);
66 | }
67 |
68 | /* (non-Javadoc)
69 | * @see com.googlecode.jsu.workflow.WorkflowPluginFactory#getDescriptorParams(java.util.Map)
70 | */
71 | public Map getDescriptorParams(Map validatorParams) {
72 | Map params = new HashMap();
73 |
74 | try{
75 | String validateField = extractSingleParam(validatorParams, "fieldList");
76 | String expression = extractSingleParam(validatorParams, "expression");
77 |
78 | params.put(PARAM_VALIDATE_FIELD, validateField);
79 | params.put(PARAM_EXPRESSION, expression);
80 |
81 | } catch(IllegalArgumentException iae) {
82 | // Aggregate so that Transitions can be added.
83 | }
84 |
85 | return params;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jsu/customfields/TimeInSourceStatusCFType.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsu.customfields;
2 |
3 | import com.atlassian.jira.issue.Issue;
4 | import com.atlassian.jira.issue.customfields.impl.CalculatedCFType;
5 | import com.atlassian.jira.issue.customfields.impl.FieldValidationException;
6 | import com.atlassian.jira.issue.customfields.manager.GenericConfigManager;
7 | import com.atlassian.jira.issue.customfields.persistence.CustomFieldValuePersister;
8 | import com.atlassian.jira.issue.fields.CustomField;
9 | import com.atlassian.jira.issue.fields.config.FieldConfig;
10 | import com.googlecode.jsu.helpers.FormattableDuration;
11 | import com.googlecode.jsu.transitionssummary.TransitionSummary;
12 | import com.googlecode.jsu.transitionssummary.TransitionsManager;
13 |
14 | import java.util.List;
15 |
16 | /**
17 | * Time in source status custom field type, a calculated ready only field showing the amount
18 | * of time that has been spent in the current status of the issue until now. That amount is
19 | * the accumulated duration in milliseconds the issue is, or has been, even multiple times,
20 | * in that status.
21 | *
22 | * In the usual issue view, the time will be shown formatted in seconds, minutes and so on
23 | * depending its length in milliseconds, for issue navigator its list view or exports, milliseconds
24 | * will be taken instead, to be able to make computations with the field its value.
25 | *
26 | * NOTE: This does not properly work yet, this is why it is not enabled in atlassian-plugin.xml
27 | * The {@link com.googlecode.jsu.helpers.FormattableDuration}, used to be able to have either the
28 | * duration in millis and on the other hand properly formatted human readable, can not be handled
29 | * by the restore functionality of JIRA. This means backup/restore fails and integration tests too.
30 | *
31 | * NOTE 2: Not yet clear and to be tested, performance. Does this really work well in large
32 | * installations with workflows having lots of steps, and where transitions are used often?
33 | */
34 | public class TimeInSourceStatusCFType extends CalculatedCFType {
35 |
36 | protected final CustomFieldValuePersister customFieldValuePersister;
37 | protected final GenericConfigManager genericConfigManager;
38 | private final TransitionsManager transitionsManager;
39 |
40 | public TimeInSourceStatusCFType(CustomFieldValuePersister customFieldValuePersister, GenericConfigManager genericConfigManager, TransitionsManager transitionsManager) {
41 | this.customFieldValuePersister = customFieldValuePersister;
42 | this.genericConfigManager = genericConfigManager;
43 | this.transitionsManager = transitionsManager;
44 | }
45 |
46 | @Override
47 | public String getStringFromSingularObject(FormattableDuration value) {
48 | return value!=null?value.toString():"0";
49 | }
50 |
51 | @Override
52 | public FormattableDuration getSingularObjectFromString(String value) throws FieldValidationException {
53 | return value!=null?new FormattableDuration(value):new FormattableDuration(0);
54 | }
55 |
56 | @Override
57 | public FormattableDuration getValueFromIssue(CustomField customField, Issue issue) {
58 | //TODO field not yet sortable nor searchable ??
59 | List summaries = transitionsManager.getTransitionSummary(issue);
60 |
61 | long duration = 0;
62 | TransitionSummary lastSummary = null;
63 | for(TransitionSummary summary:summaries) {
64 | if(summary.getFromStatus().getId().equals(issue.getStatusObject().getId())) {
65 | duration += summary.getDurationInMillis();
66 | }
67 | lastSummary = summary;
68 | }
69 |
70 | //add time since very last switch, which lead to the current status, or since created if no transition yet
71 | if(lastSummary!=null) {
72 | duration += System.currentTimeMillis() - lastSummary.getLastUpdate().getTime();
73 | } else {
74 | duration += System.currentTimeMillis() - issue.getCreated().getTime();
75 | }
76 |
77 | // duration is in millis, return as seconds
78 | return new FormattableDuration(duration);
79 | }
80 |
81 | public FormattableDuration getDefaultValue(FieldConfig fieldConfig) {
82 | return new FormattableDuration(0);
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/main/resources/templates/jira/workflow/validator/fieldsrequired-validator-edit.vm:
--------------------------------------------------------------------------------
1 |
2 | |
3 | $i18n.getText("fieldsrequired-validator-edit.required_fields.title"):
4 | |
5 |
6 |
7 |
8 |
9 | $i18n.getText("fieldsrequired-validator-edit.available_fields"):
10 |
15 |
16 |
17 | |
18 |
19 |
20 | $i18n.getText("fieldsrequired-validator-edit.required_fields"):
21 |
28 |
29 | |
30 |
31 |
32 |
33 | |
34 |
35 | |
36 |
37 |
38 |
39 | |
40 |
41 |
42 | |
43 |
44 | | $i18n.getText("fieldsrequired-validator-edit.ignore_context"): |
45 |
46 |
51 | |
52 |
53 |
54 |
55 | |
56 |
57 | $i18n.getText("fieldsrequired-validator-edit.infobox.text")
58 |
59 | |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jsu/workflow/WorkflowUserIsInAnyGroupsConditionPluginFactory.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsu.workflow;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Collection;
5 | import java.util.HashMap;
6 | import java.util.Map;
7 |
8 | import com.atlassian.crowd.embedded.api.Group;
9 | import com.atlassian.jira.plugin.workflow.AbstractWorkflowPluginFactory;
10 | import com.atlassian.jira.plugin.workflow.WorkflowPluginConditionFactory;
11 | import com.atlassian.jira.security.groups.GroupManager;
12 | import com.googlecode.jsu.util.WorkflowUtils;
13 | import com.opensymphony.workflow.loader.AbstractDescriptor;
14 | import com.opensymphony.workflow.loader.ConditionDescriptor;
15 |
16 | /**
17 | * @author Gustavo Martin.
18 | *
19 | * This class defines the parameters available for User Is In Any Group Condition.
20 | *
21 | */
22 | public class WorkflowUserIsInAnyGroupsConditionPluginFactory extends
23 | AbstractWorkflowPluginFactory implements WorkflowPluginConditionFactory {
24 |
25 | private final WorkflowUtils workflowUtils;
26 | private final GroupManager groupManager;
27 |
28 | public WorkflowUserIsInAnyGroupsConditionPluginFactory(WorkflowUtils workflowUtils, GroupManager groupManager) {
29 | this.workflowUtils = workflowUtils;
30 | this.groupManager = groupManager;
31 | }
32 |
33 | /* (non-Javadoc)
34 | * @see com.googlecode.jsu.workflow.AbstractWorkflowPluginFactory#getVelocityParamsForInput(java.util.Map)
35 | */
36 | protected void getVelocityParamsForInput(Map velocityParams) {
37 | velocityParams.put("val-groupsList", groupManager.getAllGroups());
38 | velocityParams.put("val-splitter", WorkflowUtils.SPLITTER);
39 | }
40 |
41 | /* (non-Javadoc)
42 | * @see com.googlecode.jsu.workflow.AbstractWorkflowPluginFactory#getVelocityParamsForEdit(java.util.Map, com.opensymphony.workflow.loader.AbstractDescriptor)
43 | */
44 | protected void getVelocityParamsForEdit(
45 | Map velocityParams,
46 | AbstractDescriptor descriptor
47 | ) {
48 |
49 | getVelocityParamsForInput(velocityParams);
50 |
51 | ConditionDescriptor conditionDescriptor = (ConditionDescriptor) descriptor;
52 | Map args = conditionDescriptor.getArgs();
53 |
54 | velocityParams.remove("val-groupsList");
55 |
56 | String strGroupsSelected = (String)args.get("hidGroupsList");
57 | Collection groupsSelected = workflowUtils.getGroups(strGroupsSelected, WorkflowUtils.SPLITTER);
58 |
59 | Collection groups = groupManager.getAllGroups();
60 |
61 | Collection availableGroups = new ArrayList();
62 | //do not use remove or removeAll, does not work in OD for whatever reason
63 | for(Group g:groups) {
64 | if(!groupsSelected.contains(g)) {
65 | availableGroups.add(g);
66 | }
67 | }
68 | velocityParams.put("val-groupsListSelected", groupsSelected);
69 | velocityParams.put("val-hidGroupsList", workflowUtils.getStringGroup(groupsSelected, WorkflowUtils.SPLITTER));
70 | velocityParams.put("val-groupsList", availableGroups);
71 | }
72 |
73 | /* (non-Javadoc)
74 | * @see com.googlecode.jsu.workflow.AbstractWorkflowPluginFactory#getVelocityParamsForView(java.util.Map, com.opensymphony.workflow.loader.AbstractDescriptor)
75 | */
76 | protected void getVelocityParamsForView(
77 | Map velocityParams,
78 | AbstractDescriptor descriptor
79 | ) {
80 | ConditionDescriptor conditionDescriptor = (ConditionDescriptor) descriptor;
81 | Map args = conditionDescriptor.getArgs();
82 |
83 | String strGroupsSelected = (String)args.get("hidGroupsList");
84 | Collection groupsSelected = workflowUtils.getGroups(strGroupsSelected, WorkflowUtils.SPLITTER);
85 |
86 | velocityParams.put("val-groupsListSelected", groupsSelected);
87 | }
88 |
89 | /* (non-Javadoc)
90 | * @see com.googlecode.jsu.workflow.WorkflowPluginFactory#getDescriptorParams(java.util.Map)
91 | */
92 | public Map getDescriptorParams(Map conditionParams) {
93 | Map params = new HashMap();
94 |
95 | try {
96 | String strGroupsSelected = extractSingleParam(conditionParams, "hidGroupsList");
97 |
98 | params.put("hidGroupsList", strGroupsSelected);
99 | } catch(IllegalArgumentException iae) {
100 | // Aggregate so that Transitions can be added.
101 | }
102 |
103 | return params;
104 | }
105 |
106 | }
107 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jsu/workflow/WorkflowUserIsInAnyRolesConditionPluginFactory.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsu.workflow;
2 |
3 | import com.atlassian.jira.plugin.workflow.AbstractWorkflowPluginFactory;
4 | import com.atlassian.jira.plugin.workflow.WorkflowPluginConditionFactory;
5 | import com.atlassian.jira.security.roles.ProjectRole;
6 | import com.atlassian.jira.security.roles.ProjectRoleManager;
7 | import com.googlecode.jsu.util.WorkflowUtils;
8 | import com.opensymphony.workflow.loader.AbstractDescriptor;
9 | import com.opensymphony.workflow.loader.ConditionDescriptor;
10 |
11 | import java.util.ArrayList;
12 | import java.util.Collection;
13 | import java.util.HashMap;
14 | import java.util.Map;
15 |
16 | /**
17 | * This class defines the parameters available for User Is In Any Roles Condition.
18 | */
19 | public class WorkflowUserIsInAnyRolesConditionPluginFactory extends
20 | AbstractWorkflowPluginFactory implements WorkflowPluginConditionFactory {
21 |
22 | private final WorkflowUtils workflowUtils;
23 | private final ProjectRoleManager projectRoleManager;
24 |
25 | public WorkflowUserIsInAnyRolesConditionPluginFactory(WorkflowUtils workflowUtils, ProjectRoleManager projectRoleManager) {
26 | this.workflowUtils = workflowUtils;
27 | this.projectRoleManager = projectRoleManager;
28 | }
29 |
30 | /* (non-Javadoc)
31 | * @see com.googlecode.jsu.workflow.AbstractWorkflowPluginFactory#getVelocityParamsForInput(java.util.Map)
32 | */
33 | protected void getVelocityParamsForInput(Map velocityParams) {
34 | velocityParams.put("val-rolesList", projectRoleManager.getProjectRoles());
35 | velocityParams.put("val-splitter", WorkflowUtils.SPLITTER);
36 | }
37 |
38 | /* (non-Javadoc)
39 | * @see com.googlecode.jsu.workflow.AbstractWorkflowPluginFactory#getVelocityParamsForEdit(java.util.Map, com.opensymphony.workflow.loader.AbstractDescriptor)
40 | */
41 | protected void getVelocityParamsForEdit(
42 | Map velocityParams,
43 | AbstractDescriptor descriptor
44 | ) {
45 |
46 | getVelocityParamsForInput(velocityParams);
47 |
48 | ConditionDescriptor conditionDescriptor = (ConditionDescriptor) descriptor;
49 | Map args = conditionDescriptor.getArgs();
50 |
51 | velocityParams.remove("val-rolesList");
52 |
53 | String strRolesSelected = (String)args.get("hidRolesList");
54 | Collection rolesSelected = workflowUtils.getRoles(strRolesSelected, WorkflowUtils.SPLITTER);
55 |
56 | Collection roles = projectRoleManager.getProjectRoles();
57 |
58 | Collection availableRoles = new ArrayList();
59 | //do not use remove or removeAll, does not work in OD for whatever reason
60 | for(ProjectRole r:roles) {
61 | if(!rolesSelected.contains(r)) {
62 | availableRoles.add(r);
63 | }
64 | }
65 | velocityParams.put("val-rolesListSelected", rolesSelected);
66 | velocityParams.put("val-hidRolesList", workflowUtils.getStringRole(rolesSelected, WorkflowUtils.SPLITTER));
67 | velocityParams.put("val-rolesList", availableRoles);
68 | }
69 |
70 | /* (non-Javadoc)
71 | * @see com.googlecode.jsu.workflow.AbstractWorkflowPluginFactory#getVelocityParamsForView(java.util.Map, com.opensymphony.workflow.loader.AbstractDescriptor)
72 | */
73 | protected void getVelocityParamsForView(
74 | Map velocityParams,
75 | AbstractDescriptor descriptor
76 | ) {
77 | ConditionDescriptor conditionDescriptor = (ConditionDescriptor) descriptor;
78 | Map args = conditionDescriptor.getArgs();
79 |
80 | String strRolesSelected = (String)args.get("hidRolesList");
81 | Collection rolesSelected = workflowUtils.getRoles(strRolesSelected, WorkflowUtils.SPLITTER);
82 |
83 | velocityParams.put("val-rolesListSelected", rolesSelected);
84 | }
85 |
86 | /* (non-Javadoc)
87 | * @see com.googlecode.jsu.workflow.WorkflowPluginFactory#getDescriptorParams(java.util.Map)
88 | */
89 | public Map getDescriptorParams(Map conditionParams) {
90 | Map params = new HashMap();
91 |
92 | try {
93 | String strRolesSelected = extractSingleParam(conditionParams, "hidRolesList");
94 |
95 | params.put("hidRolesList", strRolesSelected);
96 | } catch(IllegalArgumentException iae) {
97 | // Aggregate so that Transitions can be added.
98 | }
99 |
100 | return params;
101 | }
102 |
103 | }
104 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jsu/workflow/function/CopyValueFromOtherFieldPostFunction.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsu.workflow.function;
2 |
3 |
4 | import static com.googlecode.jsu.workflow.WorkflowCopyValueFromOtherFieldPostFunctionPluginFactory.PARAM_SOURCE_FIELD;
5 | import static com.googlecode.jsu.workflow.WorkflowCopyValueFromOtherFieldPostFunctionPluginFactory.PARAM_DEST_FIELD;
6 | import static com.googlecode.jsu.workflow.WorkflowCopyValueFromOtherFieldPostFunctionPluginFactory.PARAM_COPY_TYPE;
7 |
8 | import java.util.Map;
9 |
10 | import com.atlassian.jira.component.ComponentAccessor;
11 | import com.atlassian.jira.issue.MutableIssue;
12 | import com.atlassian.jira.issue.fields.Field;
13 | import com.atlassian.jira.issue.util.IssueChangeHolder;
14 | import com.atlassian.jira.user.ApplicationUser;
15 | import com.atlassian.jira.util.I18nHelper;
16 | import com.googlecode.jsu.util.WorkflowUtils;
17 | import com.opensymphony.module.propertyset.PropertySet;
18 | import com.opensymphony.workflow.WorkflowException;
19 |
20 | /**
21 | * @author Gustavo Martin
22 | *
23 | * This function copies the value from a field to another one.
24 | */
25 | public class CopyValueFromOtherFieldPostFunction extends AbstractPreserveChangesPostFunction {
26 | private final WorkflowUtils workflowUtils;
27 | private final I18nHelper.BeanFactory beanFactory;
28 |
29 | public CopyValueFromOtherFieldPostFunction(WorkflowUtils workflowUtils, I18nHelper.BeanFactory beanFactory) {
30 | this.workflowUtils = workflowUtils;
31 | this.beanFactory = beanFactory;
32 | }
33 |
34 | /* (non-Javadoc)
35 | * @see com.googlecode.jsu.workflow.function.AbstractPreserveChangesPostFunction#executeFunction(java.util.Map, java.util.Map, com.opensymphony.module.propertyset.PropertySet, com.atlassian.jira.issue.util.IssueChangeHolder)
36 | */
37 | @Override
38 | protected void executeFunction(
39 | Map transientVars, Map args,
40 | PropertySet ps, IssueChangeHolder holder
41 | ) throws WorkflowException {
42 | String fieldFromKey = args.get(PARAM_SOURCE_FIELD);
43 | String fieldToKey = args.get(PARAM_DEST_FIELD);
44 | String copyType = args.get(PARAM_COPY_TYPE);
45 |
46 | Field fieldFrom = workflowUtils.getFieldFromKey(fieldFromKey);
47 | Field fieldTo = workflowUtils.getFieldFromKey(fieldToKey);
48 |
49 | String fieldFromName = (fieldFrom != null) ? fieldFrom.getName() : fieldFromKey;
50 | String fieldToName = (fieldTo != null) ? fieldTo.getName() : fieldToKey;
51 |
52 | try {
53 | ApplicationUser currentUser = getCallerUser(transientVars, args);
54 | MutableIssue issue = getIssue(transientVars);
55 | MutableIssue sourceIssue;
56 | MutableIssue destIssue;
57 | if("parent".equals(copyType)) {
58 | sourceIssue = (MutableIssue) issue.getParentObject();
59 | destIssue = issue;
60 | if(sourceIssue==null) {
61 | log.debug("Issue (" + destIssue.getKey() + ") has no parent, wont do anything.");
62 | return;
63 | }
64 | } else {
65 | //either same, not set, past versions of this did not have this feature, they will be unset
66 | sourceIssue = issue;
67 | destIssue = issue;
68 | }
69 |
70 | // It gives the value from the source field.
71 | Object sourceValue = workflowUtils.getFieldValueFromIssue(sourceIssue, fieldFrom, true);
72 |
73 | if (log.isDebugEnabled()) {
74 | log.debug(
75 | String.format(
76 | "Copying value [%s] from issue %s field '%s' to issue %s field '%s'",
77 | sourceValue,
78 | sourceIssue.getKey(),
79 | fieldFromName,
80 | destIssue.getKey(),
81 | fieldToName
82 | )
83 | );
84 | }
85 |
86 | // It set the value to field.
87 | workflowUtils.setFieldValue(currentUser, destIssue, fieldToKey, sourceValue, holder);
88 |
89 | if (log.isDebugEnabled()) {
90 | log.debug("Value was successfully copied");
91 | }
92 | } catch (Exception e) {
93 | I18nHelper i18nh = this.beanFactory.getInstance(
94 | ComponentAccessor.getJiraAuthenticationContext().getUser().getDirectoryUser());
95 | String message = i18nh.getText("copyvaluefromfield-function-view.unable_to_copy",fieldFromName,fieldToName);
96 | log.error(message, e);
97 | throw new WorkflowException(message);
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jsu/workflow/WorkflowWindowsDateValidatorPluginFactory.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsu.workflow;
2 |
3 | import java.util.HashMap;
4 | import java.util.List;
5 | import java.util.Map;
6 |
7 | import com.atlassian.jira.issue.fields.Field;
8 | import com.atlassian.jira.plugin.workflow.AbstractWorkflowPluginFactory;
9 | import com.atlassian.jira.plugin.workflow.WorkflowPluginValidatorFactory;
10 | import com.googlecode.jsu.util.FieldCollectionsUtils;
11 | import com.googlecode.jsu.util.WorkflowUtils;
12 | import com.opensymphony.workflow.loader.AbstractDescriptor;
13 | import com.opensymphony.workflow.loader.ValidatorDescriptor;
14 | import org.apache.commons.lang.StringUtils;
15 |
16 | /**
17 | * @author Gustavo Martin.
18 | *
19 | * This class defines the parameters available for Windows Date Validator.
20 | *
21 | */
22 | public class WorkflowWindowsDateValidatorPluginFactory extends
23 | AbstractWorkflowPluginFactory implements WorkflowPluginValidatorFactory {
24 |
25 | private final FieldCollectionsUtils fieldCollectionsUtils;
26 | private final WorkflowUtils workflowUtils;
27 |
28 | public WorkflowWindowsDateValidatorPluginFactory(
29 | FieldCollectionsUtils fieldCollectionsUtils,
30 | WorkflowUtils workflowUtils
31 | ) {
32 | this.fieldCollectionsUtils = fieldCollectionsUtils;
33 | this.workflowUtils = workflowUtils;
34 | }
35 |
36 | /* (non-Javadoc)
37 | * @see com.googlecode.jsu.workflow.AbstractWorkflowPluginFactory#getVelocityParamsForInput(java.util.Map)
38 | */
39 | protected void getVelocityParamsForInput(Map velocityParams) {
40 | List allDateFields = fieldCollectionsUtils.getAllDateFields();
41 |
42 | velocityParams.put("val-date1FieldsList", allDateFields);
43 | velocityParams.put("val-date2FieldsList", allDateFields);
44 | }
45 |
46 | /* (non-Javadoc)
47 | * @see com.googlecode.jsu.workflow.AbstractWorkflowPluginFactory#getVelocityParamsForEdit(java.util.Map, com.opensymphony.workflow.loader.AbstractDescriptor)
48 | */
49 | protected void getVelocityParamsForEdit(
50 | Map velocityParams,
51 | AbstractDescriptor descriptor
52 | ) {
53 | getVelocityParamsForInput(velocityParams);
54 |
55 | ValidatorDescriptor validatorDescriptor = (ValidatorDescriptor) descriptor;
56 | Map args = validatorDescriptor.getArgs();
57 |
58 | String date1 = (String) args.get("date1Selected");
59 | String date2 = (String) args.get("date2Selected");
60 | String windowsDays = (String) args.get("windowsDays");
61 |
62 | velocityParams.put("val-date1Selected", workflowUtils.getFieldFromKey(date1));
63 | velocityParams.put("val-date2Selected", workflowUtils.getFieldFromKey(date2));
64 | velocityParams.put("val-windowsDays", windowsDays);
65 |
66 | }
67 |
68 | /* (non-Javadoc)
69 | * @see com.googlecode.jsu.workflow.AbstractWorkflowPluginFactory#getVelocityParamsForView(java.util.Map, com.opensymphony.workflow.loader.AbstractDescriptor)
70 | */
71 | protected void getVelocityParamsForView(
72 | Map velocityParams,
73 | AbstractDescriptor descriptor
74 | ) {
75 | ValidatorDescriptor validatorDescriptor = (ValidatorDescriptor) descriptor;
76 | Map args = validatorDescriptor.getArgs();
77 |
78 | String date1 = (String) args.get("date1Selected");
79 | String date2 = (String) args.get("date2Selected");
80 | String windowsDays = (String) args.get("windowsDays");
81 |
82 | velocityParams.put("val-date1Selected", workflowUtils.getFieldFromKey(date1));
83 | velocityParams.put("val-date2Selected", workflowUtils.getFieldFromKey(date2));
84 | velocityParams.put("val-windowsDays", windowsDays);
85 |
86 | }
87 |
88 | /* (non-Javadoc)
89 | * @see com.googlecode.jsu.workflow.WorkflowPluginFactory#getDescriptorParams(java.util.Map)
90 | */
91 | public Map getDescriptorParams(Map validatorParams) {
92 | Map params = new HashMap();
93 |
94 | try{
95 | String date1 = extractSingleParam(validatorParams, "date1FieldsList");
96 | String date2 = extractSingleParam(validatorParams, "date2FieldsList");
97 | String windowsDays = extractSingleParam(validatorParams, "windowsDays");
98 | if (StringUtils.isEmpty(windowsDays)) {
99 | windowsDays = "0";
100 | }
101 |
102 | params.put("date1Selected", date1);
103 | params.put("date2Selected", date2);
104 | params.put("windowsDays", windowsDays);
105 |
106 | }catch(IllegalArgumentException iae){
107 | // Aggregate so that Transitions can be added.
108 | }
109 |
110 | return params;
111 | }
112 |
113 | }
114 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jsu/helpers/ConditionCheckerFactory.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsu.helpers;
2 |
3 | import java.util.ArrayList;
4 | import java.util.LinkedHashMap;
5 | import java.util.List;
6 | import java.util.Map;
7 |
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 |
11 | import com.googlecode.jsu.helpers.checkers.CheckerCompositeFactory;
12 |
13 | /**
14 | * Return object for checking conditions.
15 | *
16 | * @author Alexey Abashev
17 | */
18 | public class ConditionCheckerFactory {
19 | public static final ConditionType GREATER = new ConditionType(1, ">", "conditiontype.greater_than", "G");
20 | public static final ConditionType GREATER_EQUAL = new ConditionType(2, ">=", "conditiontype.greater_than_or_equal_to", "GE");
21 | public static final ConditionType EQUAL = new ConditionType(3, "=", "conditiontype.equal_to", "E");
22 | public static final ConditionType LESS_EQUAL = new ConditionType(4, "<=", "conditiontype.less_than_or_equal_to", "LE");
23 | public static final ConditionType LESS = new ConditionType(5, "<", "contitiontype.less_than", "L");
24 | public static final ConditionType NOT_EQUAL = new ConditionType(6, "!=", "conditiontype.not_equal_to", "NE");
25 |
26 | public static final ComparisonType STRING = new ComparisonType(1, "comparisontype.string", "String");
27 | public static final ComparisonType NUMBER = new ComparisonType(2, "comparisontype.number", "Number");
28 | public static final ComparisonType DATE = new ComparisonType(3, "comparisontype.date_with_time", "Date");
29 | public static final ComparisonType DATE_WITHOUT_TIME = new ComparisonType(4, "comparisontype.date_without_time", "DateWithoutTime");
30 | public static final ComparisonType OPTIONID = new ComparisonType(5, "comparisontype.optionid", "OptionID");
31 |
32 | /** Template for checker class. */
33 | private static final String PACKAGE = ConditionCheckerFactory.class.getPackage().getName();
34 | private static final String CONDITION_CLASS_TEMPLATE = PACKAGE + ".checkers.Snipet";
35 | private static final String COMPARISON_CLASS_TEMPLATE = PACKAGE + ".checkers.Converter";
36 |
37 | /** Cache for searching through conditions */
38 | @SuppressWarnings("serial")
39 | private static final Map CONDITIONS_CACHE =
40 | new LinkedHashMap(6) {{
41 | put(GREATER.getId(), GREATER);
42 | put(GREATER_EQUAL.getId(), GREATER_EQUAL);
43 | put(EQUAL.getId(), EQUAL);
44 | put(LESS_EQUAL.getId(), LESS_EQUAL);
45 | put(LESS.getId(), LESS);
46 | put(NOT_EQUAL.getId(), NOT_EQUAL);
47 | }};
48 |
49 | /** Cache for searching through types */
50 | @SuppressWarnings("serial")
51 | private static final Map COMPARISONS_CACHE =
52 | new LinkedHashMap(4) {{
53 | put(STRING.getId(), STRING);
54 | put(NUMBER.getId(), NUMBER);
55 | put(DATE.getId(), DATE);
56 | put(DATE_WITHOUT_TIME.getId(), DATE_WITHOUT_TIME);
57 | put(OPTIONID.getId(),OPTIONID);
58 | }};
59 |
60 | private final Logger log = LoggerFactory.getLogger(ConditionCheckerFactory.class);
61 | private final CheckerCompositeFactory checkerCompositeFactory = new CheckerCompositeFactory();
62 |
63 | public ConditionChecker getChecker(ComparisonType type, ConditionType condition) {
64 | String conditionClassName = CONDITION_CLASS_TEMPLATE + condition.getMnemonic();
65 | String comparisonClassName = COMPARISON_CLASS_TEMPLATE + type.getMnemonic();
66 |
67 | if (log.isDebugEnabled()) {
68 | log.debug(
69 | "Using class [" + conditionClassName +
70 | "] for condition [" + condition.getValue() +
71 | "]; class [" + comparisonClassName +
72 | "] for type [" + type.getValueKey() +
73 | "]"
74 | );
75 | }
76 |
77 | return checkerCompositeFactory.getComposite(comparisonClassName, conditionClassName);
78 | }
79 |
80 | /**
81 | * Get all possible condition types.
82 | */
83 | public List getConditionTypes() {
84 | return new ArrayList(CONDITIONS_CACHE.values());
85 | }
86 |
87 | /**
88 | * Get all possible comparison types.
89 | */
90 | public List getComparisonTypes() {
91 |
92 | List comparisonTypes = new ArrayList(COMPARISONS_CACHE.values());
93 | return comparisonTypes;
94 | }
95 |
96 | /**
97 | * Find condition by id.
98 | */
99 | public ConditionType findConditionById(String id) {
100 | return CONDITIONS_CACHE.get(Integer.valueOf(id));
101 | }
102 |
103 | /**
104 | * Find comparison by id.
105 | */
106 | public ComparisonType findComparisonById(String id) {
107 | return COMPARISONS_CACHE.get(Integer.valueOf(id));
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jsu/workflow/WorkflowFieldsRequiredValidatorPluginFactory.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsu.workflow;
2 |
3 | import java.util.Collection;
4 | import java.util.HashMap;
5 | import java.util.List;
6 | import java.util.Map;
7 |
8 | import com.atlassian.jira.issue.fields.Field;
9 | import com.atlassian.jira.plugin.workflow.AbstractWorkflowPluginFactory;
10 | import com.atlassian.jira.plugin.workflow.WorkflowPluginValidatorFactory;
11 | import com.googlecode.jsu.util.FieldCollectionsUtils;
12 | import com.googlecode.jsu.util.WorkflowUtils;
13 | import com.opensymphony.workflow.loader.AbstractDescriptor;
14 | import com.opensymphony.workflow.loader.ValidatorDescriptor;
15 | import org.apache.commons.lang3.StringUtils;
16 |
17 | /**
18 | * This class defines the parameters available for Fields Required Validator.
19 | *
20 | * @author Gustavo Martin.
21 | */
22 | public class WorkflowFieldsRequiredValidatorPluginFactory
23 | extends AbstractWorkflowPluginFactory
24 | implements WorkflowPluginValidatorFactory {
25 |
26 | public static final String SELECTED_FIELDS = "hidFieldsList";
27 | public static final String CONTEXT_HANDLING = "contextHandling";
28 |
29 | private final FieldCollectionsUtils fieldCollectionsUtils;
30 | private final WorkflowUtils workflowUtils;
31 |
32 | public WorkflowFieldsRequiredValidatorPluginFactory(
33 | FieldCollectionsUtils fieldCollectionsUtils,
34 | WorkflowUtils workflowUtils
35 | ) {
36 | this.fieldCollectionsUtils = fieldCollectionsUtils;
37 | this.workflowUtils = workflowUtils;
38 | }
39 |
40 | /* (non-Javadoc)
41 | * @see com.googlecode.jsu.workflow.AbstractWorkflowPluginFactory#getVelocityParamsForInput(java.util.Map)
42 | */
43 | protected void getVelocityParamsForInput(Map velocityParams) {
44 | List allFields = fieldCollectionsUtils.getRequirableFields();
45 |
46 | velocityParams.put("val-fieldsList", allFields);
47 | velocityParams.put("val-splitter", WorkflowUtils.SPLITTER);
48 | }
49 |
50 | /* (non-Javadoc)
51 | * @see com.googlecode.jsu.workflow.AbstractWorkflowPluginFactory#getVelocityParamsForEdit(java.util.Map, com.opensymphony.workflow.loader.AbstractDescriptor)
52 | */
53 | protected void getVelocityParamsForEdit(
54 | Map velocityParams, AbstractDescriptor descriptor
55 | ) {
56 | getVelocityParamsForInput(velocityParams);
57 |
58 | ValidatorDescriptor validatorDescriptor = (ValidatorDescriptor) descriptor;
59 | Map args = validatorDescriptor.getArgs();
60 |
61 | velocityParams.remove("val-fieldsList");
62 |
63 | Collection fieldsSelected = getSelectedFields(args);
64 | List allFields = fieldCollectionsUtils.getRequirableFields();
65 |
66 | String contextHandling = (String) args.get(CONTEXT_HANDLING);
67 |
68 | allFields.removeAll(fieldsSelected);
69 |
70 | velocityParams.put("val-fieldsListSelected", fieldsSelected);
71 | velocityParams.put("val-hidFieldsList", workflowUtils.getStringField(fieldsSelected, WorkflowUtils.SPLITTER));
72 | velocityParams.put("val-fieldsList", allFields);
73 | velocityParams.put("val-contextHandling", contextHandling);
74 | }
75 |
76 | /* (non-Javadoc)
77 | * @see com.googlecode.jsu.workflow.AbstractWorkflowPluginFactory#getVelocityParamsForView(java.util.Map, com.opensymphony.workflow.loader.AbstractDescriptor)
78 | */
79 | protected void getVelocityParamsForView(
80 | Map velocityParams, AbstractDescriptor descriptor
81 | ) {
82 | ValidatorDescriptor validatorDescriptor = (ValidatorDescriptor) descriptor;
83 | Map args = validatorDescriptor.getArgs();
84 |
85 | velocityParams.put("val-fieldsListSelected", getSelectedFields(args));
86 | velocityParams.put("val-contextHandling", args.get(CONTEXT_HANDLING));
87 | }
88 |
89 | /* (non-Javadoc)
90 | * @see com.googlecode.jsu.workflow.WorkflowPluginFactory#getDescriptorParams(java.util.Map)
91 | */
92 | public Map getDescriptorParams(Map validatorParams) {
93 | Map params = new HashMap();
94 | String strFieldsSelected = extractSingleParam(validatorParams, SELECTED_FIELDS);
95 | String contextHandling = null;
96 | if(validatorParams.containsKey(CONTEXT_HANDLING)) {
97 | contextHandling = extractSingleParam(validatorParams, CONTEXT_HANDLING);
98 | }
99 |
100 | params.put(SELECTED_FIELDS, strFieldsSelected);
101 | params.put(CONTEXT_HANDLING, contextHandling);
102 |
103 | return params;
104 | }
105 |
106 | /**
107 | * Get fields were selected in UI.
108 | */
109 | public Collection getSelectedFields(Map args) {
110 | String strFieldsSelected = (String) args.get(SELECTED_FIELDS);
111 |
112 | return workflowUtils.getFields(strFieldsSelected, WorkflowUtils.SPLITTER);
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jsu/workflow/WorkflowUpdateIssueCustomFieldFunctionPluginFactory.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsu.workflow;
2 |
3 | import java.util.HashMap;
4 | import java.util.List;
5 | import java.util.Map;
6 |
7 | import com.atlassian.jira.issue.CustomFieldManager;
8 | import com.atlassian.jira.issue.fields.CustomField;
9 | import com.atlassian.jira.plugin.workflow.AbstractWorkflowPluginFactory;
10 | import com.atlassian.jira.plugin.workflow.WorkflowPluginFunctionFactory;
11 | import com.opensymphony.workflow.loader.AbstractDescriptor;
12 | import com.opensymphony.workflow.loader.FunctionDescriptor;
13 | import org.apache.commons.lang3.StringUtils;
14 |
15 | /**
16 | * Class responsible for setting up all that is necessary for the execution of
17 | * the plugin.
18 | *
19 | * @author Cristiane Fontana
20 | * @version 1.0 Plugin creation.
21 | */
22 | public class WorkflowUpdateIssueCustomFieldFunctionPluginFactory extends
23 | AbstractWorkflowPluginFactory implements WorkflowPluginFunctionFactory {
24 |
25 | public static final String PARAM_FIELD_ID = "fieldId";
26 | public static final String PARAM_FIELD_VALUE = "fieldValue";
27 | public static final String TARGET_FIELD_NAME = "field.name";
28 | public static final String TARGET_FIELD_VALUE = "field.value";
29 | public static final String PARAM_APPEND_VALUE = "appendValue";
30 | public static final String TARGET_APPEND_VALUE = "append.value";
31 |
32 | private final CustomFieldManager customFieldManager;
33 |
34 | public WorkflowUpdateIssueCustomFieldFunctionPluginFactory(CustomFieldManager customFieldManager) {
35 | this.customFieldManager = customFieldManager;
36 | }
37 |
38 | public Map getDescriptorParams(Map conditionParams) {
39 | Map params = new HashMap();
40 |
41 | String fieldId = extractSingleParam(conditionParams, PARAM_FIELD_ID);
42 | params.put(TARGET_FIELD_NAME, fieldId);
43 |
44 | String fieldValue = extractSingleParam(conditionParams, PARAM_FIELD_VALUE);
45 | params.put(TARGET_FIELD_VALUE, fieldValue.trim());
46 |
47 | if(conditionParams.containsKey(PARAM_APPEND_VALUE)) {
48 | String appendValue = extractSingleParam(conditionParams, PARAM_APPEND_VALUE);
49 | if(!StringUtils.isBlank(appendValue)) {
50 | params.put(TARGET_APPEND_VALUE, appendValue.trim());
51 | }
52 | }
53 |
54 | return params;
55 | }
56 |
57 | protected void getVelocityParamsForEdit(Map velocityParams, AbstractDescriptor descriptor) {
58 | getVelocityParamsForInput(velocityParams);
59 |
60 | if (!(descriptor instanceof FunctionDescriptor)) {
61 | throw new IllegalArgumentException("Descriptor must be a FunctionDescriptor.");
62 | }
63 |
64 | FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor;
65 |
66 | velocityParams.put(PARAM_FIELD_ID, functionDescriptor.getArgs().get(TARGET_FIELD_NAME));
67 |
68 | String value = (String) functionDescriptor.getArgs().get(TARGET_FIELD_VALUE);
69 |
70 | if (value == null || value.equals("null")) {
71 | velocityParams.put(PARAM_FIELD_VALUE, null);
72 | } else {
73 | velocityParams.put(PARAM_FIELD_VALUE, value.trim());
74 | }
75 |
76 | String appendValue = (String) functionDescriptor.getArgs().get(TARGET_APPEND_VALUE);
77 |
78 | if (appendValue == null) {
79 | velocityParams.put(PARAM_APPEND_VALUE, null);
80 | } else {
81 | velocityParams.put(PARAM_APPEND_VALUE, appendValue.trim());
82 | }
83 | }
84 |
85 | protected void getVelocityParamsForInput(Map velocityParams) {
86 | List customFields = customFieldManager.getCustomFieldObjects();
87 |
88 | velocityParams.put("fields", customFields);
89 | }
90 |
91 | protected void getVelocityParamsForView(Map velocityParams, AbstractDescriptor descriptor) {
92 | if (!(descriptor instanceof FunctionDescriptor)) {
93 | throw new IllegalArgumentException("Descriptor must be a FunctionDescriptor.");
94 | } else {
95 | FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor;
96 |
97 | String fieldName = (String) functionDescriptor.getArgs().get(TARGET_FIELD_NAME);
98 |
99 | velocityParams.put(
100 | PARAM_FIELD_ID,
101 | customFieldManager.getCustomFieldObject(fieldName).getNameKey()
102 | );
103 | velocityParams.put(
104 | PARAM_FIELD_VALUE,
105 | functionDescriptor.getArgs().get(TARGET_FIELD_VALUE)
106 | );
107 |
108 | String appendValue = (String) functionDescriptor.getArgs().get(TARGET_APPEND_VALUE);
109 | if(!StringUtils.isBlank(appendValue)) {
110 | velocityParams.put(PARAM_APPEND_VALUE, appendValue.trim());
111 | }
112 |
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jsu/workflow/WorkflowUserIsInCustomFieldConditionPluginFactory.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsu.workflow;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | import com.atlassian.jira.issue.CustomFieldManager;
7 | import com.atlassian.jira.issue.fields.Field;
8 | import com.atlassian.jira.plugin.workflow.AbstractWorkflowPluginFactory;
9 | import com.atlassian.jira.plugin.workflow.WorkflowPluginConditionFactory;
10 | import com.googlecode.jsu.util.WorkflowUtils;
11 | import com.opensymphony.workflow.loader.AbstractDescriptor;
12 | import com.opensymphony.workflow.loader.ConditionDescriptor;
13 |
14 | /**
15 | * @author Anton Afanassiev
16 | */
17 | public class WorkflowUserIsInCustomFieldConditionPluginFactory
18 | extends AbstractWorkflowPluginFactory
19 | implements WorkflowPluginConditionFactory {
20 |
21 | private static final String ALLOW_USER_IN_FIELD = "allowUserInField";
22 |
23 | private final CustomFieldManager customFieldManager;
24 | private final WorkflowUtils workflowUtils;
25 |
26 | public WorkflowUserIsInCustomFieldConditionPluginFactory(
27 | CustomFieldManager customFieldManager,
28 | WorkflowUtils workflowUtils
29 | ) {
30 | this.customFieldManager = customFieldManager;
31 | this.workflowUtils = workflowUtils;
32 | }
33 |
34 | /* (non-Javadoc)
35 | * @see com.googlecode.jsu.workflow.AbstractWorkflowPluginFactory#getVelocityParamsForInput(java.util.Map)
36 | */
37 | protected void getVelocityParamsForInput(Map velocityParams) {
38 | velocityParams.put("val-fieldsList", customFieldManager.getCustomFieldObjects());
39 | }
40 |
41 | /* (non-Javadoc)
42 | * @see com.googlecode.jsu.workflow.AbstractWorkflowPluginFactory#getVelocityParamsForEdit(java.util.Map, com.opensymphony.workflow.loader.AbstractDescriptor)
43 | */
44 | protected void getVelocityParamsForEdit(Map velocityParams, AbstractDescriptor descriptor) {
45 | getVelocityParamsForInput(velocityParams);
46 |
47 | ConditionDescriptor conditionDescriptor = (ConditionDescriptor) descriptor;
48 | Map args = conditionDescriptor.getArgs();
49 |
50 | String sField = (String) args.get("fieldsList");
51 |
52 | Field field = null;
53 |
54 | try {
55 | field = workflowUtils.getFieldFromKey(sField);
56 | } catch (Exception e) {
57 | }
58 |
59 | if (field != null) {
60 | velocityParams.put("val-fieldSelected", field);
61 | }
62 |
63 | boolean allowUserInField = getAllowUserInField(args);
64 |
65 | velocityParams.put("allowUserInField-selected", allowUserInField);
66 | }
67 |
68 | /* (non-Javadoc)
69 | * @see com.googlecode.jsu.workflow.AbstractWorkflowPluginFactory#getVelocityParamsForView(java.util.Map, com.opensymphony.workflow.loader.AbstractDescriptor)
70 | */
71 | protected void getVelocityParamsForView(Map velocityParams, AbstractDescriptor descriptor) {
72 | ConditionDescriptor conditionDescriptor = (ConditionDescriptor) descriptor;
73 | Map args = conditionDescriptor.getArgs();
74 |
75 | String sField = (String) args.get("fieldsList");
76 |
77 | Field field = null;
78 |
79 | try {
80 | field = workflowUtils.getFieldFromKey(sField);
81 | } catch (Exception e) {
82 | }
83 |
84 | if (field != null) {
85 | velocityParams.put("val-fieldSelected", field);
86 | } else {
87 | velocityParams.put("val-errorMessage", "Unable to find field '" + sField + "'");
88 | }
89 |
90 | boolean allowUserInField = getAllowUserInField(args);
91 |
92 | velocityParams.put("allowUserInField-selected", allowUserInField);
93 | }
94 |
95 | /* (non-Javadoc)
96 | * @see com.googlecode.jsu.workflow.WorkflowPluginFactory#getDescriptorParams(java.util.Map)
97 | */
98 | public Map getDescriptorParams(Map conditionParams) {
99 | Map params = new HashMap();
100 |
101 | try {
102 | String field = extractSingleParam(conditionParams, "fieldsList");
103 | String allowUser = extractSingleParam(conditionParams, ALLOW_USER_IN_FIELD);
104 |
105 | params.put("fieldsList", field);
106 | params.put(ALLOW_USER_IN_FIELD, allowUser);
107 |
108 | } catch(IllegalArgumentException iae) {
109 | // Aggregate so that Transitions can be added.
110 | }
111 |
112 | return params;
113 | }
114 |
115 | /**
116 | * Get allowUserInField parameter. Not use default valueOf to store previous behavior.
117 | */
118 | public static boolean getAllowUserInField(Map args) {
119 | String param = (String) args.get(ALLOW_USER_IN_FIELD);
120 |
121 | if (param == null) {
122 | return true;
123 | } else {
124 | return Boolean.valueOf(param);
125 | }
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jsu/workflow/validator/DateCompareValidator.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsu.workflow.validator;
2 |
3 | import com.atlassian.jira.config.properties.ApplicationProperties;
4 | import com.atlassian.jira.issue.fields.Field;
5 | import com.atlassian.jira.util.I18nHelper;
6 | import com.googlecode.jsu.annotation.Argument;
7 | import com.googlecode.jsu.helpers.ComparisonType;
8 | import com.googlecode.jsu.helpers.ConditionChecker;
9 | import com.googlecode.jsu.helpers.ConditionCheckerFactory;
10 | import com.googlecode.jsu.helpers.ConditionType;
11 | import com.googlecode.jsu.util.FieldCollectionsUtils;
12 | import com.googlecode.jsu.util.WorkflowUtils;
13 | import com.opensymphony.workflow.InvalidInputException;
14 | import com.opensymphony.workflow.WorkflowException;
15 | import org.slf4j.Logger;
16 | import org.slf4j.LoggerFactory;
17 |
18 | import java.util.Calendar;
19 | import java.util.Date;
20 |
21 | import static com.googlecode.jsu.helpers.ConditionCheckerFactory.DATE;
22 | import static com.googlecode.jsu.helpers.ConditionCheckerFactory.DATE_WITHOUT_TIME;
23 |
24 | /**
25 | * This validator compare two datetime fields, using the given comparison type.
26 | * And returning an exception if it doesn't fulfill the condition.
27 | */
28 | public class DateCompareValidator extends AbstractDateCompareValidator {
29 | @Argument("date1Selected")
30 | private String date1;
31 |
32 | @Argument("date2Selected")
33 | private String date2;
34 |
35 | @Argument("conditionSelected")
36 | private String conditionId;
37 |
38 | @Argument("includeTimeSelected")
39 | private String includeTimeValue;
40 |
41 | private final Logger log = LoggerFactory.getLogger(DateCompareValidator.class);
42 |
43 | private final ApplicationProperties applicationProperties;
44 | private final ConditionCheckerFactory conditionCheckerFactory;
45 | private final I18nHelper.BeanFactory beanFactory;
46 |
47 | public DateCompareValidator(
48 | ApplicationProperties applicationProperties,
49 | ConditionCheckerFactory conditionCheckerFactory,
50 | FieldCollectionsUtils fieldCollectionsUtils,
51 | WorkflowUtils workflowUtils,
52 | I18nHelper.BeanFactory beanFactory
53 | ) {
54 | super(applicationProperties,fieldCollectionsUtils, workflowUtils,beanFactory);
55 |
56 | this.applicationProperties = applicationProperties;
57 | this.conditionCheckerFactory = conditionCheckerFactory;
58 | this.beanFactory = beanFactory;
59 | }
60 |
61 | /* (non-Javadoc)
62 | * @see com.googlecode.jsu.workflow.validator.GenericValidator#validate()
63 | */
64 | protected void validate() throws InvalidInputException, WorkflowException {
65 | Field field1 = workflowUtils.getFieldFromKey(date1);
66 | Field field2 = workflowUtils.getFieldFromKey(date2);
67 |
68 | ConditionType condition = conditionCheckerFactory.findConditionById(conditionId);
69 | boolean includeTime = Integer.parseInt(includeTimeValue) == 1;
70 |
71 | // Compare Dates.
72 | if ((field1 != null) && (field2 != null)) {
73 | Object objValue1 = workflowUtils.getFieldValueFromIssue(getIssue(), field1);
74 | Object objValue2 = workflowUtils.getFieldValueFromIssue(getIssue(), field2);
75 | Date objDate1, objDate2;
76 |
77 | try {
78 | objDate1 = (Date) objValue1;
79 | } catch (ClassCastException e) {
80 | wrongDataErrorMessage(field1, objValue1);
81 |
82 | return;
83 | }
84 |
85 | try {
86 | objDate2 = (Date) objValue2;
87 | } catch (ClassCastException e) {
88 | wrongDataErrorMessage(field2, objValue2);
89 |
90 | return;
91 | }
92 |
93 | if ((objDate1 != null) && (objDate2 != null)) {
94 | ComparisonType comparison = (includeTime) ? DATE : DATE_WITHOUT_TIME;
95 | ConditionChecker checker = conditionCheckerFactory.getChecker(comparison, condition);
96 |
97 | Calendar calDate1 = Calendar.getInstance(applicationProperties.getDefaultLocale());
98 | Calendar calDate2 = Calendar.getInstance(applicationProperties.getDefaultLocale());
99 |
100 | calDate1.setTime( objDate1);
101 | calDate2.setTime( objDate2);
102 |
103 | boolean result = checker.checkValues(calDate1, calDate2);
104 |
105 | if (log.isDebugEnabled()) {
106 | log.debug(
107 | "Compare field \"" + field1.getName() +
108 | "\" and field \"" + field2.getName() +
109 | "\" with values [" + calDate1 +
110 | "] and [" + calDate2 +
111 | "] with result " + result
112 | );
113 | }
114 |
115 | if (!result) {
116 | generateErrorMessage(field1, objDate1, field2.getName(), objDate2, condition, includeTime);
117 | }
118 | } else {
119 | // If any of fields are null, validates if the field is required. Otherwise, doesn't throws an Exception.
120 | if (objDate1 == null) {
121 | validateRequired(field1);
122 | }
123 |
124 | if (objDate2 == null) {
125 | validateRequired(field2);
126 | }
127 | }
128 | } else {
129 | log.error("Unable to find field with ids [" + date1 + "] and [" + date2 + "]");
130 | }
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jsu/workflow/WorkflowCopyValueFromOtherFieldPostFunctionPluginFactory.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsu.workflow;
2 |
3 | import java.util.*;
4 |
5 | import com.atlassian.jira.issue.fields.Field;
6 | import com.atlassian.jira.plugin.workflow.AbstractWorkflowPluginFactory;
7 | import com.atlassian.jira.plugin.workflow.WorkflowPluginFunctionFactory;
8 | import com.googlecode.jsu.util.FieldCollectionsUtils;
9 | import com.googlecode.jsu.util.WorkflowUtils;
10 | import com.opensymphony.workflow.loader.AbstractDescriptor;
11 | import com.opensymphony.workflow.loader.FunctionDescriptor;
12 |
13 | /**
14 | * This class defines the parameters available for Copy Value From Other Field Post Function.
15 | *
16 | * @author Gustavo Martin.
17 | */
18 | public class WorkflowCopyValueFromOtherFieldPostFunctionPluginFactory extends AbstractWorkflowPluginFactory implements WorkflowPluginFunctionFactory {
19 |
20 | public static final String PARAM_SOURCE_FIELD = "sourceField";
21 | public static final String PARAM_DEST_FIELD = "destinationField";
22 | public static final String PARAM_COPY_TYPE = "copyType";
23 | private static final String VALUE_SOURCE_LIST = "val-sourceFieldsList";
24 | private static final String VALUE_DEST_LIST = "val-destinationFieldsList";
25 | private static final String VALUE_SOURCE_SELECTED = "val-sourceFieldSelected";
26 | private static final String VALUE_DEST_SELECTED = "val-destinationFieldSelected";
27 | private static final String VALUE_COPY_TYPE = "val-copyType";
28 |
29 |
30 | private final FieldCollectionsUtils fieldCollectionsUtils;
31 | private final WorkflowUtils workflowUtils;
32 |
33 | public WorkflowCopyValueFromOtherFieldPostFunctionPluginFactory(
34 | FieldCollectionsUtils fieldCollectionsUtils,
35 | WorkflowUtils workflowUtils
36 | ) {
37 | this.fieldCollectionsUtils = fieldCollectionsUtils;
38 | this.workflowUtils = workflowUtils;
39 | }
40 |
41 | /* (non-Javadoc)
42 | * @see com.googlecode.jsu.workflow.AbstractWorkflowPluginFactory#getVelocityParamsForInput(java.util.Map)
43 | */
44 | protected void getVelocityParamsForInput(Map velocityParams) {
45 | List sourceFields = fieldCollectionsUtils.getFieldContainers(fieldCollectionsUtils.getCopyFromFields());
46 | List destinationFields = fieldCollectionsUtils.getFieldContainers(fieldCollectionsUtils.getCopyToFields());
47 |
48 | velocityParams.put(VALUE_SOURCE_LIST, Collections.unmodifiableList(sourceFields));
49 | velocityParams.put(VALUE_DEST_LIST, Collections.unmodifiableList(destinationFields));
50 | }
51 |
52 | /* (non-Javadoc)
53 | * @see com.googlecode.jsu.workflow.AbstractWorkflowPluginFactory#getVelocityParamsForEdit(java.util.Map, com.opensymphony.workflow.loader.AbstractDescriptor)
54 | */
55 | protected void getVelocityParamsForEdit(Map velocityParams, AbstractDescriptor descriptor) {
56 | getVelocityParamsForInput(velocityParams);
57 |
58 | Field sourceFieldId = workflowUtils.getFieldFromDescriptor(descriptor, PARAM_SOURCE_FIELD);
59 | Field destinationField = workflowUtils.getFieldFromDescriptor(descriptor, PARAM_DEST_FIELD);
60 |
61 | velocityParams.put(VALUE_SOURCE_SELECTED, sourceFieldId);
62 | velocityParams.put(VALUE_DEST_SELECTED, destinationField);
63 |
64 | FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor;
65 | velocityParams.put(VALUE_COPY_TYPE,getCopyType(functionDescriptor));
66 | }
67 |
68 | /* (non-Javadoc)
69 | * @see com.googlecode.jsu.workflow.AbstractWorkflowPluginFactory#getVelocityParamsForView(java.util.Map, com.opensymphony.workflow.loader.AbstractDescriptor)
70 | */
71 | protected void getVelocityParamsForView(Map velocityParams, AbstractDescriptor descriptor) {
72 | Field sourceFieldId = workflowUtils.getFieldFromDescriptor(descriptor, PARAM_SOURCE_FIELD);
73 | Field destinationField = workflowUtils.getFieldFromDescriptor(descriptor, PARAM_DEST_FIELD);
74 |
75 | velocityParams.put(VALUE_SOURCE_SELECTED, sourceFieldId);
76 | velocityParams.put(VALUE_DEST_SELECTED, destinationField);
77 |
78 | FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor;
79 | velocityParams.put(VALUE_COPY_TYPE,getCopyType(functionDescriptor));
80 | }
81 |
82 | /* (non-Javadoc)
83 | * @see com.googlecode.jsu.workflow.WorkflowPluginFactory#getDescriptorParams(java.util.Map)
84 | */
85 | public Map getDescriptorParams(Map conditionParams) {
86 | Map params = new HashMap();
87 |
88 | try{
89 | String sourceField = extractSingleParam(conditionParams, "sourceFieldsList");
90 | String destinationField = extractSingleParam(conditionParams, "destinationFieldsList");
91 | String copyType = extractSingleParam(conditionParams, PARAM_COPY_TYPE);
92 |
93 | params.put(PARAM_SOURCE_FIELD, sourceField);
94 | params.put(PARAM_DEST_FIELD, destinationField);
95 | params.put(PARAM_COPY_TYPE, copyType);
96 | } catch (IllegalArgumentException iae) {
97 | // Aggregate so that Transitions can be added.
98 | }
99 |
100 | return params;
101 | }
102 |
103 | private String getCopyType(FunctionDescriptor functionDescriptor) {
104 | String value = (String) functionDescriptor.getArgs().get(PARAM_COPY_TYPE);
105 |
106 | if (value == null || value.equals("null")) {
107 | return "same";
108 | } else {
109 | return value;
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jsu/workflow/validator/AbstractDateCompareValidator.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsu.workflow.validator;
2 |
3 | import com.atlassian.jira.component.ComponentAccessor;
4 | import com.atlassian.jira.config.properties.APKeys;
5 | import com.atlassian.jira.config.properties.ApplicationProperties;
6 | import com.atlassian.jira.issue.Issue;
7 | import com.atlassian.jira.issue.fields.Field;
8 | import com.atlassian.jira.util.I18nHelper;
9 | import com.googlecode.jsu.helpers.ConditionType;
10 | import com.googlecode.jsu.util.FieldCollectionsUtils;
11 | import com.googlecode.jsu.util.WorkflowUtils;
12 | import com.opensymphony.workflow.InvalidInputException;
13 | import com.opensymphony.workflow.WorkflowException;
14 |
15 | import java.text.SimpleDateFormat;
16 |
17 | /**
18 | * Abstract base class for date validators.
19 | */
20 | public abstract class AbstractDateCompareValidator extends GenericValidator {
21 |
22 | private final ApplicationProperties applicationProperties;
23 | private final I18nHelper.BeanFactory beanFactory;
24 |
25 | public AbstractDateCompareValidator (
26 | ApplicationProperties applicationProperties,
27 | FieldCollectionsUtils fieldCollectionsUtils,
28 | WorkflowUtils workflowUtils,
29 | I18nHelper.BeanFactory beanFactory
30 | ) {
31 | super(fieldCollectionsUtils, workflowUtils);
32 |
33 | this.applicationProperties = applicationProperties;
34 | this.beanFactory = beanFactory;
35 | }
36 |
37 | protected abstract void validate() throws InvalidInputException, WorkflowException;
38 |
39 | /**
40 | * @param fldDate
41 | *
42 | * Throws an Exception if the field is null, but it is required.
43 | */
44 | protected void validateRequired(Field fldDate) {
45 | final Issue issue = getIssue();
46 |
47 | if (fieldCollectionsUtils.isFieldRequired(issue, fldDate)) {
48 | String msg = this.beanFactory.getInstance(
49 | ComponentAccessor.getJiraAuthenticationContext().getUser().getDirectoryUser())
50 | .getText("datecompare-validator-view.is_required", fldDate.getName());
51 | this.setExceptionMessage(
52 | fldDate,
53 | msg,
54 | msg
55 | );
56 | }
57 | }
58 |
59 | /**
60 | * @param dateField The date field to which the expression belongs to.
61 | * @param expression The expression value.
62 | *
63 | * Throws an Exception, because given expression is invalid (null or syntax)
64 | */
65 | protected void invalidExpression(Field dateField, String expression) {
66 | String msg = this.beanFactory.getInstance(
67 | ComponentAccessor.getJiraAuthenticationContext().getUser().getDirectoryUser())
68 | .getText("dateexpressioncompare-validator-view.is_invalid", expression);
69 | this.setExceptionMessage(
70 | dateField,
71 | msg,
72 | msg
73 | );
74 | }
75 |
76 | protected void wrongDataErrorMessage(
77 | Field field, Object fieldValue
78 | ) {
79 | String msg = this.beanFactory.getInstance(
80 | ComponentAccessor.getJiraAuthenticationContext().getUser().getDirectoryUser())
81 | .getText("datecompare-validator-view.not_a_date",field.getName(),fieldValue.toString());
82 | this.setExceptionMessage(
83 | field,
84 | msg,
85 | msg
86 | );
87 | }
88 |
89 | protected void generateErrorMessage(
90 | Field field1, Object fieldValue1,
91 | String nameOrValue, Object fieldValue2,
92 | ConditionType condition, boolean includeTime
93 | ) {
94 | // Formats date to current locale to display the Exception.
95 | SimpleDateFormat formatter;
96 | SimpleDateFormat defaultFormatter;
97 |
98 | if (includeTime) {
99 | defaultFormatter = new SimpleDateFormat(
100 | applicationProperties.getDefaultString(APKeys.JIRA_DATE_PICKER_JAVA_FORMAT)
101 | );
102 | formatter = new SimpleDateFormat(
103 | applicationProperties.getDefaultString(APKeys.JIRA_DATE_PICKER_JAVA_FORMAT),
104 | applicationProperties.getDefaultLocale()
105 | );
106 | }else{
107 | defaultFormatter = new SimpleDateFormat(
108 | applicationProperties.getDefaultString(APKeys.JIRA_DATE_TIME_PICKER_JAVA_FORMAT)
109 | );
110 | formatter = new SimpleDateFormat(
111 | applicationProperties.getDefaultString(APKeys.JIRA_DATE_TIME_PICKER_JAVA_FORMAT),
112 | applicationProperties.getDefaultLocale()
113 | );
114 | }
115 |
116 | String errorMsg;
117 |
118 | try{
119 | errorMsg = " ( " + formatter.format(fieldValue2) + " )";
120 | } catch (IllegalArgumentException e) {
121 | try {
122 | errorMsg = " ( " + defaultFormatter.format(fieldValue2) + " )";
123 | } catch(Exception e1) {
124 | errorMsg = " ( " + fieldValue2 + " )";
125 | }
126 | }
127 |
128 | I18nHelper i18nh = this.beanFactory.getInstance(
129 | ComponentAccessor.getJiraAuthenticationContext().getUser().getDirectoryUser());
130 | String msg = i18nh.getText("datecompare-validator-view.is_not",
131 | field1.getName(),i18nh.getText(condition.getDisplayTextKey()),nameOrValue==null?"":nameOrValue,errorMsg);
132 |
133 | this.setExceptionMessage(
134 | field1,
135 | msg,
136 | msg
137 | );
138 | }
139 | }
140 |
--------------------------------------------------------------------------------