View Javadoc
1   package org.argeo.tracker.e4.parts;
2   
3   import static org.argeo.activities.ActivitiesNames.ACTIVITIES_DUE_DATE;
4   import static org.argeo.eclipse.ui.EclipseUiUtils.notEmpty;
5   
6   import java.util.ArrayList;
7   import java.util.Calendar;
8   import java.util.GregorianCalendar;
9   import java.util.List;
10  import java.util.Map;
11  
12  import javax.jcr.Node;
13  import javax.jcr.NodeIterator;
14  import javax.jcr.Property;
15  import javax.jcr.RepositoryException;
16  import javax.jcr.Session;
17  import javax.jcr.query.Query;
18  
19  import org.argeo.activities.ActivitiesException;
20  import org.argeo.activities.ActivitiesNames;
21  import org.argeo.activities.ActivitiesTypes;
22  import org.argeo.activities.ui.AssignedToLP;
23  import org.argeo.cms.auth.CurrentUser;
24  import org.argeo.cms.ui.eclipse.forms.AbstractFormPart;
25  import org.argeo.cms.ui.eclipse.forms.FormToolkit;
26  import org.argeo.cms.util.CmsUtils;
27  import org.argeo.connect.ConnectConstants;
28  import org.argeo.connect.ConnectNames;
29  import org.argeo.connect.ui.ConnectColumnDefinition;
30  import org.argeo.connect.ui.ConnectImages;
31  import org.argeo.connect.ui.ConnectWorkbenchUtils;
32  import org.argeo.connect.ui.IJcrTableViewer;
33  import org.argeo.connect.ui.util.JcrRowLabelProvider;
34  import org.argeo.connect.ui.util.UserNameLP;
35  import org.argeo.connect.util.ConnectJcrUtils;
36  import org.argeo.connect.util.XPathUtils;
37  import org.argeo.eclipse.ui.ColumnDefinition;
38  import org.argeo.eclipse.ui.EclipseUiUtils;
39  import org.argeo.eclipse.ui.jcr.lists.SimpleJcrNodeLabelProvider;
40  import org.argeo.jcr.JcrUtils;
41  import org.argeo.tracker.TrackerException;
42  import org.argeo.tracker.TrackerNames;
43  import org.argeo.tracker.TrackerTypes;
44  import org.argeo.tracker.core.TrackerUtils;
45  import org.argeo.tracker.ui.TrackerLps;
46  import org.argeo.tracker.ui.controls.RepartitionChart;
47  import org.argeo.tracker.ui.dialogs.ConfigureMilestoneWizard;
48  import org.eclipse.jface.action.Action;
49  import org.eclipse.jface.action.ToolBarManager;
50  import org.eclipse.jface.dialogs.MessageDialog;
51  import org.eclipse.jface.resource.ImageDescriptor;
52  import org.eclipse.jface.viewers.DoubleClickEvent;
53  import org.eclipse.jface.viewers.IDoubleClickListener;
54  import org.eclipse.jface.viewers.IStructuredSelection;
55  import org.eclipse.jface.viewers.TableViewer;
56  import org.eclipse.jface.window.Window;
57  import org.eclipse.jface.wizard.WizardDialog;
58  import org.eclipse.swt.SWT;
59  import org.eclipse.swt.events.ModifyEvent;
60  import org.eclipse.swt.events.ModifyListener;
61  import org.eclipse.swt.events.SelectionAdapter;
62  import org.eclipse.swt.events.SelectionEvent;
63  import org.eclipse.swt.layout.GridData;
64  import org.eclipse.swt.layout.GridLayout;
65  import org.eclipse.swt.widgets.Button;
66  import org.eclipse.swt.widgets.Composite;
67  import org.eclipse.swt.widgets.Label;
68  import org.eclipse.swt.widgets.Link;
69  import org.eclipse.swt.widgets.Listener;
70  import org.eclipse.swt.widgets.Shell;
71  import org.eclipse.swt.widgets.Text;
72  
73  /** Default editor to display and edit a Tracker's milestone */
74  public class MilestoneEditor extends AbstractTrackerEditor implements IJcrTableViewer {
75  	// private static final long serialVersionUID = -4872303290083584882L;
76  
77  	// public static final String ID = TrackerUiPlugin.PLUGIN_ID +
78  	// ".milestoneEditor";
79  
80  	// Context
81  	private Node project;
82  	private Node milestone;
83  	private String milestoneUid;
84  
85  	// Ease implementation
86  	private Text filterTxt;
87  	private Button onlyMineBtn;
88  	private Button onlyOpenBtn;
89  	private Button overdueBtn;
90  	private AbstractFormPart issueListPart;
91  
92  	// local parameters
93  	private final static int CHART_DATA_LIMIT = 8;
94  	private final static int CHART_WIDTH = 300;
95  	private final static int CHART_HEIGHT = 200;
96  
97  	// @Override
98  	// public void init(IEditorSite site, IEditorInput input) throws
99  	// PartInitException {
100 	// super.init(site, input);
101 	// setTitleImage(ConnectImages.MILESTONE);
102 	// }
103 
104 	@Override
105 	protected void addPages() {
106 		// Initialise local cache
107 		milestone = getNode();
108 		project = getAppService().getEntityByUid(ConnectJcrUtils.getSession(milestone), null,
109 				ConnectJcrUtils.get(milestone, TrackerNames.TRACKER_PROJECT_UID));
110 		milestoneUid = ConnectJcrUtils.get(milestone, ConnectNames.CONNECT_UID);
111 		try {
112 			addPage(new MainPage(this));
113 
114 			// if (isInRole(ROLE_ADMIN))
115 			// addPage(new TechnicalInfoPage(this, ID + ".techInfoPage", getNode()));
116 		} catch (Exception e) {
117 			throw new TrackerException("Cannot add pages for editor of " + getNode(), e);
118 		}
119 	}
120 
121 	private class MainPage extends AbstractEditorPage {
122 		public final static String PAGE_ID = ".mainPage";
123 
124 		private Link projectLk;
125 		private Link managerLk;
126 		private Link overdueTasksLk;
127 		private Link dueDateLk;
128 		private Label dueDateLbl;
129 		private Composite chartCmp;
130 		private Label descLbl;
131 
132 		private TableViewer tableViewer;
133 
134 		public MainPage(AbstractTrackerEditor editor) {
135 			super(editor, PAGE_ID, "Overview");
136 		}
137 
138 		protected void createFormContent(Composite body) {
139 			// ScrolledForm form = mf.getForm();
140 			// Composite body = form.getBody();
141 			// ScrolledComposite form = mf.getForm();
142 			// Composite body = new Composite(form, SWT.NONE);
143 			body.setLayout(new GridLayout());
144 
145 //			new Label(body, SWT.BORDER).setText("TEST BODY");
146 
147 			Composite overview = appendOverviewPart(body);
148 			// overview.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
149 
150 			Composite filterCmp = new Composite(body, SWT.NO_FOCUS);
151 			createFilterPart(filterCmp);
152 			filterCmp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
153 
154 			Composite tableCmp = new Composite(body, SWT.NO_FOCUS);
155 			appendIssuesPart(tableCmp);
156 			GridData twd = new GridData(SWT.FILL, SWT.FILL, false, false);
157 			twd.heightHint = 300;
158 			tableCmp.setLayoutData(twd);
159 
160 			// form.reflow(true);
161 		}
162 
163 		private Composite appendOverviewPart(Composite parent) {
164 			FormToolkit tk = getFormToolkit();
165 
166 			// final Section section = TrackerUiUtils.addFormSection(tk, parent,
167 			// getMilestoneTitle());
168 
169 			Composite body = new Composite(parent, SWT.BORDER);
170 			body.setLayoutData(CmsUtils.fillAll());
171 			GridLayout layout = new GridLayout(2, false);
172 			// layout.numColumns = 2;
173 			body.setLayout(layout);
174 
175 			// Project
176 			createFormBoldLabel(tk, body, "Project");
177 			projectLk = new Link(body, SWT.NONE);
178 			projectLk.setText("TEST LINK");
179 			projectLk.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
180 			configureOpenLink(projectLk, project);
181 
182 			// Manager
183 			createFormBoldLabel(tk, body, "Manager");
184 			managerLk = new Link(body, SWT.NONE);
185 			managerLk.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
186 
187 			// Chart
188 			chartCmp = new Composite(body, SWT.NO_FOCUS);
189 			chartCmp.setLayout(EclipseUiUtils.noSpaceGridLayout());
190 			GridData twd = new GridData(SWT.FILL, SWT.FILL, false, false);
191 			twd.widthHint = CHART_WIDTH;
192 			twd.horizontalSpan = 2;
193 			twd.verticalSpan = 3;
194 			chartCmp.setLayoutData(twd);
195 
196 			// Overdue tasks
197 			createFormBoldLabel(tk, body, "Overdue Tasks");
198 			overdueTasksLk = new Link(body, SWT.NONE);
199 			overdueTasksLk.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
200 
201 			// Due Date
202 			dueDateLbl = createFormBoldLabel(tk, body, "Due Date");
203 			dueDateLk = new Link(body, SWT.NONE);
204 			dueDateLk.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
205 
206 			// // Reported by
207 			// TrackerUiUtils.createFormBoldLabel(tk, body, "Reported by");
208 			// reporterLk = new Link(body, SWT.NONE);
209 			// reporterLk.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, true, false));
210 
211 			// TODO add linked documents
212 
213 			// Description
214 			twd = (GridData) createFormBoldLabel(tk, body, "Details").getLayoutData();
215 			// twd.valign = GridData.TOP;
216 			descLbl = new Label(body, SWT.WRAP);
217 			twd = new GridData(SWT.FILL, SWT.FILL, false, false);
218 			// twd.colspan = 3;
219 			descLbl.setLayoutData(twd);
220 
221 			SectionPart part = new SectionPart(body.getParent()) {
222 
223 				@Override
224 				public void refresh() {
225 					String managerId = ConnectJcrUtils.get(milestone, TrackerNames.TRACKER_MANAGER);
226 					if (EclipseUiUtils.notEmpty(managerId))
227 						managerLk.setText(getUserAdminService().getUserDisplayName(managerId));
228 					else
229 						managerLk.setText("");
230 
231 					String dueDateStr = ConnectJcrUtils.getDateFormattedAsString(milestone,
232 							TrackerNames.TRACKER_TARGET_DATE, ConnectConstants.DEFAULT_DATE_TIME_FORMAT);
233 					if (EclipseUiUtils.notEmpty(dueDateStr))
234 						dueDateLk.setText(dueDateStr);
235 
236 					String desc = ConnectJcrUtils.get(milestone, Property.JCR_DESCRIPTION);
237 					descLbl.setText(desc);
238 
239 					// The chart
240 					CmsUtils.clear(chartCmp);
241 					GridData twd = (GridData) chartCmp.getLayoutData();
242 					Map<String, String> ot = TrackerUtils.getOpenTasksByAssignee(getUserAdminService(), project,
243 							milestoneUid, CHART_DATA_LIMIT);
244 					if (ot == null || ot.isEmpty()) {
245 						Label lbl = new Label(chartCmp, SWT.CENTER);
246 						lbl.setFont(EclipseUiUtils.getItalicFont(body));
247 						lbl.setText("No open task has been found for this milestone.");
248 						twd.heightHint = SWT.DEFAULT;
249 					} else {
250 						RepartitionChart coc = new RepartitionChart(chartCmp, SWT.NO_FOCUS);
251 						twd.heightHint = CHART_HEIGHT;
252 						coc.setLayoutData(EclipseUiUtils.fillAll());
253 						coc.setInput("Repartition by assignee - " + getOpenTaskNumber() + " open tasks", ot,
254 								CHART_WIDTH, CHART_HEIGHT);
255 					}
256 
257 					// Overdue tasks
258 					Long nb = getOverdueTaskNumber();
259 					overdueTasksLk.setText(nb < 0 ? "-" : nb.toString());
260 
261 					String closedOn = ConnectJcrUtils.getDateFormattedAsString(milestone,
262 							ConnectNames.CONNECT_CLOSE_DATE, ConnectConstants.DEFAULT_DATE_TIME_FORMAT);
263 
264 					if (EclipseUiUtils.notEmpty(closedOn)) {
265 						dueDateLbl.setText("Close date");
266 						dueDateLk.setText(closedOn);
267 					}
268 
269 					parent.layout(true, true);
270 					body.setFocus();
271 					super.refresh();
272 				}
273 			};
274 			getManagedForm().addPart(part);
275 			// addMainSectionMenu(part);
276 
277 			return body;
278 		}
279 
280 		private long getOpenTaskNumber() {
281 			StringBuilder builder = new StringBuilder();
282 			try {
283 				builder.append(XPathUtils.descendantFrom(project.getPath()));
284 				builder.append("//element(*, ").append(ActivitiesTypes.ACTIVITIES_TASK).append(")");
285 				String milestoneCond = XPathUtils.getPropertyEquals(TrackerNames.TRACKER_MILESTONE_UID, milestoneUid);
286 				String notClosedCond = "not(@" + ConnectNames.CONNECT_CLOSE_DATE + ")";
287 				builder.append("[").append(XPathUtils.localAnd(milestoneCond, notClosedCond)).append("]");
288 				Query query = XPathUtils.createQuery(getSession(), builder.toString());
289 				return query.execute().getNodes().getSize();
290 			} catch (RepositoryException e) {
291 				throw new ActivitiesException("Unable to get overdue tasks number for " + milestone);
292 			}
293 		}
294 
295 		private long getOverdueTaskNumber() {
296 			StringBuilder builder = new StringBuilder();
297 			try {
298 				builder.append(XPathUtils.descendantFrom(project.getPath()));
299 				builder.append("//element(*, ").append(TrackerTypes.TRACKER_ISSUE).append(")");
300 
301 				String milestoneCond = XPathUtils.getPropertyEquals(TrackerNames.TRACKER_MILESTONE_UID, milestoneUid);
302 
303 				// Past due date
304 				Calendar now = GregorianCalendar.getInstance();
305 				String overdueCond = XPathUtils.getPropertyDateComparaison(ACTIVITIES_DUE_DATE, now, "<");
306 				// Only opened tasks
307 				String notClosedCond = "not(@" + ConnectNames.CONNECT_CLOSE_DATE + ")";
308 
309 				builder.append("[").append(XPathUtils.localAnd(milestoneCond, overdueCond, notClosedCond)).append("]");
310 				Query query = XPathUtils.createQuery(getSession(), builder.toString());
311 				return query.execute().getNodes().getSize();
312 			} catch (RepositoryException e) {
313 				throw new ActivitiesException("Unable to get overdue tasks number for " + milestone);
314 			}
315 		}
316 
317 		/** Creates the list of issues relevant for this category */
318 		private void appendIssuesPart(Composite parent) {
319 			List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
320 			columnDefs.add(new ColumnDefinition(new SimpleJcrNodeLabelProvider(TrackerNames.TRACKER_ID), "ID", 40));
321 			columnDefs.add(new ColumnDefinition(new SimpleJcrNodeLabelProvider(Property.JCR_TITLE), "Title", 300));
322 			columnDefs.add(new ColumnDefinition(new SimpleJcrNodeLabelProvider(ActivitiesNames.ACTIVITIES_TASK_STATUS),
323 					"Status", 100));
324 			columnDefs.add(new ColumnDefinition(
325 					new TrackerLps().new DnLabelProvider(getUserAdminService(), ActivitiesNames.ACTIVITIES_ASSIGNED_TO),
326 					"Assignee", 160));
327 			columnDefs.add(new ColumnDefinition(new TrackerLps().new ImportanceLabelProvider(), "Importance", 100));
328 			columnDefs.add(new ColumnDefinition(new TrackerLps().new PriorityLabelProvider(), "Priority", 100));
329 
330 			// Create and configure the table
331 			tableViewer = TrackerUiUtils.createTableViewer(parent, SWT.SINGLE, columnDefs);
332 			tableViewer.addDoubleClickListener(new IDoubleClickListener() {
333 
334 				@Override
335 				public void doubleClick(DoubleClickEvent event) {
336 					Object element = ((IStructuredSelection) event.getSelection()).getFirstElement();
337 					// String jcrId = ConnectJcrUtils.getIdentifier((Node) element);
338 					// CommandUtils.callCommand(getAppWorkbenchService().getOpenEntityEditorCmdId(),
339 					// ConnectEditor.PARAM_JCR_ID, jcrId);
340 					getAppWorkbenchService().openEntityEditor((Node) element);
341 				}
342 			});
343 
344 			issueListPart = new AbstractFormPart() {
345 				@Override
346 				public void refresh() {
347 					refreshViewer(filterTxt.getText());
348 					// mf.getForm().reflow(true);
349 					super.refresh();
350 				}
351 			};
352 			getManagedForm().addPart(issueListPart);
353 		}
354 
355 		public void setActive(boolean active) {
356 			issueListPart.markStale();
357 			super.setActive(active);
358 		}
359 
360 		private void refreshViewer(String filter) {
361 			NodeIterator nit = getTasks();
362 			tableViewer.setInput(JcrUtils.nodeIteratorToList(nit).toArray(new Node[0]));
363 			tableViewer.refresh();
364 		}
365 
366 		// Add the filter ability
367 		private void createFilterPart(Composite parent) {
368 			GridLayout layout = EclipseUiUtils.noSpaceGridLayout(new GridLayout(4, false));
369 			layout.horizontalSpacing = 5;
370 			parent.setLayout(layout);
371 			parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
372 
373 			filterTxt = new Text(parent, SWT.BORDER | SWT.SEARCH | SWT.ICON_SEARCH | SWT.ICON_CANCEL);
374 			filterTxt.setLayoutData(EclipseUiUtils.fillWidth());
375 
376 			filterTxt.addModifyListener(new ModifyListener() {
377 				private static final long serialVersionUID = 8130545587125370689L;
378 
379 				public void modifyText(ModifyEvent event) {
380 					refreshViewer(filterTxt.getText());
381 				}
382 			});
383 
384 			SelectionAdapter adapter = new SelectionAdapter() {
385 				private static final long serialVersionUID = 1L;
386 
387 				@Override
388 				public void widgetSelected(SelectionEvent e) {
389 					refreshViewer(filterTxt.getText());
390 				}
391 			};
392 
393 			onlyMineBtn = new Button(parent, SWT.CHECK);
394 			onlyMineBtn.setText("Only mine ");
395 			onlyMineBtn.addSelectionListener(adapter);
396 			onlyOpenBtn = new Button(parent, SWT.CHECK);
397 			onlyOpenBtn.setText("Only open ");
398 			onlyOpenBtn.addSelectionListener(adapter);
399 			overdueBtn = new Button(parent, SWT.CHECK);
400 			overdueBtn.setText("Overdue ");
401 			overdueBtn.addSelectionListener(adapter);
402 
403 		}
404 
405 		// SECTION MENU
406 		private void addMainSectionMenu(SectionPart sectionPart) {
407 			ToolBarManager toolBarManager = TrackerUiUtils.addMenu(sectionPart.getSection());
408 
409 			String tooltip = "Mark this milestone as closed";
410 			Action action = new CloseMilestone(tooltip, ConnectImages.IMG_DESC_CLOSE, sectionPart);
411 			toolBarManager.add(action);
412 
413 			tooltip = "Edit the milestone main information";
414 			action = new OpenConfigureDialog(tooltip, ConnectImages.IMG_DESC_EDIT, sectionPart);
415 			toolBarManager.add(action);
416 
417 			tooltip = "Add a task to this milestone";
418 			action = new AddTask(sectionPart.getSection().getShell(), tooltip, ConnectImages.IMG_DESC_ADD);
419 			toolBarManager.add(action);
420 
421 			toolBarManager.update(true);
422 
423 		}
424 
425 		// MENU ACTIONS
426 		private class OpenConfigureDialog extends Action {
427 			private static final long serialVersionUID = -6798429720348536525L;
428 			private final SectionPart sectionPart;
429 
430 			private OpenConfigureDialog(String name, ImageDescriptor img, SectionPart sectionPart) {
431 				super(name, img);
432 				this.sectionPart = sectionPart;
433 			}
434 
435 			@Override
436 			public void run() {
437 				Shell currShell = sectionPart.getSection().getShell();
438 				ConfigureMilestoneWizard wizard = new ConfigureMilestoneWizard(getUserAdminService(),
439 						getTrackerService(), milestone);
440 				WizardDialog dialog = new WizardDialog(currShell, wizard);
441 				try {
442 					if (dialog.open() == Window.OK && milestone.getSession().hasPendingChanges()) {
443 						updatePartName();
444 						sectionPart.getSection().setText(getMilestoneTitle());
445 						sectionPart.refresh();
446 						sectionPart.markDirty();
447 						sectionPart.getSection().setFocus();
448 					}
449 				} catch (RepositoryException e) {
450 					throw new TrackerException("Cannot check session state on " + milestone, e);
451 				}
452 			}
453 		}
454 
455 		private class CloseMilestone extends Action {
456 			private static final long serialVersionUID = -6798429720348536525L;
457 			private final SectionPart sectionPart;
458 
459 			private CloseMilestone(String name, ImageDescriptor img, SectionPart sectionPart) {
460 				super(name, img);
461 				this.sectionPart = sectionPart;
462 			}
463 
464 			@Override
465 			public void run() {
466 				Shell currShell = sectionPart.getSection().getShell();
467 				boolean doIt = MessageDialog.openConfirm(currShell, "Confirm before close",
468 						"Are you sure you want to close milestone ["
469 								+ ConnectJcrUtils.get(milestone, Property.JCR_TITLE) + "] ?");
470 				try {
471 					if (doIt) {
472 						milestone.setProperty(ConnectNames.CONNECT_CLOSE_DATE, new GregorianCalendar());
473 						milestone.setProperty(ConnectNames.CONNECT_CLOSED_BY, milestone.getSession().getUserID());
474 						sectionPart.getSection().setText(getMilestoneTitle());
475 						sectionPart.refresh();
476 						sectionPart.markDirty();
477 						sectionPart.getSection().setFocus();
478 					}
479 				} catch (RepositoryException e) {
480 					throw new TrackerException("Cannot check session state on " + milestone, e);
481 				}
482 			}
483 		}
484 
485 		private class AddTask extends Action {
486 			private static final long serialVersionUID = 5112793747049604434L;
487 			final Shell shell;
488 
489 			private AddTask(Shell shell, String name, ImageDescriptor img) {
490 				super(name, img);
491 				this.shell = shell;
492 			}
493 
494 			@Override
495 			public void run() {
496 				Session session = ConnectJcrUtils.getSession(project);
497 				String mainMixin = ConnectJcrUtils.isNodeType(project, TrackerTypes.TRACKER_IT_PROJECT)
498 						? TrackerTypes.TRACKER_ISSUE
499 						: TrackerTypes.TRACKER_TASK;
500 				String propName1 = TrackerNames.TRACKER_PROJECT_UID;
501 				String value1 = ConnectJcrUtils.get(project, ConnectNames.CONNECT_UID);
502 				String propName2 = TrackerNames.TRACKER_MILESTONE_UID;
503 				String value2 = ConnectJcrUtils.get(milestone, ConnectNames.CONNECT_UID);
504 
505 				String pathCreated = ConnectWorkbenchUtils.createAndConfigureEntity(shell, session, getTrackerService(),
506 						getAppWorkbenchService(), mainMixin, propName1, value1, propName2, value2);
507 				if (EclipseUiUtils.notEmpty(pathCreated))
508 					refreshViewer(filterTxt.getText());
509 			}
510 		}
511 	}
512 
513 	@Override
514 	public Object[] getElements(String extractId) {
515 		return JcrUtils.nodeIteratorToList(getTasks()).toArray(new Node[0]);
516 	}
517 
518 	public NodeIterator getTasks() {
519 		String filter = filterTxt.getText();
520 		try {
521 			StringBuilder builder = new StringBuilder();
522 			builder.append(XPathUtils.descendantFrom(project.getPath()));
523 			builder.append("//element(*, ").append(TrackerTypes.TRACKER_TASK).append(")");
524 
525 			String milestoneCond = XPathUtils.getPropertyEquals(TrackerNames.TRACKER_MILESTONE_UID, milestoneUid);
526 
527 			String onlyMineCond = null;
528 			if (onlyMineBtn.getSelection()) {
529 				List<String> normalisedRoles = new ArrayList<>();
530 				for (String role : CurrentUser.roles())
531 					normalisedRoles.add(TrackerUtils.normalizeDn(role));
532 				String[] nrArr = normalisedRoles.toArray(new String[0]);
533 				StringBuilder tmpBuilder = new StringBuilder();
534 				for (String role : nrArr) {
535 					String attrQuery = XPathUtils.getPropertyEquals(ActivitiesNames.ACTIVITIES_ASSIGNED_TO, role);
536 					tmpBuilder.append(attrQuery).append(" or ");
537 				}
538 				if (tmpBuilder.length() > 4)
539 					onlyMineCond = "(" + tmpBuilder.substring(0, tmpBuilder.length() - 3) + ")";
540 			}
541 
542 			String overdueCond = null;
543 			if (overdueBtn.getSelection()) {
544 				Calendar now = GregorianCalendar.getInstance();
545 				overdueCond = XPathUtils.getPropertyDateComparaison(ACTIVITIES_DUE_DATE, now, "<");
546 			}
547 
548 			String notClosedCond = null;
549 			if (onlyOpenBtn.getSelection())
550 				notClosedCond = "not(@" + ConnectNames.CONNECT_CLOSE_DATE + ")";
551 
552 			String ftcCond = null;
553 			if (EclipseUiUtils.notEmpty(filter))
554 				ftcCond = XPathUtils.getFreeTextConstraint(filter);
555 
556 			String fullCond = XPathUtils.localAnd(milestoneCond, overdueCond, notClosedCond, ftcCond, onlyMineCond);
557 			builder.append("[").append(fullCond).append("]");
558 			builder.append(" order by @" + TrackerNames.TRACKER_ID);
559 
560 			Query query = XPathUtils.createQuery(getSession(), builder.toString());
561 			return query.execute().getNodes();
562 		} catch (RepositoryException e) {
563 			throw new TrackerException("Unable to get filtered tasks for " + milestone + " with filter: " + filter, e);
564 		}
565 	}
566 
567 	@Override
568 	public List<ConnectColumnDefinition> getColumnDefinition(String extractId) {
569 		List<ConnectColumnDefinition> columns = new ArrayList<ConnectColumnDefinition>();
570 		columns.add(new ConnectColumnDefinition("ID", new SimpleJcrNodeLabelProvider(TrackerNames.TRACKER_ID)));
571 		columns.add(
572 				new ConnectColumnDefinition("Status", new JcrRowLabelProvider(ActivitiesNames.ACTIVITIES_TASK_STATUS)));
573 		columns.add(new ConnectColumnDefinition("Title", new JcrRowLabelProvider(Property.JCR_TITLE)));
574 		columns.add(new ConnectColumnDefinition("Description", new JcrRowLabelProvider(Property.JCR_DESCRIPTION)));
575 		columns.add(new ConnectColumnDefinition("Assigned To",
576 				new AssignedToLP(getActivitiesService(), null, Property.JCR_DESCRIPTION)));
577 		columns.add(
578 				new ConnectColumnDefinition("Due Date", new JcrRowLabelProvider(ActivitiesNames.ACTIVITIES_DUE_DATE)));
579 		columns.add(new ConnectColumnDefinition("Importance", new TrackerLps().new ImportanceLabelProvider()));
580 		columns.add(new ConnectColumnDefinition("Priority", new TrackerLps().new PriorityLabelProvider()));
581 		columns.add(new ConnectColumnDefinition("Components",
582 				new SimpleJcrNodeLabelProvider(TrackerNames.TRACKER_COMPONENT_IDS)));
583 
584 		columns.add(new ConnectColumnDefinition("Wake-Up Date",
585 				new JcrRowLabelProvider(ActivitiesNames.ACTIVITIES_WAKE_UP_DATE)));
586 		columns.add(
587 				new ConnectColumnDefinition("Close Date", new JcrRowLabelProvider(ConnectNames.CONNECT_CLOSE_DATE)));
588 		columns.add(new ConnectColumnDefinition("Closed by",
589 				new UserNameLP(getUserAdminService(), null, ConnectNames.CONNECT_CLOSED_BY)));
590 		return columns;
591 	}
592 
593 	/* LOCAL HELPERS */
594 
595 	private String getMilestoneTitle() {
596 		String id = ConnectJcrUtils.get(getNode(), TrackerNames.TRACKER_ID);
597 		String name = ConnectJcrUtils.get(getNode(), Property.JCR_TITLE);
598 
599 		String title = null;
600 		if (notEmpty(id) && notEmpty(id))
601 			if (id.equals(name))
602 				title = "v" + id;
603 			else
604 				title = "v" + id + "  aka " + name;
605 		else if (notEmpty(id))
606 			title = "v" + id;
607 		else
608 			title = name;
609 
610 		if (notEmpty(title)) {
611 			Node project = TrackerUtils.getProjectFromChild(getNode());
612 			String pname = ConnectJcrUtils.get(project, Property.JCR_TITLE);
613 			title = title + (notEmpty(pname) ? " (" + pname + ")" : "");
614 		}
615 
616 		String closedBy = ConnectJcrUtils.get(milestone, ConnectNames.CONNECT_CLOSED_BY);
617 		String closedOn = ConnectJcrUtils.getDateFormattedAsString(milestone, ConnectNames.CONNECT_CLOSE_DATE,
618 				ConnectConstants.DEFAULT_DATE_TIME_FORMAT);
619 
620 		if (EclipseUiUtils.notEmpty(closedOn))
621 			title += " closed on " + closedOn + " by " + getUserAdminService().getUserDisplayName(closedBy);
622 		return title;
623 	}
624 
625 	// private Label createFormBoldLabel(FormToolkit toolkit, Composite parent,
626 	// String value) {
627 	// Label label = toolkit.createLabel(parent, " " + value, SWT.END);
628 	// label.setFont(EclipseUiUtils.getBoldFont(parent));
629 	// GridData twd = new GridData(SWT.FILL, SWT.FILL, false, false);
630 	// label.setLayoutData(twd);
631 	// return label;
632 	// }
633 
634 	private void configureOpenLink(Link link, Node targetNode) {
635 		link.setText("<a>" + ConnectJcrUtils.get(targetNode, Property.JCR_TITLE) + "</a>");
636 
637 		// Remove existing if necessary
638 		Listener[] existings = link.getListeners(SWT.Selection);
639 		for (Listener l : existings)
640 			link.removeListener(SWT.Selection, l);
641 
642 		link.addSelectionListener(new SelectionAdapter() {
643 			private static final long serialVersionUID = 1L;
644 
645 			@Override
646 			public void widgetSelected(SelectionEvent e) {
647 				// CommandUtils.callCommand(getAppWorkbenchService().getOpenEntityEditorCmdId(),
648 				// ConnectEditor.PARAM_JCR_ID, ConnectJcrUtils.getIdentifier(targetNode));
649 				getAppWorkbenchService().openEntityEditor(targetNode);
650 			}
651 		});
652 	}
653 }