Adapt to changes in Connect
[gpl/argeo-suite.git] / org.argeo.suite.workbench.rap / src / org / argeo / suite / workbench / parts / DefaultDashboardEditor.java
1 package org.argeo.suite.workbench.parts;
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
13 import org.apache.commons.logging.Log;
14 import org.apache.commons.logging.LogFactory;
15 import org.argeo.activities.ActivitiesNames;
16 import org.argeo.activities.ActivitiesService;
17 import org.argeo.activities.workbench.parts.TaskViewerContextMenu;
18 import org.argeo.cms.auth.CurrentUser;
19 import org.argeo.cms.ui.workbench.util.CommandUtils;
20 import org.argeo.cms.util.CmsUtils;
21 import org.argeo.connect.ConnectNames;
22 import org.argeo.connect.ui.ConnectEditor;
23 import org.argeo.connect.ui.Refreshable;
24 import org.argeo.connect.util.ConnectJcrUtils;
25 import org.argeo.connect.workbench.ConnectWorkbenchUtils;
26 import org.argeo.eclipse.ui.EclipseUiUtils;
27 import org.argeo.jcr.JcrUtils;
28 import org.argeo.node.NodeUtils;
29 import org.argeo.suite.workbench.AsUiPlugin;
30 import org.argeo.suite.workbench.SuiteWorkbenchException;
31 import org.argeo.tracker.TrackerNames;
32 import org.argeo.tracker.TrackerService;
33 import org.argeo.tracker.TrackerTypes;
34 import org.argeo.tracker.core.TrackerUtils;
35 import org.argeo.tracker.ui.TaskListLabelProvider;
36 import org.argeo.tracker.ui.TaskVirtualListComposite;
37 import org.eclipse.jface.viewers.ColumnLabelProvider;
38 import org.eclipse.jface.viewers.IStructuredSelection;
39 import org.eclipse.jface.viewers.TableViewer;
40 import org.eclipse.swt.SWT;
41 import org.eclipse.swt.events.MouseAdapter;
42 import org.eclipse.swt.events.MouseEvent;
43 import org.eclipse.swt.events.SelectionAdapter;
44 import org.eclipse.swt.events.SelectionEvent;
45 import org.eclipse.swt.graphics.Color;
46 import org.eclipse.swt.graphics.Point;
47 import org.eclipse.swt.layout.GridData;
48 import org.eclipse.swt.layout.GridLayout;
49 import org.eclipse.swt.layout.RowLayout;
50 import org.eclipse.swt.widgets.Composite;
51 import org.eclipse.swt.widgets.Display;
52 import org.eclipse.swt.widgets.Group;
53 import org.eclipse.swt.widgets.Label;
54 import org.eclipse.swt.widgets.Link;
55 import org.eclipse.ui.IEditorInput;
56 import org.eclipse.ui.IEditorSite;
57 import org.eclipse.ui.PartInitException;
58
59 /** Argeo Suite Default Dashboard */
60 public class DefaultDashboardEditor extends AbstractSuiteDashboard implements Refreshable {
61 final static Log log = LogFactory.getLog(DefaultDashboardEditor.class);
62 public final static String ID = AsUiPlugin.PLUGIN_ID + ".defaultDashboardEditor";
63
64 private ActivitiesService activitiesService;
65 private TrackerService trackerService;
66
67 private String datePattern = "dd MMM yyyy";
68
69 private Composite headerCmp;
70 private Composite taskListCmp;
71 private TaskVirtualListComposite tvlc;
72
73 @Override
74 public void init(IEditorSite site, IEditorInput input) throws PartInitException {
75 super.init(site, input);
76 }
77
78 @Override
79 public void createPartControl(Composite parent) {
80 super.createPartControl(parent);
81 parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
82 Composite bodyCmp = new Composite(parent, SWT.NO_FOCUS);
83 bodyCmp.setLayoutData(EclipseUiUtils.fillAll());
84 bodyCmp.setLayout(new GridLayout());
85
86 headerCmp = new Composite(bodyCmp, SWT.NO_FOCUS);
87 headerCmp.setLayoutData(EclipseUiUtils.fillWidth());
88
89 taskListCmp = new Composite(bodyCmp, SWT.NO_FOCUS);
90 taskListCmp.setLayoutData(EclipseUiUtils.fillAll());
91 forceRefresh(null);
92 }
93
94 @Override
95 public void forceRefresh(Object object) {
96 CmsUtils.clear(headerCmp);
97 populateHeaderPart(headerCmp, NodeUtils.getUserHome(getSession()));
98
99 CmsUtils.clear(taskListCmp);
100 populateTaskListCmp(taskListCmp);
101
102 headerCmp.getParent().layout(true, true);
103 }
104
105 private void populateTaskListCmp(Composite parent) {
106 parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
107 NodeIterator nit = activitiesService.getMyTasks(getSession(), true);
108 if (!nit.hasNext()) {
109 Composite noTaskCmp = new Composite(parent, SWT.NO_FOCUS);
110 noTaskCmp.setLayoutData(EclipseUiUtils.fillAll());
111 noTaskCmp.setLayout(new GridLayout());
112
113 Label noTaskLbl = new Label(noTaskCmp, SWT.CENTER);
114 noTaskLbl.setText("<i> <big> You have no pending Task. </big> </i>");
115 CmsUtils.markup(noTaskLbl);
116 noTaskLbl.setLayoutData(new GridData(SWT.CENTER, SWT.BOTTOM, true, true));
117
118 final Link createTaskLk = new Link(noTaskCmp, SWT.CENTER);
119 createTaskLk.setText("<a> Create a task </a>");
120 createTaskLk.setLayoutData(new GridData(SWT.CENTER, SWT.TOP, true, true));
121
122 createTaskLk.addSelectionListener(new SelectionAdapter() {
123 private static final long serialVersionUID = -9028457805156989935L;
124
125 @Override
126 public void widgetSelected(SelectionEvent e) {
127 String mainMixin = TrackerTypes.TRACKER_TASK;
128 String pathCreated = ConnectWorkbenchUtils.createAndConfigureEntity(createTaskLk.getShell(), getSession(),
129 getSystemAppService(), getSystemWorkbenchService(), mainMixin);
130 if (EclipseUiUtils.notEmpty(pathCreated))
131 forceRefresh(null);
132 }
133 });
134
135 } else {
136 TaskListLabelProvider labelProvider = new TaskListLabelProvider(trackerService);
137 tvlc = new TaskVirtualListComposite(parent, SWT.NO_FOCUS, labelProvider, 54);
138 tvlc.setLayoutData(EclipseUiUtils.fillAll());
139 final TableViewer viewer = tvlc.getTableViewer();
140 viewer.setInput(JcrUtils.nodeIteratorToList(nit).toArray());
141 final TaskViewerContextMenu contextMenu = new TaskViewerContextMenu(viewer, getSession(),
142 activitiesService) {
143 @Override
144 public boolean performAction(String actionId) {
145 boolean hasChanged = super.performAction(actionId);
146 if (hasChanged) {
147 viewer.getTable().setFocus();
148 forceRefresh(null);
149 // NodeIterator nit =
150 // activitiesService.getMyTasks(getSession(), true);
151 // viewer.setInput(JcrUtils.nodeIteratorToList(nit).toArray());
152 }
153 return hasChanged;
154 }
155 };
156 viewer.getTable().addMouseListener(new MouseAdapter() {
157 private static final long serialVersionUID = 6737579410648595940L;
158
159 @Override
160 public void mouseDown(MouseEvent e) {
161 if (e.button == 3) {
162 // contextMenu.setCurrFolderPath(currDisplayedFolder);
163 contextMenu.show(viewer.getTable(), new Point(e.x, e.y),
164 (IStructuredSelection) viewer.getSelection());
165 }
166 }
167 });
168
169 }
170 }
171
172 private boolean isOverdue(Node node, String propName) {
173 try {
174 Calendar now = GregorianCalendar.getInstance();
175 return node.hasProperty(propName) && node.getProperty(propName).getDate().before(now);
176 } catch (RepositoryException e) {
177 throw new SuiteWorkbenchException("Cannot check overdue status with property " + propName + " on " + node, e);
178 }
179 }
180
181 private void populateHeaderPart(Composite bodyCmp, Node context) {
182 bodyCmp.setLayout(EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, true)));
183
184 Composite leftCmp = new Composite(bodyCmp, SWT.NO_FOCUS);
185 leftCmp.setLayout(new GridLayout());
186 leftCmp.setLayoutData(EclipseUiUtils.fillWidth());
187 Composite rightCmp = new Composite(bodyCmp, SWT.NO_FOCUS);
188 rightCmp.setLayout(new GridLayout());
189 rightCmp.setLayoutData(EclipseUiUtils.fillWidth());
190
191 // Title
192 Label titleLbl = new Label(leftCmp, SWT.WRAP | SWT.LEAD);
193 CmsUtils.markup(titleLbl);
194 String titleStr = "<big><b> Hello " + CurrentUser.getDisplayName() + " </b></big>";
195 titleLbl.setText(titleStr);
196 GridData gd = new GridData(SWT.TOP, SWT.BOTTOM, false, false);
197 gd.verticalIndent = 5;
198 gd.horizontalIndent = 10;
199 titleLbl.setLayoutData(gd);
200
201 NodeIterator nit = activitiesService.getMyTasks(getSession(), true);
202 if (nit.hasNext()) {
203 List<Node> overdueTasks = new ArrayList<>();
204 while (nit.hasNext()) {
205 Node currNode = nit.nextNode();
206 if (isOverdue(currNode, ActivitiesNames.ACTIVITIES_DUE_DATE))
207 overdueTasks.add(currNode);
208 }
209 if (!overdueTasks.isEmpty()) {
210 Composite overdueCmp = new Composite(leftCmp, SWT.NO_FOCUS);
211 long size = overdueTasks.size();
212 String overdueStr = "You have " + size + " overdue task" + (size > 1 ? "s" : "") + ": ";
213 populateMuliValueClickableList(overdueCmp, overdueTasks.toArray(new Node[0]), new TaskLp(), overdueStr);
214 }
215 }
216
217 nit = trackerService.getMyMilestones(getSession(), true);
218 List<Node> openMilestones = new ArrayList<>();
219
220 if (nit.hasNext()) {
221 List<Node> overdueMilestones = new ArrayList<>();
222 while (nit.hasNext()) {
223 Node currNode = nit.nextNode();
224 openMilestones.add(currNode);
225 if (isOverdue(currNode, TrackerNames.TRACKER_TARGET_DATE))
226 overdueMilestones.add(currNode);
227 }
228 if (!overdueMilestones.isEmpty()) {
229 Composite overdueCmp = new Composite(leftCmp, SWT.NO_FOCUS);
230 long size = overdueMilestones.size();
231 String overdueStr = "You have " + size + " overdue milestone" + (size > 1 ? "s" : "") + ": ";
232 populateMuliValueClickableList(overdueCmp, overdueMilestones.toArray(new Node[0]), new MilestoneLp(),
233 overdueStr);
234 }
235 }
236
237 // My projects
238 List<Node> openProjects = JcrUtils.nodeIteratorToList(trackerService.getMyProjects(getSession(), true));
239 if (!openProjects.isEmpty()) {
240 Group myProjectsGp = new Group(rightCmp, SWT.NO_FOCUS);
241 myProjectsGp.setText("My open projects");
242 myProjectsGp.setLayoutData(EclipseUiUtils.fillWidth());
243 populateMuliValueClickableList(myProjectsGp, openProjects.toArray(new Node[0]), new ProjectLp(), null);
244 }
245
246 // My Milestones
247 if (!openMilestones.isEmpty()) {
248 Group myMilestoneGp = new Group(rightCmp, SWT.NO_FOCUS);
249 myMilestoneGp.setText("My open milestones");
250 myMilestoneGp.setLayoutData(EclipseUiUtils.fillWidth());
251 populateMuliValueClickableList(myMilestoneGp, openMilestones.toArray(new Node[0]), new MilestoneLp(), null);
252 }
253 }
254
255 private class ProjectLp extends ColumnLabelProvider {
256 private static final long serialVersionUID = 7231233932794865555L;
257
258 @Override
259 public String getText(Object element) {
260 Node project = (Node) element;
261
262 String percent;
263 NodeIterator nit = TrackerUtils.getIssues(project, null, null, null, true);
264 long openNb = nit.getSize();
265
266 nit = TrackerUtils.getIssues(project, null, null, null, false);
267 long allNb = nit.getSize();
268
269 if (allNb < 1)
270 percent = "empty";
271 else {
272 double num = allNb - openNb;
273 double result = num / allNb * 100;
274 percent = String.format("%.1f", result) + "% done";
275 }
276 StringBuilder builder = new StringBuilder();
277 builder.append("<a>").append(ConnectJcrUtils.get(project, Property.JCR_TITLE)).append("</a>");
278 builder.append(" (").append(percent).append(")");
279
280 return builder.toString();
281 }
282 }
283
284 private class MilestoneLp extends ColumnLabelProvider {
285 private static final long serialVersionUID = 7231233932794865555L;
286
287 @Override
288 public String getText(Object element) {
289 Node milestone = (Node) element;
290 Node project = TrackerUtils.getRelatedProject(trackerService, milestone);
291 String dueDate = ConnectJcrUtils.getDateFormattedAsString(milestone, TrackerNames.TRACKER_TARGET_DATE,
292 datePattern);
293
294 String percent;
295 String propName = TrackerNames.TRACKER_MILESTONE_UID;
296 String muid = ConnectJcrUtils.get(milestone, ConnectNames.CONNECT_UID);
297 NodeIterator nit = TrackerUtils.getIssues(project, null, propName, muid, true);
298 long openNb = nit.getSize();
299
300 nit = TrackerUtils.getIssues(project, null, propName, muid, false);
301 long allNb = nit.getSize();
302
303 if (allNb < 1)
304 percent = "empty";
305 else {
306 double num = allNb - openNb;
307 double result = num / allNb * 100;
308 percent = String.format("%.1f", result) + "% done";
309 }
310 StringBuilder builder = new StringBuilder();
311 builder.append("<a>").append(ConnectJcrUtils.get(milestone, Property.JCR_TITLE)).append("</a>");
312 builder.append(" (");
313 if (EclipseUiUtils.notEmpty(dueDate))
314 builder.append("due to ").append(dueDate).append(", ");
315
316 builder.append(percent).append(")");
317 return builder.toString();
318 }
319
320 @Override
321 public Color getForeground(Object element) {
322 Node milestone = (Node) element;
323 Calendar dueDate = ConnectJcrUtils.getDateValue(milestone, TrackerNames.TRACKER_TARGET_DATE);
324 if (dueDate != null && dueDate.before(Calendar.getInstance()))
325 return Display.getCurrent().getSystemColor(SWT.COLOR_RED);
326 return null;
327 }
328 }
329
330 private class TaskLp extends ColumnLabelProvider {
331 private static final long serialVersionUID = 7231233932794865555L;
332
333 @Override
334 public String getText(Object element) {
335 Node task = (Node) element;
336 String dueDate = ConnectJcrUtils.getDateFormattedAsString(task, ActivitiesNames.ACTIVITIES_DUE_DATE,
337 datePattern);
338
339 StringBuilder builder = new StringBuilder();
340 builder.append("<a>").append(ConnectJcrUtils.get(task, Property.JCR_TITLE)).append("</a>");
341 if (EclipseUiUtils.notEmpty(dueDate))
342 builder.append(" (").append("due to ").append(dueDate).append(")");
343 return builder.toString();
344 }
345
346 @Override
347 public Color getForeground(Object element) {
348 Node milestone = (Node) element;
349 Calendar dueDate = ConnectJcrUtils.getDateValue(milestone, TrackerNames.TRACKER_TARGET_DATE);
350 if (dueDate != null && dueDate.before(Calendar.getInstance()))
351 return Display.getCurrent().getSystemColor(SWT.COLOR_RED);
352 return null;
353 }
354 }
355
356 @Override
357 public void setFocus() {
358 // refreshDocListGadget();
359 }
360
361 public void setActivitiesService(ActivitiesService activitiesService) {
362 this.activitiesService = activitiesService;
363 }
364
365 public void setTrackerService(TrackerService trackerService) {
366 this.trackerService = trackerService;
367 }
368
369 // LOCAL HELPERS
370 private void populateMuliValueClickableList(Composite parent, Node[] nodes, ColumnLabelProvider lp,
371 String listLabel) {
372 CmsUtils.clear(parent);
373 RowLayout rl = new RowLayout(SWT.HORIZONTAL | SWT.WRAP);
374 rl.wrap = true;
375 rl.marginLeft = rl.marginTop = rl.marginBottom = 0;
376 rl.marginRight = 8;
377 parent.setLayout(rl);
378
379 if (EclipseUiUtils.notEmpty(listLabel)) {
380 Link link = new Link(parent, SWT.NONE);
381 link.setText(listLabel);
382 link.setFont(EclipseUiUtils.getBoldFont(parent));
383 }
384
385 int i = 1;
386 for (Node node : nodes) {
387 Link link = new Link(parent, SWT.NONE);
388 CmsUtils.markup(link);
389 link.setText(lp.getText(node) + (i != nodes.length ? ", " : ""));
390 i++;
391 // Color fc = lp.getForeground(node);
392 // if (fc != null)
393 // link.setForeground(fc);
394
395 link.addSelectionListener(new SelectionAdapter() {
396 private static final long serialVersionUID = 1L;
397
398 @Override
399 public void widgetSelected(final SelectionEvent event) {
400 CommandUtils.callCommand(getSystemWorkbenchService().getOpenEntityEditorCmdId(),
401 ConnectEditor.PARAM_JCR_ID, ConnectJcrUtils.getIdentifier(node));
402 }
403 });
404 }
405 }
406
407 }