1 package org.argeo.tracker.core;
2
3 import static org.argeo.activities.ActivitiesNames.ACTIVITIES_DUE_DATE;
4 import static org.argeo.eclipse.ui.EclipseUiUtils.notEmpty;
5
6 import java.text.DateFormat;
7 import java.text.SimpleDateFormat;
8 import java.util.ArrayList;
9 import java.util.Calendar;
10 import java.util.Collections;
11 import java.util.GregorianCalendar;
12 import java.util.HashMap;
13 import java.util.LinkedHashMap;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.Map.Entry;
17 import java.util.stream.Collectors;
18
19 import javax.jcr.Node;
20 import javax.jcr.NodeIterator;
21 import javax.jcr.Property;
22 import javax.jcr.RepositoryException;
23 import javax.jcr.Session;
24 import javax.jcr.query.Query;
25 import javax.jcr.query.QueryManager;
26 import javax.jcr.query.QueryResult;
27
28 import org.argeo.activities.ActivitiesException;
29 import org.argeo.activities.ActivitiesNames;
30 import org.argeo.activities.ActivitiesService;
31 import org.argeo.activities.ActivitiesTypes;
32 import org.argeo.connect.AppService;
33 import org.argeo.connect.ConnectConstants;
34 import org.argeo.connect.ConnectNames;
35 import org.argeo.connect.UserAdminService;
36 import org.argeo.connect.util.ConnectJcrUtils;
37 import org.argeo.connect.util.ConnectUtils;
38 import org.argeo.connect.util.XPathUtils;
39 import org.argeo.eclipse.ui.EclipseUiUtils;
40 import org.argeo.tracker.TrackerConstants;
41 import org.argeo.tracker.TrackerException;
42 import org.argeo.tracker.TrackerNames;
43 import org.argeo.tracker.TrackerService;
44 import org.argeo.tracker.TrackerTypes;
45
46
47 public class TrackerUtils {
48
49 public static final Map<String, String> DEFAULT_COMPONENTS;
50 static {
51 Map<String, String> tmpMap = new LinkedHashMap<String, String>();
52 tmpMap.put("Model", "Specification, design and data model");
53 tmpMap.put("Backend", "Core components");
54 tmpMap.put("Demo", "A basic instance for this project that can be freely shown");
55 tmpMap.put("UI", "The user interface");
56 tmpMap.put("Documentation", "Reference documentation");
57 tmpMap.put("Integration", "Import, export, external APIs");
58 tmpMap.put("QA", "Build, deployment, testing, documentation");
59 DEFAULT_COMPONENTS = Collections.unmodifiableMap(tmpMap);
60 }
61
62
63 public static final Map<String, String> MAPS_ISSUE_PRIORITIES;
64 static {
65 Map<String, String> tmpMap = new LinkedHashMap<String, String>();
66 tmpMap.put(TrackerConstants.TRACKER_PRIORITY_LOWEST + "", "Lowest");
67 tmpMap.put(TrackerConstants.TRACKER_PRIORITY_LOW + "", "Low");
68 tmpMap.put(TrackerConstants.TRACKER_PRIORITY_NORMAL + "", "Normal");
69 tmpMap.put(TrackerConstants.TRACKER_PRIORITY_HIGH + "", "High");
70 tmpMap.put(TrackerConstants.TRACKER_PRIORITY_HIGHEST + "", "Highest");
71 MAPS_ISSUE_PRIORITIES = Collections.unmodifiableMap(tmpMap);
72 }
73
74 public static final Map<String, String> MAPS_ISSUE_IMPORTANCES;
75 static {
76 Map<String, String> tmpMap = new LinkedHashMap<String, String>();
77 tmpMap.put(TrackerConstants.TRACKER_IMPORTANCE_ENHANCEMENT + "", "Enhancement");
78 tmpMap.put(TrackerConstants.TRACKER_IMPORTANCE_TRIVIAL + "", "Trivial");
79 tmpMap.put(TrackerConstants.TRACKER_IMPORTANCE_MINOR + "", "Minor");
80 tmpMap.put(TrackerConstants.TRACKER_IMPORTANCE_NORMAL + "", "Normal");
81 tmpMap.put(TrackerConstants.TRACKER_IMPORTANCE_MAJOR + "", "Major");
82 tmpMap.put(TrackerConstants.TRACKER_IMPORTANCE_CRITICAL + "", "Critical");
83 tmpMap.put(TrackerConstants.TRACKER_IMPORTANCE_BLOCKER + "", "Blocker");
84 MAPS_ISSUE_IMPORTANCES = Collections.unmodifiableMap(tmpMap);
85 }
86
87 public static String issuesRelPath() {
88 return TrackerNames.TRACKER_ISSUES;
89 }
90
91 public static String componentsRelPath() {
92 return TrackerNames.TRACKER_COMPONENTS;
93 }
94
95 public static String versionsRelPath() {
96 return TrackerNames.TRACKER_MILESTONES;
97 }
98
99 public static String getRelevantPropName(Node category) {
100 try {
101 if (category.isNodeType(TrackerTypes.TRACKER_COMPONENT))
102 return TrackerNames.TRACKER_COMPONENT_IDS;
103 else if (category.isNodeType(TrackerTypes.TRACKER_MILESTONE))
104 return TrackerNames.TRACKER_MILESTONE_UID;
105 else if (category.isNodeType(TrackerTypes.TRACKER_VERSION)) {
106
107 if (category.hasProperty(TrackerNames.TRACKER_RELEASE_DATE))
108 return TrackerNames.TRACKER_VERSION_IDS;
109 else
110 return TrackerNames.TRACKER_MILESTONE_UID;
111 } else
112 throw new TrackerException("Unsupported category node type " + category.getMixinNodeTypes().toString()
113 + " for " + category.getPath());
114 } catch (RepositoryException e) {
115 throw new TrackerException("Cannot get relevant property name for category " + category, e);
116 }
117 }
118
119
120 public static NodeIterator getProjects(Session session, String projectParentPath) {
121 try {
122 StringBuilder builder = new StringBuilder();
123 if (EclipseUiUtils.notEmpty(projectParentPath))
124 builder.append(XPathUtils.descendantFrom(projectParentPath));
125 builder.append("//element(*, ").append(TrackerTypes.TRACKER_PROJECT).append(")");
126 builder.append(" order by @").append(Property.JCR_TITLE);
127 QueryResult result = XPathUtils.createQuery(session, builder.toString()).execute();
128 return result.getNodes();
129 } catch (RepositoryException e) {
130 throw new TrackerException(
131 "Unable to get projects under " + projectParentPath + " for session " + session.getUserID(), e);
132 }
133 }
134
135 public static NodeIterator getOpenMilestones(Node project, String filter) {
136 try {
137 StringBuilder builder = new StringBuilder();
138 builder.append(XPathUtils.descendantFrom(project.getPath()));
139 builder.append("//element(*, ").append(TrackerTypes.TRACKER_MILESTONE).append(")");
140 builder.append("[not(@").append(ConnectNames.CONNECT_CLOSE_DATE).append(")");
141 if (EclipseUiUtils.notEmpty(filter))
142 builder.append(" and ").append(XPathUtils.getFreeTextConstraint(filter));
143 builder.append("]");
144 builder.append(" order by @").append(Property.JCR_TITLE).append(" ascending");
145 QueryResult result = XPathUtils.createQuery(project.getSession(), builder.toString()).execute();
146 return result.getNodes();
147 } catch (RepositoryException e) {
148 throw new TrackerException("Unable to get milestones on " + project + " with filter:" + filter, e);
149 }
150 }
151
152
153
154
155
156
157 public static NodeIterator getMilestones(Node project, String filter) {
158 try {
159 StringBuilder builder = new StringBuilder();
160 Node parent = project.getNode(versionsRelPath());
161 builder.append(XPathUtils.descendantFrom(parent.getPath()));
162 builder.append("//element(*, ").append(TrackerTypes.TRACKER_VERSION).append(")");
163 builder.append("[not(@").append(TrackerNames.TRACKER_RELEASE_DATE).append(")");
164 if (EclipseUiUtils.notEmpty(filter))
165 builder.append(" and ").append(XPathUtils.getFreeTextConstraint(filter));
166 builder.append("]");
167 builder.append(" order by @").append(TrackerNames.TRACKER_ID).append(" descending");
168 QueryResult result = XPathUtils.createQuery(parent.getSession(), builder.toString()).execute();
169 return result.getNodes();
170 } catch (RepositoryException e) {
171 throw new TrackerException("Unable to get milestones on " + project + " with filter:" + filter, e);
172 }
173 }
174
175 public static List<String> getMilestoneIds(Node project, String filter) {
176 NodeIterator nit = getMilestones(project, filter);
177 List<String> milestoneIds = new ArrayList<String>();
178 while (nit.hasNext()) {
179 Node currNode = nit.nextNode();
180 milestoneIds.add(ConnectJcrUtils.get(currNode, TrackerNames.TRACKER_ID));
181 }
182 return milestoneIds;
183 }
184
185 public static NodeIterator getVersions(Node project, String filter) throws RepositoryException {
186 StringBuilder builder = new StringBuilder();
187
188 builder.append(XPathUtils.descendantFrom(project.getPath()));
189 builder.append("//element(*, ").append(TrackerTypes.TRACKER_VERSION).append(")");
190 builder.append("[@").append(TrackerNames.TRACKER_RELEASE_DATE);
191 if (EclipseUiUtils.notEmpty(filter))
192 builder.append(" and ").append(XPathUtils.getFreeTextConstraint(filter));
193 builder.append("]");
194 builder.append(" order by @").append(TrackerNames.TRACKER_ID).append(" descending");
195 QueryResult result = XPathUtils.createQuery(project.getSession(), builder.toString()).execute();
196 return result.getNodes();
197 }
198
199 public static List<String> getVersionIds(Node project, String filter) {
200 try {
201 NodeIterator nit = getVersions(project, filter);
202 List<String> versionIds = new ArrayList<String>();
203 while (nit.hasNext()) {
204 Node currNode = nit.nextNode();
205 versionIds.add(ConnectJcrUtils.get(currNode, TrackerNames.TRACKER_ID));
206 }
207 return versionIds;
208 } catch (RepositoryException e) {
209 throw new TrackerException("Unable to get version ids on " + project + " with filter:" + filter, e);
210 }
211 }
212
213 public static NodeIterator getTasks(Node project, String filter) {
214 try {
215 StringBuilder builder = new StringBuilder();
216 builder.append(XPathUtils.descendantFrom(project.getPath()));
217 builder.append("//element(*, ").append(TrackerTypes.TRACKER_TASK).append(")");
218 if (EclipseUiUtils.notEmpty(filter))
219 builder.append("[").append(XPathUtils.getFreeTextConstraint(filter)).append("]");
220 builder.append(" order by @").append(TrackerNames.TRACKER_ID);
221 QueryResult result = XPathUtils.createQuery(project.getSession(), builder.toString()).execute();
222 return result.getNodes();
223 } catch (RepositoryException e) {
224 throw new TrackerException("Unable to get issues for " + project + " with filter: " + filter, e);
225 }
226 }
227
228 public static NodeIterator getIssues(Node project, String filter) {
229 try {
230 StringBuilder builder = new StringBuilder();
231 Node parent = project.getNode(issuesRelPath());
232 builder.append(XPathUtils.descendantFrom(parent.getPath()));
233 builder.append("//element(*, ").append(TrackerTypes.TRACKER_ISSUE).append(")");
234 if (EclipseUiUtils.notEmpty(filter))
235 builder.append("[").append(XPathUtils.getFreeTextConstraint(filter)).append("]");
236 builder.append(" order by @").append(TrackerNames.TRACKER_ID);
237
238 QueryManager qm = project.getSession().getWorkspace().getQueryManager();
239 QueryResult result = qm.createQuery(builder.toString(), ConnectConstants.QUERY_XPATH).execute();
240 return result.getNodes();
241 } catch (RepositoryException e) {
242 throw new TrackerException("Unable to get issues for " + project + " with filter: " + filter, e);
243 }
244 }
245
246
247
248
249
250 public static NodeIterator getIssues(Node project, String filter, String propName, String catId) {
251 return getIssues(project, filter, propName, catId, false);
252 }
253
254
255 public static boolean isIssueClosed(Node issue) {
256 try {
257
258 return issue.hasProperty(ConnectNames.CONNECT_CLOSE_DATE);
259 } catch (RepositoryException e) {
260 throw new TrackerException("Unable to check closed status of " + issue, e);
261 }
262 }
263
264 public static NodeIterator getIssues(Node project, String filter, String propName, String catId,
265 boolean onlyOpenTasks) {
266 try {
267 StringBuilder builder = new StringBuilder();
268
269 builder.append(XPathUtils.descendantFrom(project.getPath()));
270 builder.append("//element(*, ").append(TrackerTypes.TRACKER_TASK).append(")");
271
272 StringBuilder tmpBuilder = new StringBuilder();
273
274 String andStr = " and ";
275 if (EclipseUiUtils.notEmpty(catId)) {
276 tmpBuilder.append(XPathUtils.getPropertyEquals(propName, catId));
277 tmpBuilder.append(andStr);
278 }
279
280 if (EclipseUiUtils.notEmpty(filter)) {
281 tmpBuilder.append(XPathUtils.getFreeTextConstraint(filter));
282 tmpBuilder.append(andStr);
283 }
284
285 if (onlyOpenTasks) {
286 tmpBuilder.append(" not(@");
287 tmpBuilder.append(ConnectNames.CONNECT_CLOSE_DATE);
288 tmpBuilder.append(")");
289 tmpBuilder.append(andStr);
290 }
291
292 if (tmpBuilder.length() > 0)
293 builder.append("[").append(tmpBuilder.substring(0, tmpBuilder.length() - andStr.length())).append("]");
294
295 builder.append(" order by @" + TrackerNames.TRACKER_ID);
296 Query xpathQuery = XPathUtils.createQuery(project.getSession(), builder.toString());
297 QueryResult result = xpathQuery.execute();
298 return result.getNodes();
299 } catch (RepositoryException e) {
300 throw new TrackerException("Unable to get issues for " + project + " with filter: " + filter, e);
301 }
302 }
303
304 private final static String UNKNOWN = "Unknown";
305 private final static String OTHERS = "Others";
306
307 public static Map<String, String> getOpenTasksByAssignee(UserAdminService uas, Node project, String milestoneUid,
308 int maxSize) {
309 try {
310 StringBuilder builder = new StringBuilder();
311 builder.append(XPathUtils.descendantFrom(project.getPath()));
312 builder.append("//element(*, ").append(TrackerTypes.TRACKER_TASK).append(")");
313 builder.append("[");
314 if (EclipseUiUtils.notEmpty(milestoneUid)) {
315 builder.append(XPathUtils.getPropertyEquals(TrackerNames.TRACKER_MILESTONE_UID, milestoneUid));
316 builder.append(" and ");
317 }
318 builder.append("( not(@").append(ConnectNames.CONNECT_CLOSE_DATE).append("))");
319 builder.append("]");
320 QueryResult result = XPathUtils.createQuery(project.getSession(), builder.toString()).execute();
321
322 Map<String, Long> nodeCount = countAndLimit(result.getNodes(), ActivitiesNames.ACTIVITIES_ASSIGNED_TO,
323 maxSize);
324 Map<String, String> openTasks = new LinkedHashMap<String, String>();
325 for (String key : nodeCount.keySet()) {
326 String dName;
327 if (OTHERS.equals(key) || UNKNOWN.equals(key))
328 dName = key;
329 else
330 dName = uas.getUserDisplayName(key);
331 openTasks.put(dName, nodeCount.get(key).toString());
332 }
333 return openTasks;
334 } catch (RepositoryException e) {
335 throw new TrackerException("Unable to get issues for " + project + " with filter: ", e);
336 }
337 }
338
339 private static Map<String, Long> countAndLimit(NodeIterator nodes, String propName, int limit) {
340
341 Map<String, Long> nodeCount = new HashMap<>();
342 while (nodes.hasNext()) {
343 Node currNode = nodes.nextNode();
344 String id = ConnectJcrUtils.get(currNode, propName);
345 if (EclipseUiUtils.isEmpty(id))
346 id = UNKNOWN;
347 if (nodeCount.containsKey(id))
348 nodeCount.replace(id, nodeCount.get(id) + 1);
349 else
350 nodeCount.put(id, 1l);
351 }
352
353 if (nodeCount.size() < limit)
354 return nodeCount;
355
356 Map<String, Long> orderedCount = sortByValue(nodeCount);
357 Map<String, Long> limitedCount = new LinkedHashMap<String, Long>();
358
359 int i = 0;
360 long otherCount = 0;
361 for (String key : orderedCount.keySet()) {
362 if (i < limit)
363 limitedCount.put(key, orderedCount.get(key));
364 else
365 otherCount += orderedCount.get(key);
366 i++;
367 }
368 limitedCount.put(OTHERS, otherCount);
369 return limitedCount;
370 }
371
372 private static Map<String, Long> sortByValue(Map<String, Long> map) {
373 return map.entrySet().stream().sorted(Map.Entry.comparingByValue(Collections.reverseOrder()))
374 .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
375 }
376
377 public static NodeIterator getAllVersions(Node project, String filter) {
378 try {
379 StringBuilder builder = new StringBuilder();
380 Node parent = project.getNode(versionsRelPath());
381 builder.append(XPathUtils.descendantFrom(parent.getPath()));
382 builder.append("//element(*, ").append(TrackerTypes.TRACKER_VERSION).append(")");
383 if (EclipseUiUtils.notEmpty(filter))
384 builder.append("[").append(XPathUtils.getFreeTextConstraint(filter)).append("]");
385 builder.append(" order by @").append(TrackerNames.TRACKER_ID).append(" descending");
386 QueryManager qm = parent.getSession().getWorkspace().getQueryManager();
387 QueryResult result = qm.createQuery(builder.toString(), ConnectConstants.QUERY_XPATH).execute();
388 return result.getNodes();
389 } catch (RepositoryException e) {
390 throw new TrackerException("Unable to get version for " + project + " with filter: " + filter, e);
391 }
392 }
393
394 public static List<String> getComponentIds(Node project, String filter) {
395 NodeIterator nit = getComponents(project, filter);
396 List<String> componentIds = new ArrayList<String>();
397 while (nit.hasNext()) {
398 Node currNode = nit.nextNode();
399 componentIds.add(ConnectJcrUtils.get(currNode, TrackerNames.TRACKER_ID));
400 }
401 return componentIds;
402 }
403
404 public static NodeIterator getComponents(Node project, String filter) {
405 try {
406 StringBuilder builder = new StringBuilder();
407 builder.append(XPathUtils.descendantFrom(project.getPath()));
408 builder.append("//element(*, ").append(TrackerTypes.TRACKER_COMPONENT).append(")");
409 if (EclipseUiUtils.notEmpty(filter))
410 builder.append("[").append(XPathUtils.getFreeTextConstraint(filter)).append("]");
411 builder.append(" order by @").append(TrackerNames.TRACKER_ID).append(" ascending");
412 QueryResult result = XPathUtils.createQuery(project.getSession(), builder.toString()).execute();
413 return result.getNodes();
414 } catch (RepositoryException e) {
415 throw new TrackerException("Unable to get components for " + project + " with filter: " + filter, e);
416 }
417 }
418
419 public static Node getVersionById(Node project, String versionId) {
420 try {
421
422 String xpathQueryStr = XPathUtils.descendantFrom(project.getPath());
423 xpathQueryStr += "//element(*, " + TrackerTypes.TRACKER_VERSION + ")";
424 xpathQueryStr += "[" + XPathUtils.getPropertyEquals(TrackerNames.TRACKER_ID, versionId) + "]";
425 Query xpathQuery = XPathUtils.createQuery(project.getSession(), xpathQueryStr);
426 NodeIterator results = xpathQuery.execute().getNodes();
427 if (!results.hasNext())
428 return null;
429 else if (results.getSize() > 1)
430 throw new TrackerException(
431 "Found " + results.getSize() + " versions with Id " + versionId + " under " + project);
432 else
433 return results.nextNode();
434 } catch (RepositoryException e) {
435 throw new TrackerException("Unable to get version " + versionId + " under " + project, e);
436 }
437 }
438
439 public static Node getComponentById(Node project, String officeId) {
440 try {
441 Node parent = project.getNode(componentsRelPath());
442 String xpathQueryStr = XPathUtils.descendantFrom(parent.getPath());
443 xpathQueryStr += "//element(*, " + TrackerTypes.TRACKER_COMPONENT + ")";
444 xpathQueryStr += "[" + XPathUtils.getPropertyEquals(TrackerNames.TRACKER_ID, officeId) + "]";
445 QueryManager qm = parent.getSession().getWorkspace().getQueryManager();
446 Query xpathQuery = qm.createQuery(xpathQueryStr, ConnectConstants.QUERY_XPATH);
447 NodeIterator results = xpathQuery.execute().getNodes();
448 if (!results.hasNext())
449 return null;
450 else if (results.getSize() > 1)
451 throw new TrackerException(
452 "Found " + results.getSize() + " versions with Id " + officeId + " under " + project);
453 else
454 return results.nextNode();
455 } catch (RepositoryException e) {
456 throw new TrackerException("Unable to get component " + officeId + " under " + project, e);
457 }
458 }
459
460 public static long getIssueNb(Node category, boolean onlyOpen) {
461 Node project = getProjectFromChild(category);
462 String relProp = getRelevantPropName(category);
463 NodeIterator nit = getIssues(project, null, relProp, ConnectJcrUtils.get(category, TrackerNames.TRACKER_ID),
464 onlyOpen);
465 return nit.getSize();
466 }
467
468 public static Node getRelatedProject(AppService appService, Node node) {
469 String refUid = ConnectJcrUtils.get(node, TrackerNames.TRACKER_PROJECT_UID);
470 if (EclipseUiUtils.notEmpty(refUid))
471 return appService.getEntityByUid(ConnectJcrUtils.getSession(node), null, refUid);
472 else
473 return null;
474 }
475
476 public static Node getMilestone(AppService appService, Node task) {
477 String muid = ConnectJcrUtils.get(task, TrackerNames.TRACKER_MILESTONE_UID);
478 if (EclipseUiUtils.notEmpty(muid))
479 return appService.getEntityByUid(ConnectJcrUtils.getSession(task), null, muid);
480 return null;
481 }
482
483 public static Node getProjectFromChild(Node issue) {
484 try {
485
486
487 Node parent = issue;
488 while (!parent.isNodeType(TrackerTypes.TRACKER_PROJECT))
489 parent = parent.getParent();
490 return parent;
491 } catch (RepositoryException e) {
492 throw new TrackerException("Unable to get project for " + issue, e);
493 }
494 }
495
496 public static String getImportanceLabel(Node issue) {
497 Long importance = ConnectJcrUtils.getLongValue(issue, TrackerNames.TRACKER_IMPORTANCE);
498 if (importance != null)
499 return TrackerUtils.MAPS_ISSUE_IMPORTANCES.get(importance.toString());
500 else
501 return "";
502
503
504
505
506
507
508
509
510
511 }
512
513 public static String getPriorityLabel(Node issue) {
514 Long priority = ConnectJcrUtils.getLongValue(issue, TrackerNames.TRACKER_PRIORITY);
515 if (priority != null)
516 return TrackerUtils.MAPS_ISSUE_PRIORITIES.get(priority.toString());
517 else
518 return "";
519
520
521
522
523
524
525
526
527
528 }
529
530 public static String getCreationLabel(UserAdminService userAdminService, Node issue) {
531 try {
532 String result = "";
533 if (issue.hasProperty(Property.JCR_CREATED_BY)) {
534 String userId = issue.getProperty(Property.JCR_CREATED_BY).getString();
535 String displayName = userAdminService.getUserDisplayName(userId);
536 if (EclipseUiUtils.notEmpty(displayName))
537 result = displayName;
538 }
539
540 if (issue.hasProperty(Property.JCR_CREATED)) {
541 result += " on " + ConnectJcrUtils.getDateFormattedAsString(issue, Property.JCR_CREATED,
542 ConnectConstants.DEFAULT_DATE_TIME_FORMAT);
543 }
544 return result;
545 } catch (RepositoryException e) {
546 throw new TrackerException("Unable to get creation label for " + issue, e);
547 }
548 }
549
550 private static DateFormat dtFormat = new SimpleDateFormat(ConnectConstants.DEFAULT_DATE_TIME_FORMAT);
551
552 public static String getStatusText(UserAdminService userAdminService, ActivitiesService activityService,
553 Node issue) {
554 try {
555 StringBuilder builder = new StringBuilder();
556
557
558 builder.append("<b> Status: </b>")
559 .append(ConnectJcrUtils.get(issue, ActivitiesNames.ACTIVITIES_TASK_STATUS)).append(" ");
560 builder.append("[").append(TrackerUtils.getImportanceLabel(issue)).append("/")
561 .append(TrackerUtils.getPriorityLabel(issue)).append("] - ");
562
563
564 Node milestone = TrackerUtils.getMilestone(activityService, issue);
565 if (milestone != null)
566 builder.append("<b>Target milestone: </b> ").append(ConnectJcrUtils.get(milestone, Property.JCR_TITLE))
567 .append(" - ");
568 String versionId = ConnectJcrUtils.getMultiAsString(issue, TrackerNames.TRACKER_VERSION_IDS, ", ");
569 builder.append(" - ");
570 if (notEmpty(versionId))
571 builder.append("<b>Affect version: </b> ").append(versionId).append(" - ");
572
573
574 if (activityService.isTaskDone(issue)) {
575 String closeBy = ConnectJcrUtils.get(issue, ConnectNames.CONNECT_CLOSED_BY);
576 Calendar closedDate = issue.getProperty(ConnectNames.CONNECT_CLOSE_DATE).getDate();
577 builder.append(" - Marked as closed by ").append(closeBy);
578 builder.append(" on ").append(dtFormat.format(closedDate.getTime())).append(".");
579 } else {
580 String assignedToId = ConnectJcrUtils.get(issue, ActivitiesNames.ACTIVITIES_ASSIGNED_TO);
581 String dName = userAdminService.getUserDisplayName(assignedToId);
582 if (notEmpty(dName))
583 builder.append("<b>Assigned to: </b>").append(dName);
584 }
585 return ConnectUtils.replaceAmpersand(builder.toString());
586 } catch (RepositoryException e) {
587 throw new TrackerException("Unable to get status text for issue " + issue, e);
588 }
589 }
590
591
592 public static void createDefaultComponents(TrackerService issueService, Node parentProject)
593 throws RepositoryException {
594 for (String title : DEFAULT_COMPONENTS.keySet()) {
595 issueService.createComponent(parentProject, title, title, DEFAULT_COMPONENTS.get(title));
596 }
597 }
598
599
600
601
602
603 public static <T, E> T getKeyByValue(Map<T, E> map, E value) {
604 for (Entry<T, E> entry : map.entrySet()) {
605 if (value.equals(entry.getValue())) {
606 return entry.getKey();
607 }
608 }
609 return null;
610 }
611
612 public static String normalizeDn(String dn) {
613
614 String lowerCased = dn.replaceAll("UID=", "uid=").replaceAll("CN=", "cn=").replaceAll("DC=", "dc=")
615 .replaceAll("OU=", "ou=").replaceAll(", ", ",");
616 return lowerCased;
617 }
618
619 public static long getProjectOverdueTasksNumber(Node project) {
620 StringBuilder builder = new StringBuilder();
621 try {
622 builder.append(XPathUtils.descendantFrom(project.getPath()));
623 builder.append("//element(*, ").append(ActivitiesTypes.ACTIVITIES_TASK).append(")");
624
625 Calendar now = GregorianCalendar.getInstance();
626 String overdueCond = XPathUtils.getPropertyDateComparaison(ACTIVITIES_DUE_DATE, now, "<");
627
628 String notClosedCond = "not(@" + ConnectNames.CONNECT_CLOSE_DATE + ")";
629 builder.append("[").append(XPathUtils.localAnd(overdueCond, notClosedCond)).append("]");
630 Query query = XPathUtils.createQuery(project.getSession(), builder.toString());
631 return query.execute().getNodes().getSize();
632 } catch (RepositoryException e) {
633 throw new ActivitiesException("Unable to get overdue tasks number for " + project);
634 }
635 }
636 }