View Javadoc
1   package org.argeo.activities.ui;
2   
3   import java.text.DateFormat;
4   import java.text.SimpleDateFormat;
5   import java.util.ArrayList;
6   import java.util.Calendar;
7   import java.util.List;
8   
9   import javax.jcr.Node;
10  import javax.jcr.Property;
11  import javax.jcr.PropertyType;
12  import javax.jcr.RepositoryException;
13  import javax.jcr.Session;
14  
15  import org.argeo.activities.ActivitiesException;
16  import org.argeo.activities.ActivitiesNames;
17  import org.argeo.activities.ActivitiesService;
18  import org.argeo.cms.ui.eclipse.forms.AbstractFormPart;
19  import org.argeo.cms.ui.eclipse.forms.FormToolkit;
20  import org.argeo.cms.ui.useradmin.PickUpUserDialog;
21  import org.argeo.connect.ConnectConstants;
22  import org.argeo.connect.UserAdminService;
23  import org.argeo.connect.resources.ResourcesService;
24  import org.argeo.connect.ui.ConnectEditor;
25  import org.argeo.connect.ui.ConnectUiUtils;
26  import org.argeo.connect.ui.ConnectWorkbenchUtils;
27  import org.argeo.connect.ui.SystemWorkbenchService;
28  import org.argeo.connect.ui.parts.AbstractPanelFormPart;
29  import org.argeo.connect.ui.parts.DateTextPart;
30  import org.argeo.connect.util.ConnectJcrUtils;
31  import org.argeo.eclipse.ui.EclipseUiUtils;
32  import org.eclipse.jface.window.Window;
33  import org.eclipse.swt.SWT;
34  import org.eclipse.swt.events.SelectionAdapter;
35  import org.eclipse.swt.events.SelectionEvent;
36  import org.eclipse.swt.layout.GridData;
37  import org.eclipse.swt.layout.GridLayout;
38  import org.eclipse.swt.widgets.Combo;
39  import org.eclipse.swt.widgets.Composite;
40  import org.eclipse.swt.widgets.Label;
41  import org.eclipse.swt.widgets.Link;
42  import org.eclipse.swt.widgets.Text;
43  import org.osgi.service.useradmin.User;
44  
45  /** Provides basic information about a task in a form context */
46  public class TaskBasicHeader extends Composite {
47  	private static final long serialVersionUID = -5813631462166187272L;
48  
49  	// Context
50  	private final Session session;
51  
52  	private final UserAdminService userAdminService;
53  	private final ResourcesService resourceService;
54  	private final ActivitiesService activityService;
55  	private final SystemWorkbenchService systemWorkbenchService;
56  	private final Node task;
57  	private final String taskTypeId;
58  
59  	// local cache
60  	private String assignedToGroupId;
61  	// private boolean isBeingEdited;
62  	private List<String> hiddenItemIds;
63  	private List<String> modifiedPaths = new ArrayList<String>();
64  
65  	// UI Context
66  	private final ConnectEditor editor;
67  	private final MyFormPart myFormPart;
68  	private final FormToolkit toolkit;
69  
70  	// COMPOSITES
71  	// Edit
72  	private Combo statusCmb;
73  	private DateTextPart dueDateCmp;
74  	private DateTextPart wakeUpDateCmp;
75  	private Link changeAssignationLk;
76  	// Read Only
77  	private Label statusROLbl;
78  	private Label assignedToROLbl;
79  
80  	// Both edit and RO
81  	private LinkListPart relatedCmp;
82  	private Text titleTxt;
83  	private Text descTxt;
84  
85  	private DateFormat dtFormat = new SimpleDateFormat(ConnectConstants.DEFAULT_DATE_TIME_FORMAT);
86  	private DateFormat dateFormat = new SimpleDateFormat(ConnectConstants.DEFAULT_DATE_FORMAT);
87  
88  	public TaskBasicHeader(ConnectEditor editor, Composite parent, int style, UserAdminService uas,
89  			ResourcesService resourceService, ActivitiesService activityService,
90  			SystemWorkbenchService systemWorkbenchService, String taskTypeId, Node task) {
91  		this(editor, parent, style, uas, resourceService, activityService, systemWorkbenchService, taskTypeId, task,
92  				null);
93  	}
94  
95  	public TaskBasicHeader(ConnectEditor editor, Composite parent, int style, UserAdminService uas,
96  			ResourcesService resourceService, ActivitiesService activityService,
97  			SystemWorkbenchService systemWorkbenchService, String taskTypeId, Node task, List<String> hiddenItemIds) {
98  		super(parent, style);
99  		this.editor = editor;
100 		this.toolkit = editor.getFormToolkit();
101 		this.resourceService = resourceService;
102 		this.activityService = activityService;
103 		this.userAdminService = uas;
104 		this.systemWorkbenchService = systemWorkbenchService;
105 		this.taskTypeId = taskTypeId;
106 		this.task = task;
107 
108 		// Caches a few context object to ease implementation
109 		session = ConnectJcrUtils.getSession(task);
110 
111 		this.hiddenItemIds = hiddenItemIds;
112 
113 		// Initialise the form
114 		myFormPart = new MyFormPart(this);
115 		myFormPart.initialize(editor.getManagedForm());
116 		editor.getManagedForm().addPart(myFormPart);
117 	}
118 
119 	public void refresh() {
120 		myFormPart.refresh();
121 	}
122 
123 	private class MyFormPart extends AbstractPanelFormPart {
124 
125 		public MyFormPart(Composite parent) {
126 			super(parent, editor, task);
127 		}
128 
129 		@Override
130 		public void commit(boolean onSave) {
131 			super.commit(onSave);
132 			if (onSave) {
133 				ConnectJcrUtils.saveAndPublish(task, false);
134 				String path = ConnectJcrUtils.getPath(task);
135 				if (!modifiedPaths.contains(path))
136 					modifiedPaths.add(path);
137 				ConnectJcrUtils.checkPoint(ConnectJcrUtils.getSession(task), modifiedPaths, true);
138 				modifiedPaths.clear();
139 			}
140 		}
141 
142 		protected void reCreateChildComposite(Composite panel, Node node) {
143 			if (isEditing())
144 				createEditComposite(panel);
145 			else
146 				createROComposite(panel);
147 			refreshContent(panel, node);
148 		}
149 
150 		protected void refreshContent(Composite parent, Node node) {
151 			if (TaskBasicHeader.this.isDisposed())
152 				return;
153 			if (isEditing()) {
154 				refreshStatusCombo(statusCmb, node);
155 
156 				dueDateCmp.refresh();
157 				wakeUpDateCmp.refresh();
158 				// update current assigned to group cache here
159 				String manager = activityService.getAssignedToDisplayName(node);
160 				manager += " ~ <a>Change</a>";
161 				changeAssignationLk.setText(manager);
162 				changeAssignationLk.getParent().layout();
163 			} else {
164 				statusROLbl.setText(getStatusText());
165 				assignedToROLbl.setText(activityService.getAssignedToDisplayName(task));
166 			}
167 
168 			ConnectWorkbenchUtils.refreshFormTextWidget(editor, titleTxt, task, Property.JCR_TITLE);
169 			ConnectWorkbenchUtils.refreshFormTextWidget(editor, descTxt, task, Property.JCR_DESCRIPTION);
170 			relatedCmp.refresh();
171 			// Refresh the parent because the whole header must be
172 			// re-layouted if some added relations triggers the creation
173 			// of a new line of the row data
174 			parent.getParent().layout(true, true);
175 		}
176 	}
177 
178 	/* READ ONLY LAYOUT */
179 	private void createROComposite(Composite parent) {
180 		parent.setLayout(new GridLayout(2, false));
181 
182 		ConnectUiUtils.createBoldLabel(toolkit, parent, "Status");
183 		statusROLbl = new Label(parent, SWT.NO_FOCUS | SWT.WRAP);
184 		statusROLbl.setLayoutData(EclipseUiUtils.fillWidth());
185 
186 		ConnectUiUtils.createBoldLabel(toolkit, parent, "Assigned to");
187 		assignedToROLbl = new Label(parent, SWT.NO_FOCUS | SWT.WRAP);
188 		assignedToROLbl.setLayoutData(EclipseUiUtils.fillWidth());
189 
190 		// RELATED ENTITIES
191 		// Label label =
192 		ConnectUiUtils.createBoldLabel(toolkit, parent, "Related to");
193 		relatedCmp = new LinkListPart(editor, myFormPart, parent, SWT.NO_FOCUS, systemWorkbenchService, task,
194 				ActivitiesNames.ACTIVITIES_RELATED_TO, hiddenItemIds);
195 		relatedCmp.setLayoutData(EclipseUiUtils.fillWidth());
196 
197 		// Title
198 		ConnectUiUtils.createBoldLabel(parent, "Title");
199 		titleTxt = toolkit.createText(parent, "", SWT.BORDER);
200 		titleTxt.setLayoutData(EclipseUiUtils.fillWidth());
201 
202 		// Description
203 		ConnectUiUtils.createBoldLabel(parent, "Description");
204 		descTxt = toolkit.createText(parent, "", SWT.BORDER);
205 		descTxt.setLayoutData(EclipseUiUtils.fillWidth());
206 	}
207 
208 	/* EDIT LAYOUT */
209 	private void createEditComposite(Composite parent) {
210 		parent.setLayout(new GridLayout(4, false));
211 
212 		// 1st line (NOTE: it defines the grid data layout of this part)
213 		ConnectUiUtils.createBoldLabel(toolkit, parent, "Status");
214 		statusCmb = new Combo(parent, SWT.READ_ONLY);
215 		statusCmb.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false));
216 
217 		// DUE DATE
218 		ConnectUiUtils.createBoldLabel(toolkit, parent, "Due date");
219 		dueDateCmp = new DateTextPart(editor, parent, SWT.NO_FOCUS, myFormPart, task,
220 				ActivitiesNames.ACTIVITIES_DUE_DATE);
221 		dueDateCmp.setLayoutData(EclipseUiUtils.fillWidth());
222 
223 		// ASSIGNED TO
224 		ConnectUiUtils.createBoldLabel(toolkit, parent, "Assigned to");
225 		changeAssignationLk = new Link(parent, SWT.NONE);
226 		changeAssignationLk.setLayoutData(EclipseUiUtils.fillWidth());
227 
228 		// WAKE UP DATE
229 		ConnectUiUtils.createBoldLabel(toolkit, parent, "Wake up date");
230 		wakeUpDateCmp = new DateTextPart(editor, parent, SWT.NO_FOCUS, myFormPart, task,
231 				ActivitiesNames.ACTIVITIES_WAKE_UP_DATE);
232 		wakeUpDateCmp.setLayoutData(EclipseUiUtils.fillWidth());
233 
234 		// RELATED ENTITIES
235 		ConnectUiUtils.createBoldLabel(toolkit, parent, "Related to");
236 		relatedCmp = new LinkListPart(editor, myFormPart, parent, SWT.NO_FOCUS, systemWorkbenchService, task,
237 				ActivitiesNames.ACTIVITIES_RELATED_TO, hiddenItemIds);
238 		relatedCmp.setLayoutData(EclipseUiUtils.fillWidth(3));
239 		relatedCmp.layout();
240 
241 		// Title
242 		ConnectUiUtils.createBoldLabel(parent, "Title");
243 		titleTxt = toolkit.createText(parent, "", SWT.BORDER);
244 		titleTxt.setLayoutData(EclipseUiUtils.fillWidth(3));
245 
246 		// Description
247 		ConnectUiUtils.createBoldLabel(parent, "Description");
248 		descTxt = toolkit.createText(parent, "", SWT.BORDER);
249 		descTxt.setLayoutData(EclipseUiUtils.fillWidth(3));
250 
251 		// Add listeners
252 		dueDateCmp.setFormPart(myFormPart);
253 		wakeUpDateCmp.setFormPart(myFormPart);
254 		addStatusCmbSelListener(myFormPart, statusCmb, task, ActivitiesNames.ACTIVITIES_TASK_STATUS,
255 				PropertyType.STRING);
256 		ConnectWorkbenchUtils.addModifyListener(titleTxt, task, Property.JCR_TITLE, myFormPart);
257 		ConnectWorkbenchUtils.addModifyListener(descTxt, task, Property.JCR_DESCRIPTION, myFormPart);
258 		addChangeAssignListener();
259 	}
260 
261 	private String getStatusText() {
262 		try {
263 			StringBuilder builder = new StringBuilder();
264 			String status = ConnectJcrUtils.get(task, ActivitiesNames.ACTIVITIES_TASK_STATUS);
265 
266 			String dueDateStr = null;
267 			if (task.hasProperty(ActivitiesNames.ACTIVITIES_DUE_DATE)) {
268 				Calendar dueDate = task.getProperty(ActivitiesNames.ACTIVITIES_DUE_DATE).getDate();
269 				dueDateStr = dateFormat.format(dueDate.getTime());
270 			}
271 			builder.append(status);
272 
273 			if (activityService.isTaskDone(task)) {
274 				String closeBy = ConnectJcrUtils.get(task, ActivitiesNames.ACTIVITIES_CLOSED_BY);
275 				Calendar closedDate = task.getProperty(ActivitiesNames.ACTIVITIES_CLOSE_DATE).getDate();
276 				builder.append(" - Marked as closed by ").append(userAdminService.getUserDisplayName(closeBy));
277 				builder.append(" on ").append(dtFormat.format(closedDate.getTime())).append(".");
278 				if (EclipseUiUtils.notEmpty(dueDateStr))
279 					builder.append(" Due date was ").append(dueDateStr);
280 			} else if (activityService.isTaskSleeping(task)) {
281 				Calendar wakeUpDate = task.getProperty(ActivitiesNames.ACTIVITIES_WAKE_UP_DATE).getDate();
282 				builder.append(" - Sleeping until  ");
283 				builder.append(dateFormat.format(wakeUpDate.getTime()));
284 				if (EclipseUiUtils.notEmpty(dueDateStr))
285 					builder.append(".  Due date is ").append(dueDateStr);
286 			} else if (EclipseUiUtils.notEmpty(dueDateStr))
287 				builder.append(" - Due date is ").append(dueDateStr);
288 
289 			return builder.toString();
290 		} catch (RepositoryException e) {
291 			throw new ActivitiesException("Unable to get status text for task " + task, e);
292 		}
293 	}
294 
295 	// HELPERS
296 	/** Override this to add specific behaviour on status change */
297 	protected void addStatusCmbSelListener(final AbstractFormPart part, final Combo combo, final Node entity,
298 			final String propName, final int propType) {
299 		combo.addSelectionListener(new SelectionAdapter() {
300 			private static final long serialVersionUID = 1L;
301 
302 			@Override
303 			public void widgetSelected(SelectionEvent e) {
304 				int index = combo.getSelectionIndex();
305 				if (index != -1) {
306 					String newStatus = combo.getItem(index);
307 					try {
308 						if (activityService.updateStatus(taskTypeId, task, newStatus, modifiedPaths))
309 							part.markDirty();
310 					} catch (RepositoryException e1) {
311 						throw new ActivitiesException("Cannot update status to " + newStatus + " for " + task, e1);
312 					}
313 				}
314 
315 			}
316 		});
317 	}
318 
319 	/** Override this to add specific rights for status change */
320 	protected void refreshStatusCombo(Combo combo, Node currTask) {
321 		List<String> values = resourceService.getTemplateCatalogue(session, taskTypeId,
322 				ActivitiesNames.ACTIVITIES_TASK_STATUS, null);
323 		combo.setItems(values.toArray(new String[values.size()]));
324 		ConnectWorkbenchUtils.refreshFormCombo(editor, combo, currTask, ActivitiesNames.ACTIVITIES_TASK_STATUS);
325 		combo.setEnabled(editor.isEditing());
326 	}
327 
328 	private void addChangeAssignListener() {
329 		changeAssignationLk.addSelectionListener(new SelectionAdapter() {
330 			private static final long serialVersionUID = 1L;
331 
332 			@Override
333 			public void widgetSelected(final SelectionEvent event) {
334 				try {
335 					PickUpUserDialog diag = new PickUpUserDialog(changeAssignationLk.getShell(), "Choose a group",
336 							userAdminService.getUserAdmin());
337 					int result = diag.open();
338 					if (Window.OK == result) {
339 						User newGroup = diag.getSelected();
340 						if (newGroup == null)
341 							return;
342 
343 						String newGroupName = newGroup.getName();
344 						if (newGroupName.equals(assignedToGroupId))
345 							return; // nothing has changed
346 						else {
347 							// Update value
348 							task.setProperty(ActivitiesNames.ACTIVITIES_ASSIGNED_TO, newGroupName);
349 							// update cache and display.
350 							assignedToGroupId = newGroupName;
351 							changeAssignationLk
352 									.setText(userAdminService.getUserDisplayName(newGroupName) + "  ~ <a>Change</a>");
353 							myFormPart.markDirty();
354 						}
355 					}
356 
357 					// Node newNode = diag.getSelected();
358 					// if (assignedToNode != null
359 					// && newNode.getPath().equals(
360 					// assignedToNode.getPath()))
361 					// return; // nothing has changed
362 					// else {
363 					// // Update value
364 					// String groupId = newNode.getProperty(
365 					// PeopleNames.PEOPLE_GROUP_ID).getString();
366 					// task.setProperty(PeopleNames.PEOPLE_ASSIGNED_TO,
367 					// groupId);
368 					// // update cache and display.
369 					// assignedToNode = newNode;
370 					// changeAssignationLk.setText(ConnectJcrUtils.get(
371 					// assignedToNode, Property.JCR_TITLE)
372 					// + " ~ <a>Change</a>");
373 					// myFormPart.markDirty();
374 					// }
375 					// }
376 				} catch (RepositoryException re) {
377 					throw new ActivitiesException("Unable to change assignation for node " + task, re);
378 				}
379 			}
380 		});
381 	}
382 }