1 package org.argeo.activities.ui;
2
3 import static org.argeo.eclipse.ui.jcr.JcrUiUtils.getNodeSelectionAdapter;
4
5 import java.text.DateFormat;
6 import java.text.SimpleDateFormat;
7 import java.util.Calendar;
8 import java.util.GregorianCalendar;
9 import java.util.HashMap;
10 import java.util.List;
11 import java.util.Map;
12
13 import javax.jcr.Node;
14 import javax.jcr.NodeIterator;
15 import javax.jcr.Property;
16 import javax.jcr.PropertyType;
17 import javax.jcr.RepositoryException;
18 import javax.jcr.Session;
19 import javax.jcr.Value;
20 import javax.jcr.query.Query;
21 import javax.jcr.query.QueryResult;
22
23 import org.argeo.activities.ActivitiesException;
24 import org.argeo.activities.ActivitiesNames;
25 import org.argeo.activities.ActivitiesService;
26 import org.argeo.activities.ActivitiesTypes;
27 import org.argeo.activities.core.ActivityUtils;
28 import org.argeo.cms.util.CmsUtils;
29 import org.argeo.connect.ConnectConstants;
30 import org.argeo.connect.UserAdminService;
31 import org.argeo.connect.resources.ResourcesService;
32 import org.argeo.connect.ui.ConnectWorkbenchUtils;
33 import org.argeo.connect.ui.SystemWorkbenchService;
34 import org.argeo.connect.ui.util.HtmlListRwtAdapter;
35 import org.argeo.connect.util.ConnectJcrUtils;
36 import org.argeo.connect.util.ConnectUtils;
37 import org.argeo.connect.util.XPathUtils;
38 import org.argeo.eclipse.ui.EclipseUiUtils;
39 import org.argeo.jcr.JcrUtils;
40 import org.eclipse.jface.layout.TableColumnLayout;
41 import org.eclipse.jface.viewers.ColumnLabelProvider;
42 import org.eclipse.jface.viewers.ColumnWeightData;
43 import org.eclipse.jface.viewers.IStructuredContentProvider;
44 import org.eclipse.jface.viewers.TableViewer;
45 import org.eclipse.jface.viewers.TableViewerColumn;
46 import org.eclipse.jface.viewers.Viewer;
47 import org.eclipse.swt.SWT;
48 import org.eclipse.swt.widgets.Composite;
49 import org.eclipse.swt.widgets.Table;
50 import org.eclipse.swt.widgets.TableColumn;
51
52
53 public class ActivityTable extends Composite {
54 private static final long serialVersionUID = 1L;
55
56 private TableViewer tableViewer;
57 private Session session;
58 private Node entity;
59 private UserAdminService userAdminService;
60 private ResourcesService resourceService;
61 private ActivitiesService activitiesService;
62 private SystemWorkbenchService systemWorkbenchService;
63
64
65
66
67
68
69
70
71
72 public ActivityTable(Composite parent, int style, UserAdminService userAdminService,
73 ResourcesService resourceService, ActivitiesService activityService,
74 SystemWorkbenchService systemWorkbenchService, Node entity) {
75 super(parent, SWT.NONE);
76 this.entity = entity;
77 session = ConnectJcrUtils.getSession(entity);
78 this.userAdminService = userAdminService;
79 this.resourceService = resourceService;
80 this.activitiesService = activityService;
81 this.systemWorkbenchService = systemWorkbenchService;
82
83 this.setLayout(EclipseUiUtils.noSpaceGridLayout());
84 Composite tableComp = new Composite(this, SWT.NO_FOCUS);
85 tableViewer = createActivityViewer(tableComp, style);
86 tableComp.setLayoutData(EclipseUiUtils.fillAll());
87
88 }
89
90 private TableViewer createActivityViewer(final Composite parent, int style) {
91 TableViewer viewer = new TableViewer(parent, SWT.V_SCROLL | style);
92 TableColumnLayout tableColumnLayout = new TableColumnLayout();
93
94 Table table = viewer.getTable();
95 table.setLayoutData(EclipseUiUtils.fillAll());
96 CmsUtils.markup(table);
97 CmsUtils.setItemHeight(table, 54);
98 table.setHeaderVisible(false);
99 table.setLinesVisible(true);
100
101 Map<String, ColumnLabelProvider> lpMap = new HashMap<String, ColumnLabelProvider>();
102 lpMap.put(ActivitiesNames.ACTIVITIES_ASSIGNED_TO, new UsersLabelProvider());
103 lpMap.put(ActivitiesNames.ACTIVITIES_RELATED_TO, new AlsoRelatedToLP());
104
105 ActivityViewerComparator comparator = new ActivityViewerComparator(activitiesService, lpMap);
106
107 TableColumn col;
108 TableViewerColumn tvCol;
109 int colIndex = 0;
110
111
112 col = new TableColumn(table, SWT.LEFT);
113 tableColumnLayout.setColumnData(col, new ColumnWeightData(30, 120, true));
114 tvCol = new TableViewerColumn(viewer, col);
115 tvCol.setLabelProvider(new TypeLabelProvider());
116 col.addSelectionListener(getNodeSelectionAdapter(colIndex++, PropertyType.STRING, Property.JCR_PRIMARY_TYPE,
117 comparator, viewer));
118
119
120 col = new TableColumn(table, SWT.LEFT);
121 tableColumnLayout.setColumnData(col, new ColumnWeightData(60, 145, true));
122 tvCol = new TableViewerColumn(viewer, col);
123 tvCol.setLabelProvider(new DateLabelProvider());
124 col.addSelectionListener(getNodeSelectionAdapter(colIndex++, PropertyType.DATE,
125 ActivityViewerComparator.RELEVANT_DATE, comparator, viewer));
126
127
128 col = new TableColumn(table, SWT.LEFT);
129 col.setText("Reported by");
130 col.addSelectionListener(getNodeSelectionAdapter(colIndex++, PropertyType.STRING,
131 ActivitiesNames.ACTIVITIES_ASSIGNED_TO, comparator, viewer));
132 tableColumnLayout.setColumnData(col, new ColumnWeightData(60, 180, true));
133 tvCol = new TableViewerColumn(viewer, col);
134 tvCol.setLabelProvider(new UsersLabelProvider());
135
136
137 col = new TableColumn(table, SWT.LEFT | SWT.WRAP);
138 col.setText("Also related to");
139 tableColumnLayout.setColumnData(col, new ColumnWeightData(80, 80, true));
140 tvCol = new TableViewerColumn(viewer, col);
141 tvCol.setLabelProvider(new AlsoRelatedToLP());
142 col.setToolTipText("Also related to these entities");
143
144
145 col = new TableColumn(table, SWT.LEFT | SWT.WRAP);
146 tableColumnLayout.setColumnData(col, new ColumnWeightData(200, 150, true));
147
148
149 tvCol = new TableViewerColumn(viewer, col);
150 tvCol.setLabelProvider(new TitleDescLabelProvider());
151
152
153
154 comparator.setColumn(PropertyType.DATE, ActivityViewerComparator.RELEVANT_DATE);
155
156 comparator.setColumn(PropertyType.DATE, ActivityViewerComparator.RELEVANT_DATE);
157 viewer.setComparator(comparator);
158
159 table.addSelectionListener(new HtmlListRwtAdapter(systemWorkbenchService));
160
161 viewer.setContentProvider(new MyTableContentProvider());
162
163
164 parent.setLayout(tableColumnLayout);
165 return viewer;
166 }
167
168
169
170
171
172 protected void refreshFilteredList() {
173 try {
174 List<Node> nodes = JcrUtils.nodeIteratorToList(listFilteredElements(session, null));
175 tableViewer.setInput(nodes.toArray());
176 } catch (RepositoryException e) {
177 throw new ActivitiesException("Unable to list activities", e);
178 }
179 }
180
181
182 public TableViewer getTableViewer() {
183 return tableViewer;
184 }
185
186
187
188
189
190 protected NodeIterator listFilteredElements(Session session, String filter) throws RepositoryException {
191 String xpathQueryStr = "//element(*, " + ActivitiesTypes.ACTIVITIES_ACTIVITY + ")";
192 String attrQuery = XPathUtils.getFreeTextConstraint(filter);
193 if (EclipseUiUtils.notEmpty(attrQuery))
194 xpathQueryStr += "[" + attrQuery + "]";
195 Query xpathQuery = XPathUtils.createQuery(session, xpathQueryStr);
196 QueryResult result = xpathQuery.execute();
197 return result.getNodes();
198 }
199
200
201
202 private class TypeLabelProvider extends ColumnLabelProvider {
203 private static final long serialVersionUID = 1L;
204
205 @Override
206 public String getText(Object element) {
207 Node currNode = (Node) element;
208
209 try {
210 StringBuilder builder = new StringBuilder();
211
212
213
214
215
216
217
218
219
220
221 if (currNode.isNodeType(ActivitiesTypes.ACTIVITIES_ACTIVITY)) {
222 builder.append("<b>");
223 builder.append(activitiesService.getActivityLabel(currNode));
224
225
226 if (currNode.isNodeType(ActivitiesTypes.ACTIVITIES_RATE)) {
227 Long rate = ConnectJcrUtils.getLongValue(currNode, ActivitiesNames.ACTIVITIES_RATE);
228 if (rate != null)
229 builder.append(": ").append(rate);
230 } else
231
232 if (currNode.isNodeType(ActivitiesTypes.ACTIVITIES_TASK))
233 builder.append(": ")
234 .append(ConnectJcrUtils.get(currNode, ActivitiesNames.ACTIVITIES_TASK_STATUS));
235 builder.append("</b>");
236 }
237 return builder.toString();
238 } catch (RepositoryException re) {
239 throw new ActivitiesException("Unable to get type snippet for " + currNode, re);
240 }
241 }
242 }
243
244 private class DateLabelProvider extends ColumnLabelProvider {
245 private static final long serialVersionUID = 1L;
246
247 @Override
248 public String getText(Object element) {
249 Node activityNode = (Node) element;
250 try {
251 Calendar date = null;
252 StringBuilder builder = new StringBuilder();
253
254 if (activityNode.isNodeType(ActivitiesTypes.ACTIVITIES_TASK)) {
255
256 if (activityNode.hasProperty(ActivitiesNames.ACTIVITIES_CLOSE_DATE)) {
257 date = activityNode.getProperty(ActivitiesNames.ACTIVITIES_CLOSE_DATE).getDate();
258 builder.append(funkyFormat(date)).append(" (Done date)").append("<br />");
259
260 if (activityNode.hasProperty(ActivitiesNames.ACTIVITIES_DUE_DATE)) {
261 date = activityNode.getProperty(ActivitiesNames.ACTIVITIES_DUE_DATE).getDate();
262 builder.append(funkyFormat(date)).append(" (Due date)");
263
264 }
265 } else if (activityNode.hasProperty(ActivitiesNames.ACTIVITIES_DUE_DATE)) {
266 date = activityNode.getProperty(ActivitiesNames.ACTIVITIES_DUE_DATE).getDate();
267 builder.append(funkyFormat(date)).append(" (Due date)").append("<br />");
268
269 boolean sleeping = false;
270 if (activityNode.hasProperty(ActivitiesNames.ACTIVITIES_WAKE_UP_DATE)) {
271 date = activityNode.getProperty(ActivitiesNames.ACTIVITIES_WAKE_UP_DATE).getDate();
272 Calendar now = GregorianCalendar.getInstance();
273 if (date.after(now)) {
274 builder.append(funkyFormat(date)).append(" (Sleep until)");
275 sleeping = true;
276 }
277 }
278
279 if (activityNode.hasProperty(Property.JCR_LAST_MODIFIED) && !sleeping) {
280 date = activityNode.getProperty(Property.JCR_LAST_MODIFIED).getDate();
281 builder.append(funkyFormat(date)).append(" (Last update)");
282 }
283 } else {
284 if (activityNode.hasProperty(Property.JCR_LAST_MODIFIED)) {
285 date = activityNode.getProperty(Property.JCR_LAST_MODIFIED).getDate();
286 builder.append(funkyFormat(date)).append(" (Last update)").append("<br />");
287 }
288
289 if (activityNode.hasProperty(ActivitiesNames.ACTIVITIES_ACTIVITY_DATE)) {
290 date = activityNode.getProperty(ActivitiesNames.ACTIVITIES_ACTIVITY_DATE).getDate();
291 builder.append(funkyFormat(date)).append(" (Creation date)");
292 } else if (activityNode.hasProperty(Property.JCR_CREATED)) {
293 date = activityNode.getProperty(Property.JCR_CREATED).getDate();
294 builder.append(funkyFormat(date)).append(" (Creation date)");
295 }
296 }
297 } else if (activityNode.isNodeType(ActivitiesTypes.ACTIVITIES_ACTIVITY)) {
298 Calendar happened = null;
299
300 String happenedLbl = "";
301 Calendar lastMod = null;
302 if (activityNode.hasProperty(Property.JCR_LAST_MODIFIED))
303 lastMod = activityNode.getProperty(Property.JCR_LAST_MODIFIED).getDate();
304
305 if (activityNode.hasProperty(ActivitiesNames.ACTIVITIES_ACTIVITY_DATE)) {
306 happened = activityNode.getProperty(ActivitiesNames.ACTIVITIES_ACTIVITY_DATE).getDate();
307 happenedLbl = " (Done date)";
308 } else if (activityNode.hasProperty(Property.JCR_CREATED)) {
309 happened = activityNode.getProperty(Property.JCR_CREATED).getDate();
310 happenedLbl = " (Creation date)";
311 }
312 boolean addUpdateDt = happened == null;
313 if (!addUpdateDt) {
314 date = (Calendar) happened.clone();
315 date.add(Calendar.MINUTE, 5);
316 if (lastMod != null)
317 addUpdateDt = lastMod.after(date);
318 }
319 if (addUpdateDt)
320 builder.append(funkyFormat(lastMod)).append(" (Last update)").append("<br />");
321 if (happened != null)
322 builder.append(funkyFormat(happened)).append(happenedLbl);
323 }
324 return builder.toString();
325 } catch (RepositoryException e) {
326 throw new ActivitiesException("Unable to get date label for " + activityNode, e);
327 }
328 }
329 }
330
331 private String getDisplayName(String id) {
332 return userAdminService.getUserDisplayName(id);
333 }
334
335 private String getDNameFromProp(Node node, String propName) {
336 String id = ConnectJcrUtils.get(node, propName);
337 if (EclipseUiUtils.notEmpty(id))
338 return userAdminService.getUserDisplayName(id);
339 return "";
340 }
341
342 private class UsersLabelProvider extends ColumnLabelProvider {
343 private static final long serialVersionUID = 1L;
344
345 @Override
346 public String getText(Object element) {
347 Node activityNode = (Node) element;
348 try {
349 String value = "";
350 StringBuilder builder = new StringBuilder();
351 if (activityNode.isNodeType(ActivitiesTypes.ACTIVITIES_TASK)) {
352
353 if (activitiesService.isTaskDone(activityNode)) {
354 value = getDNameFromProp(activityNode, ActivitiesNames.ACTIVITIES_CLOSED_BY);
355 if (EclipseUiUtils.notEmpty(value))
356 builder.append(value).append(" (Closed by)").append("<br />");
357 value = activitiesService.getAssignedToDisplayName(activityNode);
358 if (EclipseUiUtils.notEmpty(value))
359 builder.append(value).append(" (Assignee)").append("<br />");
360 } else {
361 value = activitiesService.getAssignedToDisplayName(activityNode);
362 if (EclipseUiUtils.notEmpty(value))
363 builder.append(value).append(" (Assignee)").append("<br />");
364
365 value = ConnectJcrUtils.get(activityNode, Property.JCR_LAST_MODIFIED_BY);
366 if (EclipseUiUtils.notEmpty(value))
367 builder.append(getDisplayName(value)).append(" (Last updater)");
368 }
369 } else if (activityNode.isNodeType(ActivitiesTypes.ACTIVITIES_ACTIVITY)) {
370 String reporter = getDNameFromProp(activityNode, ActivitiesNames.ACTIVITIES_REPORTED_BY);
371 String updater = getDNameFromProp(activityNode, Property.JCR_LAST_MODIFIED_BY);
372
373 if (EclipseUiUtils.isEmpty(reporter))
374 reporter = getDNameFromProp(activityNode, Property.JCR_CREATED_BY);
375
376 if (EclipseUiUtils.notEmpty(reporter))
377 builder.append(reporter).append(" (Reporter)").append("<br />");
378 if (EclipseUiUtils.notEmpty(updater) && (reporter == null || !reporter.equals(updater)))
379 builder.append(updater).append(" (Last updater)").append("<br />");
380 }
381 return builder.toString();
382 } catch (RepositoryException e) {
383 throw new ActivitiesException("Unable to get related users snippet for " + activityNode, e);
384 }
385 }
386 }
387
388 private class AlsoRelatedToLP extends ColumnLabelProvider {
389 private static final long serialVersionUID = 1L;
390
391 @Override
392 public String getText(Object element) {
393 try {
394 Node currNode = (Node) element;
395 if (currNode.hasProperty(ActivitiesNames.ACTIVITIES_RELATED_TO)) {
396 StringBuilder builder = new StringBuilder();
397 Value[] refs = currNode.getProperty(ActivitiesNames.ACTIVITIES_RELATED_TO).getValues();
398 if (refs.length > 0) {
399 String currEntityId = null;
400 if (entity != null)
401 currEntityId = entity.getIdentifier();
402 for (Value value : refs) {
403 String id = value.getString();
404 if (!id.equals(currEntityId)) {
405 Node currReferenced = session.getNodeByIdentifier(id);
406 String label = ConnectWorkbenchUtils.getOpenEditorSnippet(
407 systemWorkbenchService.getOpenEntityEditorCmdId(), currReferenced,
408 ConnectJcrUtils.get(currReferenced, Property.JCR_TITLE));
409 builder.append(label).append(", ");
410 }
411 }
412 if (builder.lastIndexOf(", ") != -1) {
413 String value = ConnectUtils
414 .replaceAmpersand(builder.substring(0, builder.lastIndexOf(", ")));
415 return wrapThis(value);
416 }
417 }
418
419 }
420 return "";
421 } catch (RepositoryException re) {
422 throw new ActivitiesException("Unable to get date from node " + element, re);
423 }
424 }
425 }
426
427 private class TitleDescLabelProvider extends ColumnLabelProvider {
428 private static final long serialVersionUID = 1L;
429
430 @Override
431 public String getText(Object element) {
432 try {
433 Node currNode = (Node) element;
434
435 if (currNode.isNodeType(ActivitiesTypes.ACTIVITIES_ACTIVITY)) {
436 String title = ConnectJcrUtils.get(currNode, Property.JCR_TITLE);
437
438 if (currNode.isNodeType(ActivitiesTypes.ACTIVITIES_POLL)) {
439 title = ConnectJcrUtils.get(currNode, ActivitiesNames.ACTIVITIES_POLL_NAME) + ": "
440 + ActivityUtils.getAvgRating(currNode);
441 }
442
443 String desc = ConnectJcrUtils.get(currNode, Property.JCR_DESCRIPTION);
444 String res = ConnectJcrUtils.concatIfNotEmpty(title, desc, " - ");
445 return wrapThis(res);
446 }
447 return "";
448 } catch (RepositoryException re) {
449 throw new ActivitiesException("Unable to get date from node " + element, re);
450 }
451 }
452 }
453
454 private DateFormat todayFormat = new SimpleDateFormat("HH:mm");
455 private DateFormat inMonthFormat = new SimpleDateFormat("dd MMM");
456 private DateFormat dateFormat = new SimpleDateFormat(ConnectConstants.DEFAULT_SHORT_DATE_FORMAT);
457
458 private String funkyFormat(Calendar date) {
459 Calendar now = GregorianCalendar.getInstance();
460 if (date.get(Calendar.YEAR) == now.get(Calendar.YEAR) && date.get(Calendar.MONTH) == now.get(Calendar.MONTH))
461 if (date.get(Calendar.DAY_OF_MONTH) == now.get(Calendar.DAY_OF_MONTH))
462 return todayFormat.format(date.getTime());
463 else
464 return inMonthFormat.format(date.getTime());
465 else
466 return dateFormat.format(date.getTime());
467
468 }
469
470 private class MyTableContentProvider implements IStructuredContentProvider {
471 private static final long serialVersionUID = 7164029504991808317L;
472
473 public Object[] getElements(Object inputElement) {
474 return (Object[]) inputElement;
475 }
476
477 public void dispose() {
478 }
479
480 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
481 }
482 }
483
484 private final String LIST_WRAP_STYLE = "style='float:left;padding:0px;white-space:pre-wrap;'";
485
486 private String wrapThis(String value) {
487 String wrapped = "<span " + LIST_WRAP_STYLE + " >" + ConnectUtils.replaceAmpersand(value) + "</span>";
488 return wrapped;
489 }
490
491
492
493 @Override
494 public boolean setFocus() {
495 tableViewer.getTable().setFocus();
496 return true;
497 }
498
499 @Override
500 public void dispose() {
501 super.dispose();
502 }
503
504 public void refresh() {
505 refreshFilteredList();
506 }
507 }