Add more info to the default dashboard
[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.cms.auth.CurrentUser;
18 import org.argeo.cms.ui.workbench.util.CommandUtils;
19 import org.argeo.cms.util.CmsUtils;
20 import org.argeo.connect.ConnectNames;
21 import org.argeo.connect.util.ConnectJcrUtils;
22 import org.argeo.connect.workbench.Refreshable;
23 import org.argeo.connect.workbench.commands.OpenEntityEditor;
24 import org.argeo.eclipse.ui.EclipseUiUtils;
25 import org.argeo.jcr.JcrUtils;
26 import org.argeo.node.NodeUtils;
27 import org.argeo.suite.SuiteException;
28 import org.argeo.suite.workbench.AsUiPlugin;
29 import org.argeo.tracker.TrackerNames;
30 import org.argeo.tracker.TrackerService;
31 import org.argeo.tracker.core.TrackerUtils;
32 import org.argeo.tracker.ui.TaskListLabelProvider;
33 import org.argeo.tracker.ui.TaskVirtualListComposite;
34 import org.eclipse.jface.viewers.ColumnLabelProvider;
35 import org.eclipse.swt.SWT;
36 import org.eclipse.swt.events.SelectionAdapter;
37 import org.eclipse.swt.events.SelectionEvent;
38 import org.eclipse.swt.graphics.Color;
39 import org.eclipse.swt.layout.GridData;
40 import org.eclipse.swt.layout.GridLayout;
41 import org.eclipse.swt.layout.RowLayout;
42 import org.eclipse.swt.widgets.Composite;
43 import org.eclipse.swt.widgets.Display;
44 import org.eclipse.swt.widgets.Group;
45 import org.eclipse.swt.widgets.Label;
46 import org.eclipse.swt.widgets.Link;
47
48 /** Argeo Suite Default Dashboard */
49 public class DefaultDashboardEditor extends AbstractSuiteDashboard implements Refreshable {
50 final static Log log = LogFactory.getLog(DefaultDashboardEditor.class);
51 public final static String ID = AsUiPlugin.PLUGIN_ID + ".defaultDashboardEditor";
52
53 private ActivitiesService activitiesService;
54 private TrackerService trackerService;
55
56 private String datePattern = "dd MMM yyyy";
57
58 private Composite headerCmp;
59 private Composite taskListCmp;
60 private TaskVirtualListComposite tvlc;
61
62 @Override
63 public void createPartControl(Composite parent) {
64 super.createPartControl(parent);
65 parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
66 Composite bodyCmp = new Composite(parent, SWT.NO_FOCUS);
67 bodyCmp.setLayoutData(EclipseUiUtils.fillAll());
68 bodyCmp.setLayout(new GridLayout());
69
70 // Header
71 try {
72 headerCmp = createHeaderPart(bodyCmp, NodeUtils.getUserHome(getSession()));
73 headerCmp.setLayoutData(EclipseUiUtils.fillWidth());
74
75 } catch (RepositoryException e) {
76 throw new SuiteException("Cannot create dashboard overview", e);
77 }
78
79 taskListCmp = new Composite(bodyCmp, SWT.NO_FOCUS);
80 taskListCmp.setLayoutData(EclipseUiUtils.fillAll());
81
82 populateTaskListCmp();
83 }
84
85 private void populateTaskListCmp() {
86 CmsUtils.clear(taskListCmp);
87 taskListCmp.setLayout(EclipseUiUtils.noSpaceGridLayout());
88 // Composite innerCmp = new Composite(taskListCmp, SWT.NO_FOCUS);
89 // innerCmp.setLayoutData(EclipseUiUtils.fillAll());
90
91 TaskListLabelProvider labelProvider = new TaskListLabelProvider(trackerService);
92 tvlc = new TaskVirtualListComposite(taskListCmp, SWT.NO_FOCUS, labelProvider, 54);
93 tvlc.setLayoutData(EclipseUiUtils.fillAll());
94 forceRefresh(null);
95 }
96
97 @Override
98 public void forceRefresh(Object object) {
99 NodeIterator nit = activitiesService.getMyTasks(getSession(), true);
100 tvlc.getTableViewer().setInput(JcrUtils.nodeIteratorToList(nit).toArray());
101 }
102
103 private Composite createHeaderPart(Composite parent, Node context) throws RepositoryException {
104 Composite bodyCmp = new Composite(parent, SWT.NO_FOCUS);
105 bodyCmp.setLayout(EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, true)));
106
107 Composite leftCmp = new Composite(bodyCmp, SWT.NO_FOCUS);
108 leftCmp.setLayout(new GridLayout());
109 leftCmp.setLayoutData(EclipseUiUtils.fillWidth());
110 Composite rightCmp = new Composite(bodyCmp, SWT.NO_FOCUS);
111 rightCmp.setLayout(new GridLayout());
112 rightCmp.setLayoutData(EclipseUiUtils.fillWidth());
113
114 // Title
115 Label titleLbl = new Label(leftCmp, SWT.WRAP | SWT.LEAD);
116 CmsUtils.markup(titleLbl);
117 String titleStr = "<big><b> Hello " + CurrentUser.getDisplayName() + " </b></big>";
118 titleLbl.setText(titleStr);
119 GridData gd = new GridData(SWT.TOP, SWT.BOTTOM, false, false);
120 gd.verticalIndent = 5;
121 gd.horizontalIndent = 10;
122 titleLbl.setLayoutData(gd);
123
124 Calendar now = GregorianCalendar.getInstance();
125
126 NodeIterator nit = activitiesService.getMyTasks(getSession(), true);
127 if (nit.hasNext()) {
128 List<Node> overdueTasks = new ArrayList<>();
129 while (nit.hasNext()) {
130 Node currNode = nit.nextNode();
131 if (currNode.hasProperty(ActivitiesNames.ACTIVITIES_DUE_DATE)
132 && currNode.getProperty(ActivitiesNames.ACTIVITIES_DUE_DATE).getDate().before(now))
133 overdueTasks.add(currNode);
134 }
135 if (!overdueTasks.isEmpty()) {
136 Composite overdueCmp = new Composite(leftCmp, SWT.NO_FOCUS);
137 long size = overdueTasks.size();
138 String overdueStr = "You have " + size + " overdue task" + (size > 1 ? "s" : "") + ": ";
139 populateMuliValueClickableList(overdueCmp, overdueTasks.toArray(new Node[0]), new TaskLp(), overdueStr);
140 }
141 }
142
143 nit = trackerService.getMyMilestones(getSession(), true);
144 List<Node> openMilestones = new ArrayList<>();
145
146 if (nit.hasNext()) {
147 List<Node> overdueMilestones = new ArrayList<>();
148 while (nit.hasNext()) {
149 Node currNode = nit.nextNode();
150 openMilestones.add(currNode);
151 if (currNode.hasProperty(TrackerNames.TRACKER_TARGET_DATE)
152 && currNode.getProperty(TrackerNames.TRACKER_TARGET_DATE).getDate().before(now))
153 overdueMilestones.add(currNode);
154 }
155 if (!overdueMilestones.isEmpty()) {
156 Composite overdueCmp = new Composite(leftCmp, SWT.NO_FOCUS);
157 long size = overdueMilestones.size();
158 String overdueStr = "You have " + size + " overdue milestone" + (size > 1 ? "s" : "") + ": ";
159 populateMuliValueClickableList(overdueCmp, overdueMilestones.toArray(new Node[0]), new MilestoneLp(),
160 overdueStr);
161 }
162 }
163
164 // My projects
165 List<Node> openProjects = JcrUtils.nodeIteratorToList(trackerService.getMyProjects(getSession(), true));
166 if (!openProjects.isEmpty()) {
167 Group myProjectsGp = new Group(rightCmp, SWT.NO_FOCUS);
168 myProjectsGp.setText("My open projects");
169 myProjectsGp.setLayoutData(EclipseUiUtils.fillWidth());
170 populateMuliValueClickableList(myProjectsGp, openProjects.toArray(new Node[0]), new ProjectLp(), null);
171 }
172
173 // My Milestones
174 if (!openMilestones.isEmpty()) {
175 Group myMilestoneGp = new Group(rightCmp, SWT.NO_FOCUS);
176 myMilestoneGp.setText("My open milestones");
177 myMilestoneGp.setLayoutData(EclipseUiUtils.fillWidth());
178 populateMuliValueClickableList(myMilestoneGp, openMilestones.toArray(new Node[0]), new MilestoneLp(), null);
179 }
180 return bodyCmp;
181 }
182
183 private class ProjectLp extends ColumnLabelProvider {
184 private static final long serialVersionUID = 7231233932794865555L;
185
186 @Override
187 public String getText(Object element) {
188 Node project = (Node) element;
189
190 String percent;
191 NodeIterator nit = TrackerUtils.getIssues(project, null, null, null, true);
192 long openNb = nit.getSize();
193
194 nit = TrackerUtils.getIssues(project, null, null, null, false);
195 long allNb = nit.getSize();
196
197 if (allNb < 1)
198 percent = "empty";
199 else {
200 double result = (allNb - openNb) / allNb * 100;
201 percent = String.format("%.1f", result) + "% done";
202 }
203 StringBuilder builder = new StringBuilder();
204 builder.append("<a>").append(ConnectJcrUtils.get(project, Property.JCR_TITLE)).append("</a>");
205 builder.append(" (").append(percent).append(")");
206
207 return builder.toString();
208 }
209 }
210
211 private class MilestoneLp extends ColumnLabelProvider {
212 private static final long serialVersionUID = 7231233932794865555L;
213
214 @Override
215 public String getText(Object element) {
216 Node milestone = (Node) element;
217 Node project = TrackerUtils.getRelatedProject(trackerService, milestone);
218 String dueDate = ConnectJcrUtils.getDateFormattedAsString(milestone, TrackerNames.TRACKER_TARGET_DATE,
219 datePattern);
220
221 String percent;
222 String propName = TrackerNames.TRACKER_MILESTONE_UID;
223 String muid = ConnectJcrUtils.get(milestone, ConnectNames.CONNECT_UID);
224 NodeIterator nit = TrackerUtils.getIssues(project, null, propName, muid, true);
225 long openNb = nit.getSize();
226
227 nit = TrackerUtils.getIssues(project, null, propName, muid, false);
228 long allNb = nit.getSize();
229
230 if (allNb < 1)
231 percent = "empty";
232 else {
233 double result = (allNb - openNb) / allNb * 100;
234 percent = String.format("%.1f", result) + "% done";
235 }
236 StringBuilder builder = new StringBuilder();
237 builder.append("<a>").append(ConnectJcrUtils.get(milestone, Property.JCR_TITLE)).append("</a>");
238 builder.append(" (");
239 if (EclipseUiUtils.notEmpty(dueDate))
240 builder.append("due to ").append(dueDate).append(", ");
241
242 builder.append(percent).append(")");
243 return builder.toString();
244 }
245
246 @Override
247 public Color getForeground(Object element) {
248 Node milestone = (Node) element;
249 Calendar dueDate = ConnectJcrUtils.getDateValue(milestone, TrackerNames.TRACKER_TARGET_DATE);
250 if (dueDate != null && dueDate.before(Calendar.getInstance()))
251 return Display.getCurrent().getSystemColor(SWT.COLOR_RED);
252 return null;
253 }
254 }
255
256 private class TaskLp extends ColumnLabelProvider {
257 private static final long serialVersionUID = 7231233932794865555L;
258
259 @Override
260 public String getText(Object element) {
261 Node task = (Node) element;
262 String dueDate = ConnectJcrUtils.getDateFormattedAsString(task, ActivitiesNames.ACTIVITIES_DUE_DATE,
263 datePattern);
264
265 StringBuilder builder = new StringBuilder();
266 builder.append("<a>").append(ConnectJcrUtils.get(task, Property.JCR_TITLE)).append("</a>");
267 if (EclipseUiUtils.notEmpty(dueDate))
268 builder.append(" (").append("due to ").append(dueDate).append(")");
269 return builder.toString();
270 }
271
272 @Override
273 public Color getForeground(Object element) {
274 Node milestone = (Node) element;
275 Calendar dueDate = ConnectJcrUtils.getDateValue(milestone, TrackerNames.TRACKER_TARGET_DATE);
276 if (dueDate != null && dueDate.before(Calendar.getInstance()))
277 return Display.getCurrent().getSystemColor(SWT.COLOR_RED);
278 return null;
279 }
280 }
281
282 @Override
283 public void setFocus() {
284 // refreshDocListGadget();
285 }
286
287 public void setActivitiesService(ActivitiesService activitiesService) {
288 this.activitiesService = activitiesService;
289 }
290
291 public void setTrackerService(TrackerService trackerService) {
292 this.trackerService = trackerService;
293 }
294
295 // LOCAL HELPERS
296 private void populateMuliValueClickableList(Composite parent, Node[] nodes, ColumnLabelProvider lp,
297 String listLabel) {
298 CmsUtils.clear(parent);
299 RowLayout rl = new RowLayout(SWT.HORIZONTAL);
300 rl.wrap = true;
301 rl.marginLeft = rl.marginTop = rl.marginBottom = 0;
302 rl.marginRight = 8;
303 parent.setLayout(rl);
304
305 if (EclipseUiUtils.notEmpty(listLabel)) {
306 Link link = new Link(parent, SWT.NONE);
307 link.setText(listLabel);
308 link.setFont(EclipseUiUtils.getBoldFont(parent));
309 }
310
311 int i = 1;
312 for (Node node : nodes) {
313 Link link = new Link(parent, SWT.NONE);
314 CmsUtils.markup(link);
315 link.setText(lp.getText(node) + (i != nodes.length ? ", " : ""));
316 i++;
317 // Color fc = lp.getForeground(node);
318 // if (fc != null)
319 // link.setForeground(fc);
320
321 link.addSelectionListener(new SelectionAdapter() {
322 private static final long serialVersionUID = 1L;
323
324 @Override
325 public void widgetSelected(final SelectionEvent event) {
326 CommandUtils.callCommand(getSystemWorkbenchService().getOpenEntityEditorCmdId(),
327 OpenEntityEditor.PARAM_JCR_ID, ConnectJcrUtils.getIdentifier(node));
328 }
329 });
330 }
331 }
332
333 }