1 package org.argeo.activities.core;
2
3 import java.util.ArrayList;
4 import java.util.Calendar;
5 import java.util.GregorianCalendar;
6 import java.util.List;
7
8 import javax.jcr.Node;
9 import javax.jcr.NodeIterator;
10 import javax.jcr.Property;
11 import javax.jcr.RepositoryException;
12 import javax.jcr.Session;
13 import javax.jcr.query.Query;
14
15 import org.apache.commons.logging.Log;
16 import org.apache.commons.logging.LogFactory;
17 import org.argeo.activities.ActivitiesException;
18 import org.argeo.activities.ActivitiesNames;
19 import org.argeo.activities.ActivitiesService;
20 import org.argeo.activities.ActivitiesTypes;
21 import org.argeo.activities.ActivityValueCatalogs;
22 import org.argeo.cms.auth.CurrentUser;
23 import org.argeo.cms.util.UserAdminUtils;
24 import org.argeo.connect.ConnectNames;
25 import org.argeo.connect.UserAdminService;
26 import org.argeo.connect.core.AbstractAppService;
27 import org.argeo.connect.resources.ResourcesService;
28 import org.argeo.connect.util.ConnectJcrUtils;
29 import org.argeo.connect.util.ConnectUtils;
30 import org.argeo.connect.util.XPathUtils;
31 import org.argeo.eclipse.ui.EclipseUiUtils;
32 import org.argeo.jcr.JcrUtils;
33
34
35 public class ActivitiesServiceImpl extends AbstractAppService implements ActivitiesService, ActivitiesNames {
36 private final static Log log = LogFactory.getLog(ActivitiesServiceImpl.class);
37
38
39 private UserAdminService userAdminService;
40 private ResourcesService resourcesService;
41
42
43 @Override
44 public String getAppBaseName() {
45 return ActivitiesNames.ACTIVITIES_APP_BASE_NAME;
46 }
47
48 @Override
49 public String getDefaultRelPath(Node entity) throws RepositoryException {
50 if (entity.isNodeType(ActivitiesTypes.ACTIVITIES_ACTIVITY)) {
51 String currentUser = null;
52 if (entity.hasProperty(ACTIVITIES_REPORTED_BY))
53 currentUser = entity.getProperty(ACTIVITIES_REPORTED_BY).getString();
54 else {
55 currentUser = CurrentUser.getUsername();
56 if (log.isDebugEnabled())
57 log.warn("Activity at " + entity.getPath() + "has no reportedBy property");
58 }
59 String userId = UserAdminUtils.getUserLocalId(currentUser);
60
61 Calendar currentTime = null;
62 if (entity.hasProperty(ACTIVITIES_ACTIVITY_DATE))
63 currentTime = entity.getProperty(ACTIVITIES_ACTIVITY_DATE).getDate();
64 else {
65 currentTime = entity.getProperty(Property.JCR_CREATED).getDate();
66 if (log.isDebugEnabled())
67 log.warn("Activity at " + entity.getPath() + "has no activity date ");
68 }
69
70 String nodeType = getMainNodeType(entity);
71
72 return dateAsRelPath(currentTime) + "/" + userId + "/" + nodeType;
73 }
74 return null;
75 }
76
77 @Override
78 public String getDefaultRelPath(Session session, String nodeType, String id) {
79 throw new ActivitiesException("This method should not be used anymore");
80
81
82
83
84
85
86 }
87
88
89
90
91
92
93
94
95
96 @Override
97 public Node configureActivity(Node activity, String type, String title, String desc, List<Node> relatedTo)
98 throws RepositoryException {
99 return configureActivity(activity, activity.getSession().getUserID(), type, title, desc, relatedTo,
100 new GregorianCalendar());
101 }
102
103 @Override
104 public Node configureActivity(Node activity, String reporterId, String type, String title, String desc,
105 List<Node> relatedTo, Calendar date) throws RepositoryException {
106
107
108
109
110
111
112
113
114 activity.setProperty(ActivitiesNames.ACTIVITIES_REPORTED_BY, reporterId);
115
116
117 if (date != null)
118 activity.setProperty(ActivitiesNames.ACTIVITIES_ACTIVITY_DATE, date);
119
120
121 if (relatedTo != null && !relatedTo.isEmpty())
122 ConnectJcrUtils.setMultipleReferences(activity, ActivitiesNames.ACTIVITIES_RELATED_TO, relatedTo);
123
124
125 activity.setProperty(Property.JCR_TITLE, title);
126 activity.setProperty(Property.JCR_DESCRIPTION, desc);
127 JcrUtils.updateLastModified(activity);
128 return activity;
129
130
131
132 }
133
134 @Override
135 public boolean isKnownType(Node entity) {
136 if (ConnectJcrUtils.isNodeType(entity, ActivitiesTypes.ACTIVITIES_TASK)
137 || ConnectJcrUtils.isNodeType(entity, ActivitiesTypes.ACTIVITIES_ACTIVITY))
138 return true;
139 else
140 return false;
141 }
142
143 private static final String[] KNOWN_MIXIN = { ActivitiesTypes.ACTIVITIES_TASK, ActivitiesTypes.ACTIVITIES_NOTE,
144 ActivitiesTypes.ACTIVITIES_MEETING, ActivitiesTypes.ACTIVITIES_SENT_EMAIL,
145 ActivitiesTypes.ACTIVITIES_SENT_FAX, ActivitiesTypes.ACTIVITIES_SENT_EMAIL,
146 ActivitiesTypes.ACTIVITIES_BLOG_POST, ActivitiesTypes.ACTIVITIES_CALL, ActivitiesTypes.ACTIVITIES_CHAT,
147 ActivitiesTypes.ACTIVITIES_PAYMENT, ActivitiesTypes.ACTIVITIES_POLL, ActivitiesTypes.ACTIVITIES_RATE,
148 ActivitiesTypes.ACTIVITIES_REVIEW, ActivitiesTypes.ACTIVITIES_TWEET, ActivitiesTypes.ACTIVITIES_ACTIVITY };
149
150 @Override
151 public String getMainNodeType(Node entity) {
152
153 for (String mixin : KNOWN_MIXIN)
154 if (ConnectJcrUtils.isNodeType(entity, mixin))
155 return mixin;
156 return null;
157 }
158
159 @Override
160 public boolean isKnownType(String nodeType) {
161 for (String mixin : KNOWN_MIXIN)
162 if (mixin.equals(nodeType))
163 return true;
164 return false;
165 }
166
167
168
169 private String dateAsRelPath(Calendar cal) {
170
171
172
173
174
175
176
177
178
179
180
181
182 return JcrUtils.dateAsPath(cal, true);
183 }
184
185
186
187 @Override
188 public Calendar getActivityRelevantDate(Node activityNode) {
189 try {
190 Calendar relevantDate = null;
191 if (activityNode.isNodeType(ActivitiesTypes.ACTIVITIES_TASK)) {
192 if (activityNode.hasProperty(ConnectNames.CONNECT_CLOSE_DATE))
193 relevantDate = activityNode.getProperty(ConnectNames.CONNECT_CLOSE_DATE).getDate();
194 else if (activityNode.hasProperty(ActivitiesNames.ACTIVITIES_DUE_DATE))
195 relevantDate = activityNode.getProperty(ActivitiesNames.ACTIVITIES_DUE_DATE).getDate();
196 else if (activityNode.hasProperty(ActivitiesNames.ACTIVITIES_WAKE_UP_DATE))
197 relevantDate = activityNode.getProperty(ActivitiesNames.ACTIVITIES_WAKE_UP_DATE).getDate();
198 else if (activityNode.hasProperty(ActivitiesNames.ACTIVITIES_ACTIVITY_DATE))
199 relevantDate = activityNode.getProperty(ActivitiesNames.ACTIVITIES_ACTIVITY_DATE).getDate();
200 else if (activityNode.hasProperty(Property.JCR_LAST_MODIFIED))
201 relevantDate = activityNode.getProperty(Property.JCR_LAST_MODIFIED).getDate();
202 else if (activityNode.hasProperty(Property.JCR_CREATED))
203 relevantDate = activityNode.getProperty(Property.JCR_CREATED).getDate();
204 } else if (activityNode.isNodeType(ActivitiesTypes.ACTIVITIES_ACTIVITY)) {
205 if (activityNode.hasProperty(ActivitiesNames.ACTIVITIES_ACTIVITY_DATE))
206 relevantDate = activityNode.getProperty(ActivitiesNames.ACTIVITIES_ACTIVITY_DATE).getDate();
207 else if (activityNode.hasProperty(Property.JCR_LAST_MODIFIED))
208 relevantDate = activityNode.getProperty(Property.JCR_LAST_MODIFIED).getDate();
209 else if (activityNode.hasProperty(Property.JCR_CREATED))
210 relevantDate = activityNode.getProperty(Property.JCR_CREATED).getDate();
211 }
212 return relevantDate;
213 } catch (RepositoryException re) {
214 throw new ActivitiesException("Unable to get relevant date " + "for activity " + activityNode, re);
215 }
216 }
217
218 @Override
219 public String getActivityLabel(Node activity) {
220 try {
221 for (String type : ActivityValueCatalogs.MAPS_ACTIVITY_TYPES.keySet()) {
222 if (activity.isNodeType(type))
223 return ActivityValueCatalogs.MAPS_ACTIVITY_TYPES.get(type);
224 }
225 throw new ActivitiesException("Undefined type for activity: " + activity);
226 } catch (RepositoryException e) {
227 throw new ActivitiesException("Unable to get type for activity " + activity, e);
228 }
229 }
230
231
232 @Override
233 public NodeIterator getMyTasks(Session session, boolean onlyOpenTasks) {
234 List<String> normalisedRoles = new ArrayList<>();
235 for (String role : CurrentUser.roles())
236 normalisedRoles.add(normalizeDn(role));
237 String[] nrArr = normalisedRoles.toArray(new String[0]);
238 return getTasksForGroup(session, nrArr, onlyOpenTasks);
239
240 }
241
242 private String normalizeDn(String dn) {
243
244 String lowerCased = dn.replaceAll("UID=", "uid=").replaceAll("CN=", "cn=").replaceAll("DC=", "dc=")
245 .replaceAll("OU=", "ou=").replaceAll(", ", ",");
246 return lowerCased;
247 }
248
249
250
251
252
253
254
255
256 @Override
257 public NodeIterator getTasksForUser(Session session, String username, boolean onlyOpenTasks) {
258 return getTasksForGroup(session, userAdminService.getUserRoles(username), onlyOpenTasks);
259 }
260
261 public NodeIterator getTasksForGroup(Session session, String[] roles, boolean onlyOpenTasks) {
262 try {
263
264 StringBuilder builder = new StringBuilder();
265 builder.append("//element(*, ").append(ActivitiesTypes.ACTIVITIES_TASK).append(")");
266
267
268 StringBuilder tmpBuilder = new StringBuilder();
269 for (String role : roles) {
270 String attrQuery = XPathUtils.getPropertyEquals(ActivitiesNames.ACTIVITIES_ASSIGNED_TO, role);
271 if (ConnectUtils.notEmpty(attrQuery))
272 tmpBuilder.append(attrQuery).append(" or ");
273 }
274 String groupCond = null;
275 if (tmpBuilder.length() > 4)
276 groupCond = "(" + tmpBuilder.substring(0, tmpBuilder.length() - 3) + ")";
277
278
279 String notClosedCond = null;
280 if (onlyOpenTasks)
281 notClosedCond = "not(@" + ConnectNames.CONNECT_CLOSE_DATE + ")";
282
283 String allCond = XPathUtils.localAnd(groupCond, notClosedCond);
284 if (EclipseUiUtils.notEmpty(allCond))
285 builder.append("[").append(allCond).append("]");
286
287 builder.append(" order by @").append(ActivitiesNames.ACTIVITIES_DUE_DATE).append(", @")
288 .append(Property.JCR_LAST_MODIFIED).append(" ascending");
289 if (log.isTraceEnabled())
290 log.trace("Getting todo list for " + CurrentUser.getDisplayName() + " (DN: " + CurrentUser.getUsername()
291 + ") with query: " + builder.toString());
292 Query query = XPathUtils.createQuery(session, builder.toString());
293 return query.execute().getNodes();
294 } catch (RepositoryException e) {
295 throw new ActivitiesException("Unable to get tasks for groups " + roles.toString());
296 }
297 }
298
299 protected boolean manageClosedState(String templateId, Node taskNode, String oldStatus, String newStatus,
300 List<String> modifiedPaths) throws RepositoryException {
301 try {
302 Session session = taskNode.getSession();
303 List<String> closingStatus = resourcesService.getTemplateCatalogue(session, templateId,
304 ActivitiesNames.ACTIVITIES_TASK_CLOSING_STATUSES, null);
305
306 boolean changed = false;
307
308 if (closingStatus.contains(newStatus)) {
309 if (closingStatus.contains(oldStatus)) {
310
311 } else {
312
313 taskNode.setProperty(ConnectNames.CONNECT_CLOSE_DATE, new GregorianCalendar());
314 taskNode.setProperty(ConnectNames.CONNECT_CLOSED_BY, session.getUserID());
315 changed = true;
316 }
317 } else {
318 if (!closingStatus.contains(oldStatus)) {
319
320 } else {
321
322 if (taskNode.hasProperty(ConnectNames.CONNECT_CLOSE_DATE))
323 taskNode.getProperty(ConnectNames.CONNECT_CLOSE_DATE).remove();
324 if (taskNode.hasProperty(ConnectNames.CONNECT_CLOSED_BY))
325 taskNode.getProperty(ConnectNames.CONNECT_CLOSED_BY).remove();
326 changed = true;
327 }
328 }
329 return changed;
330 } catch (RepositoryException re) {
331 throw new RepositoryException("Unable to manage closed state for " + newStatus + " status for task "
332 + taskNode + " of template ID " + templateId, re);
333 }
334 }
335
336 public boolean updateStatus(String templateId, Node taskNode, String newStatus, List<String> modifiedPaths)
337 throws RepositoryException {
338 try {
339 String oldStatus = ConnectJcrUtils.get(taskNode, ActivitiesNames.ACTIVITIES_TASK_STATUS);
340 if (ConnectUtils.notEmpty(oldStatus) && oldStatus.equals(newStatus))
341 return false;
342 else {
343 taskNode.setProperty(ActivitiesNames.ACTIVITIES_TASK_STATUS, newStatus);
344 manageClosedState(templateId, taskNode, oldStatus, newStatus, modifiedPaths);
345 return true;
346 }
347 } catch (RepositoryException re) {
348 throw new RepositoryException("Unable to set new status " + newStatus + " status for task " + taskNode
349 + " of template ID " + templateId, re);
350 }
351 }
352
353 @Override
354 public boolean isTaskDone(Node taskNode) {
355 try {
356
357 return taskNode.hasProperty(ConnectNames.CONNECT_CLOSE_DATE);
358 } catch (RepositoryException re) {
359 throw new ActivitiesException("Unable to get done status for task " + taskNode, re);
360 }
361 }
362
363 @Override
364 public boolean isTaskSleeping(Node taskNode) {
365 try {
366 if (taskNode.hasProperty(ActivitiesNames.ACTIVITIES_WAKE_UP_DATE)) {
367 Calendar wuDate = taskNode.getProperty(ActivitiesNames.ACTIVITIES_WAKE_UP_DATE).getDate();
368 Calendar now = new GregorianCalendar();
369
370 now.add(Calendar.DAY_OF_YEAR, 1);
371 return wuDate.after(now);
372 } else
373 return false;
374 } catch (RepositoryException re) {
375 throw new ActivitiesException("Unable to get sleeping status for task " + taskNode, re);
376 }
377 }
378
379
380 @Override
381 public String getAssignedToDisplayName(Node taskNode) {
382 try {
383 if (taskNode.hasProperty(ActivitiesNames.ACTIVITIES_ASSIGNED_TO)) {
384 String groupId = taskNode.getProperty(ActivitiesNames.ACTIVITIES_ASSIGNED_TO).getString();
385 return userAdminService.getUserDisplayName(groupId);
386 }
387 return "";
388 } catch (RepositoryException e) {
389 throw new ActivitiesException("Unable to get name of group assigned to " + taskNode, e);
390 }
391 }
392
393 @Override
394 public Node configureTask(Node task, String taskNodeType, String title, String description, String assignedTo)
395 throws RepositoryException {
396 return configureTask(task, taskNodeType, task.getSession().getUserID(), title, description, assignedTo, null,
397 new GregorianCalendar(), null, null);
398 }
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421 @Override
422 public Node configureTask(Node draftTask, String taskNodeType, String reporterId, String title, String description,
423 String assignedTo, List<Node> relatedTo, Calendar creationDate, Calendar dueDate, Calendar wakeUpDate)
424 throws RepositoryException {
425
426
427
428
429
430
431
432
433 if (ConnectUtils.notEmpty(title))
434 draftTask.setProperty(Property.JCR_TITLE, title);
435 if (ConnectUtils.notEmpty(description))
436 draftTask.setProperty(Property.JCR_DESCRIPTION, description);
437 if (EclipseUiUtils.isEmpty(reporterId))
438 reporterId = draftTask.getSession().getUserID();
439 draftTask.setProperty(ActivitiesNames.ACTIVITIES_REPORTED_BY, reporterId);
440
441 if (ConnectUtils.notEmpty(assignedTo))
442 draftTask.setProperty(ActivitiesNames.ACTIVITIES_ASSIGNED_TO, assignedTo);
443
444 if (relatedTo != null && !relatedTo.isEmpty())
445 ConnectJcrUtils.setMultipleReferences(draftTask, ActivitiesNames.ACTIVITIES_RELATED_TO, relatedTo);
446
447 if (creationDate == null)
448 creationDate = new GregorianCalendar();
449 draftTask.setProperty(ActivitiesNames.ACTIVITIES_ACTIVITY_DATE, creationDate);
450
451 if (dueDate != null) {
452 draftTask.setProperty(ActivitiesNames.ACTIVITIES_DUE_DATE, dueDate);
453 }
454 if (wakeUpDate != null) {
455 draftTask.setProperty(ActivitiesNames.ACTIVITIES_WAKE_UP_DATE, wakeUpDate);
456 }
457 setTaskDefaultStatus(draftTask, taskNodeType);
458 return draftTask;
459 }
460
461 @Override
462 public void setTaskDefaultStatus(Node taskNode, String taskNodeType) throws RepositoryException {
463
464 Node template = resourcesService.getNodeTemplate(taskNode.getSession(), taskNodeType);
465 String defaultStatus = null;
466 if (template != null)
467 defaultStatus = ConnectJcrUtils.get(template, ActivitiesNames.ACTIVITIES_TASK_DEFAULT_STATUS);
468 if (ConnectUtils.notEmpty(defaultStatus))
469 taskNode.setProperty(ActivitiesNames.ACTIVITIES_TASK_STATUS, defaultStatus);
470 }
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499 public void setUserAdminService(UserAdminService userAdminService) {
500 this.userAdminService = userAdminService;
501 }
502
503 public void setResourcesService(ResourcesService resourcesService) {
504 this.resourcesService = resourcesService;
505 }
506 }