View Javadoc
1   package org.argeo.tracker.e4.parts;
2   
3   import static org.argeo.eclipse.ui.EclipseUiUtils.notEmpty;
4   
5   import java.util.ArrayList;
6   import java.util.List;
7   
8   import javax.jcr.Node;
9   import javax.jcr.Property;
10  import javax.jcr.PropertyType;
11  import javax.jcr.RepositoryException;
12  import javax.jcr.Session;
13  import javax.jcr.Value;
14  
15  import org.argeo.activities.ActivitiesNames;
16  import org.argeo.cms.ui.CmsEditable;
17  import org.argeo.cms.ui.eclipse.forms.AbstractFormPart;
18  import org.argeo.cms.ui.eclipse.forms.FormToolkit;
19  import org.argeo.cms.ui.eclipse.forms.IManagedForm;
20  import org.argeo.cms.util.CmsUtils;
21  import org.argeo.connect.ui.ConnectImages;
22  import org.argeo.connect.ui.ConnectWorkbenchUtils;
23  import org.argeo.connect.util.ConnectJcrUtils;
24  import org.argeo.eclipse.ui.EclipseUiUtils;
25  import org.argeo.tracker.TrackerException;
26  import org.argeo.tracker.TrackerNames;
27  import org.argeo.tracker.TrackerTypes;
28  import org.argeo.tracker.core.TrackerUtils;
29  import org.argeo.tracker.ui.dialogs.ConfigureIssueWizard;
30  import org.eclipse.jface.action.Action;
31  import org.eclipse.jface.action.ToolBarManager;
32  import org.eclipse.jface.resource.ImageDescriptor;
33  import org.eclipse.jface.window.Window;
34  import org.eclipse.jface.wizard.WizardDialog;
35  import org.eclipse.swt.SWT;
36  import org.eclipse.swt.custom.ScrolledComposite;
37  import org.eclipse.swt.events.SelectionAdapter;
38  import org.eclipse.swt.events.SelectionEvent;
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.Combo;
43  import org.eclipse.swt.widgets.Composite;
44  import org.eclipse.swt.widgets.Label;
45  import org.eclipse.swt.widgets.Link;
46  import org.eclipse.swt.widgets.Listener;
47  import org.eclipse.swt.widgets.Shell;
48  
49  /** Default editor to display and edit an issue */
50  public class IssueEditor extends AbstractTrackerEditor implements CmsEditable {
51  	// private final static Log log = LogFactory.getLog(IssueEditor.class);
52  	// private static final long serialVersionUID = -1934562242002470050L;
53  	//
54  	// public static final String ID = TrackerUiPlugin.PLUGIN_ID + ".issueEditor";
55  
56  	// Context
57  	private Session session;
58  	private Node issue;
59  	private Node project;
60  
61  	// public void init(IEditorSite site, IEditorInput input) throws
62  	// PartInitException {
63  	// super.init(site, input);
64  	// }
65  
66  	@Override
67  	protected void addPages() {
68  		// Cache to ease implementation
69  		issue = getNode();
70  		session = ConnectJcrUtils.getSession(issue);
71  		project = TrackerUtils.getRelatedProject(getTrackerService(), issue);
72  		try {
73  			addPage(new IssueMainPage(this));
74  
75  			// if (CurrentUser.isInRole(NodeConstants.ROLE_ADMIN))
76  			// addPage(new TechnicalInfoPage(this, ID + ".techInfoPage", getNode()));
77  		} catch (Exception e) {
78  			throw new TrackerException("Cannot add pages for editor of " + getNode(), e);
79  		}
80  	}
81  
82  	/** Overwrite to provide a specific part Name */
83  	protected void updatePartName() {
84  		String name = getIssueTitle();
85  		if (notEmpty(name))
86  			setPartName(name);
87  		else
88  			super.updatePartName();
89  	}
90  
91  	@Override
92  	public Boolean canEdit() {
93  		return true;
94  	}
95  
96  	@Override
97  	public Boolean isEditing() {
98  		return true;
99  	}
100 
101 	@Override
102 	public void startEditing() {
103 	}
104 
105 	@Override
106 	public void stopEditing() {
107 	}
108 
109 	// Specific pages
110 	private class IssueMainPage extends AbstractEditorPage {
111 		public final static String PAGE_ID = "issueMainPage";
112 
113 		private Combo statusCmb;
114 		private Link projectLk;
115 		private Link milestoneLk;
116 		private Link assignedToLk;
117 		private Composite versionsCmp;
118 		private Composite componentsCmp;
119 		// private Map<String, Link> versionLks;
120 		// private Link reporterLk;
121 		private Label descLbl;
122 
123 		public IssueMainPage(AbstractTrackerEditor editor) {
124 			super(editor, PAGE_ID, "Main");
125 		}
126 
127 		protected void createFormContent(final IManagedForm mf) {
128 			ScrolledComposite form = mf.getForm();
129 			Composite body = new Composite(form, SWT.NONE);
130 			GridLayout layout = new GridLayout();
131 			body.setLayout(layout);
132 			appendOverviewPart(body);
133 
134 			Composite commentFormPart = new CommentListFormPart(getPageManagedForm(), body, SWT.NO_FOCUS,
135 					getTrackerService(), getNode());
136 			commentFormPart.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
137 		}
138 
139 		/** Creates the general section */
140 		private void appendOverviewPart(Composite parent) {
141 			FormToolkit tk = getPageManagedForm().getToolkit();
142 
143 			final Section section = TrackerUiUtils.addFormSection(tk, parent, getIssueTitle());
144 			section.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
145 
146 			Composite body = (Composite) section.getClient();
147 			GridLayout layout = new GridLayout();
148 			layout.numColumns = 6;
149 			body.setLayout(layout);
150 
151 			// Status
152 			createFormBoldLabel(tk, body, "Status");
153 			statusCmb = new Combo(body, SWT.READ_ONLY);
154 			statusCmb.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM));
155 
156 			// Project
157 			createFormBoldLabel(tk, body, "Project");
158 			projectLk = new Link(body, SWT.NONE);
159 			projectLk.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, true, false));
160 			configureOpenLink(projectLk, project);
161 
162 			// Target milestone
163 			createFormBoldLabel(tk, body, "Target milestone");
164 			milestoneLk = new Link(body, SWT.NONE);
165 			milestoneLk.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, true, false));
166 
167 			// Assigned to
168 			createFormBoldLabel(tk, body, "Assigned to");
169 			assignedToLk = new Link(body, SWT.NONE);
170 			assignedToLk.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM));
171 
172 			// Versions
173 			TrackerUiUtils.createFormBoldLabel(tk, body, "Impacted versions");
174 			versionsCmp = new Composite(body, SWT.NO_FOCUS);
175 			versionsCmp.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
176 
177 			// Components
178 			TrackerUiUtils.createFormBoldLabel(tk, body, "Related components");
179 			componentsCmp = new Composite(body, SWT.NO_FOCUS);
180 			componentsCmp.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
181 
182 			// TODO add stack traces and documents
183 
184 			// Description
185 			GridData twd = (GridData) TrackerUiUtils.createFormBoldLabel(tk, body, "Details").getLayoutData();
186 			// twd.valign = SWT.TOP;
187 			descLbl = new Label(body, SWT.WRAP);
188 			twd = new GridData(SWT.FILL, SWT.TOP, true, false);
189 			// twd.colspan = 5;
190 			descLbl.setLayoutData(twd);
191 
192 			// // Components
193 			// TrackerUiUtils.createFormBoldLabel(tk, body, "Components");
194 			// ComponentListFormPart clfp = new
195 			// ComponentListFormPart(getManagedForm(), body, SWT.NO_FOCUS);
196 			// twd = new GridData(FILL_GRAB);
197 			// twd.colspan = 3;
198 			// clfp.setLayoutData(twd);
199 
200 			//
201 			// // Versions
202 			// TrackerUiUtils.createFormBoldLabel(tk, body, "Versions");
203 			// VersionListFormPart vlfp = new
204 			// VersionListFormPart(getManagedForm(), body, SWT.NO_FOCUS);
205 			// twd = new GridData(FILL_GRAB);
206 			// twd.colspan = 3;
207 			// vlfp.setLayoutData(twd);
208 
209 			SectionPart part = new SectionPart((Section) body.getParent()) {
210 
211 				@Override
212 				public void refresh() {
213 					refreshStatusCombo(statusCmb, issue);
214 					statusCmb.setText(ConnectJcrUtils.get(issue, ActivitiesNames.ACTIVITIES_TASK_STATUS));
215 
216 					Node milestone = TrackerUtils.getMilestone(getTrackerService(), issue);
217 					if (milestone != null)
218 						configureOpenLink(milestoneLk, milestone);
219 
220 					String manager = getActivitiesService().getAssignedToDisplayName(issue);
221 					String importance = TrackerUtils.getImportanceLabel(issue);
222 					String priority = TrackerUtils.getPriorityLabel(issue);
223 					String tmp = ConnectJcrUtils.concatIfNotEmpty(importance, priority, "/");
224 					if (EclipseUiUtils.notEmpty(tmp))
225 						manager += " (" + tmp + " )";
226 					// TODO make it clickable
227 					assignedToLk.setText(manager);
228 
229 					// Versions
230 					List<Node> nodes = new ArrayList<Node>();
231 					try {
232 						if (issue.hasProperty(TrackerNames.TRACKER_VERSION_IDS)) {
233 							Value[] values = issue.getProperty(TrackerNames.TRACKER_VERSION_IDS).getValues();
234 							for (int i = 0; i < values.length; i++) {
235 								Node version = TrackerUtils.getVersionById(project, values[i].getString());
236 								if (version != null)
237 									nodes.add(version);
238 							}
239 						}
240 						// if (!nodes.isEmpty())
241 						populateMuliValueClickableList(versionsCmp, nodes.toArray(new Node[0]));
242 
243 						// Components
244 						nodes = new ArrayList<Node>();
245 						if (issue.hasProperty(TrackerNames.TRACKER_COMPONENT_IDS)) {
246 							Value[] values = issue.getProperty(TrackerNames.TRACKER_COMPONENT_IDS).getValues();
247 							for (int i = 0; i < values.length; i++) {
248 								Node component = TrackerUtils.getComponentById(project, values[i].getString());
249 								if (component != null)
250 									nodes.add(component);
251 							}
252 						}
253 						// if (!nodes.isEmpty())
254 						populateMuliValueClickableList(componentsCmp, nodes.toArray(new Node[0]));
255 
256 					} catch (RepositoryException e) {
257 						throw new TrackerException("Cannot update info for " + issue, e);
258 					}
259 
260 					String desc = ConnectJcrUtils.get(issue, Property.JCR_DESCRIPTION);
261 					descLbl.setText(desc);
262 
263 					parent.layout(true, true);
264 					section.setFocus();
265 					super.refresh();
266 				}
267 			};
268 			addStatusCmbSelListener(part, statusCmb, issue, ActivitiesNames.ACTIVITIES_TASK_STATUS,
269 					PropertyType.STRING);
270 
271 			getPageManagedForm().addPart(part);
272 			addMainSectionMenu(part);
273 		}
274 
275 	}
276 
277 	// SECTION MENU
278 	private void addMainSectionMenu(SectionPart sectionPart) {
279 		ToolBarManager toolBarManager = TrackerUiUtils.addMenu(sectionPart.getSection());
280 		String tooltip = "Edit this issue main information";
281 		Action action = new OpenConfigureDialog(tooltip, ConnectImages.IMG_DESC_EDIT, sectionPart);
282 		toolBarManager.add(action);
283 		toolBarManager.update(true);
284 	}
285 
286 	// MENU ACTIONS
287 	private class OpenConfigureDialog extends Action {
288 		private static final long serialVersionUID = -6798429720348536525L;
289 		private final SectionPart sectionPart;
290 
291 		private OpenConfigureDialog(String name, ImageDescriptor img, SectionPart sectionPart) {
292 			super(name, img);
293 			this.sectionPart = sectionPart;
294 		}
295 
296 		@Override
297 		public void run() {
298 			Shell currShell = sectionPart.getSection().getShell();
299 			ConfigureIssueWizard wizard = new ConfigureIssueWizard(getUserAdminService(), getTrackerService(), issue);
300 			WizardDialog dialog = new WizardDialog(currShell, wizard);
301 			try {
302 				if (dialog.open() == Window.OK && issue.getSession().hasPendingChanges()) {
303 					updatePartName();
304 					sectionPart.getSection().setText(getIssueTitle());
305 					sectionPart.refresh();
306 					sectionPart.markDirty();
307 					sectionPart.getSection().setFocus();
308 				}
309 			} catch (RepositoryException e) {
310 				throw new TrackerException("Cannot check session state on " + issue, e);
311 			}
312 		}
313 	}
314 
315 	// LISTENERS
316 	private void addStatusCmbSelListener(final AbstractFormPart part, final Combo combo, final Node entity,
317 			final String propName, final int propType) {
318 		combo.addSelectionListener(new SelectionAdapter() {
319 			private static final long serialVersionUID = 1L;
320 
321 			@Override
322 			public void widgetSelected(SelectionEvent e) {
323 				int index = combo.getSelectionIndex();
324 				if (index != -1) {
325 					String selectedStatus = combo.getItem(index);
326 					try {
327 						if (getActivitiesService().updateStatus(TrackerTypes.TRACKER_ISSUE, issue, selectedStatus,
328 								new ArrayList<String>()))
329 							part.markDirty();
330 					} catch (RepositoryException e1) {
331 						throw new TrackerException("Cannot update status to " + selectedStatus + " for " + issue, e1);
332 					}
333 				}
334 			}
335 		});
336 	}
337 
338 	/** Override this to add specific rights for status change */
339 	protected void refreshStatusCombo(Combo combo, Node currTask) {
340 		List<String> values = getResourcesService().getTemplateCatalogue(session, TrackerTypes.TRACKER_ISSUE,
341 				ActivitiesNames.ACTIVITIES_TASK_STATUS, null);
342 		combo.setItems(values.toArray(new String[values.size()]));
343 		ConnectWorkbenchUtils.refreshFormCombo(IssueEditor.this, combo, currTask,
344 				ActivitiesNames.ACTIVITIES_TASK_STATUS);
345 		combo.setEnabled(IssueEditor.this.isEditing());
346 	}
347 
348 	private String getIssueTitle() {
349 		String id = ConnectJcrUtils.get(getNode(), TrackerNames.TRACKER_ID);
350 		String name = ConnectJcrUtils.get(getNode(), Property.JCR_TITLE);
351 		if (notEmpty(name)) {
352 			Node project = TrackerUtils.getProjectFromChild(getNode());
353 			String pname = ConnectJcrUtils.get(project, Property.JCR_TITLE);
354 			name = name + (notEmpty(pname) ? " (" + pname + ")" : "");
355 		}
356 		return "#" + id + " " + name;
357 	}
358 
359 	// LOCAL HELPERS
360 	private void populateMuliValueClickableList(Composite parent, Node[] nodes) {
361 		CmsUtils.clear(parent);
362 		GridData twd = ((GridData) parent.getLayoutData());
363 		if (nodes == null || nodes.length < 1) {
364 			twd.heightHint = 0;
365 			return;
366 		} else
367 			twd.heightHint = SWT.DEFAULT;
368 
369 		RowLayout rl = new RowLayout(SWT.HORIZONTAL);
370 		rl.wrap = true;
371 		rl.marginLeft = rl.marginTop = rl.marginBottom = 0;
372 		rl.marginRight = 8;
373 		parent.setLayout(rl);
374 
375 		for (Node node : nodes) {
376 			String value = ConnectJcrUtils.get(node, Property.JCR_TITLE);
377 			Link link = new Link(parent, SWT.NONE);
378 			CmsUtils.markup(link);
379 			link.setText(" <a>" + value + "</a>");
380 
381 			link.addSelectionListener(new SelectionAdapter() {
382 				private static final long serialVersionUID = 1L;
383 
384 				@Override
385 				public void widgetSelected(final SelectionEvent event) {
386 					// CommandUtils.callCommand(getAppWorkbenchService().getOpenEntityEditorCmdId(),
387 					// ConnectEditor.PARAM_JCR_ID, ConnectJcrUtils.getIdentifier(node));
388 					getAppWorkbenchService().openEntityEditor(node);
389 				}
390 			});
391 		}
392 	}
393 
394 	private void configureOpenLink(Link link, Node targetNode) {
395 		link.setText("<a>" + ConnectJcrUtils.get(targetNode, Property.JCR_TITLE) + "</a>");
396 
397 		// Remove existing if necessary
398 		Listener[] existings = link.getListeners(SWT.Selection);
399 		for (Listener l : existings)
400 			link.removeListener(SWT.Selection, l);
401 
402 		link.addSelectionListener(new SelectionAdapter() {
403 			private static final long serialVersionUID = 1L;
404 
405 			@Override
406 			public void widgetSelected(SelectionEvent e) {
407 				// CommandUtils.callCommand(getAppWorkbenchService().getOpenEntityEditorCmdId(),
408 				// ConnectEditor.PARAM_JCR_ID, ConnectJcrUtils.getIdentifier(targetNode));
409 				getAppWorkbenchService().openEntityEditor(targetNode);
410 			}
411 		});
412 	}
413 
414 //	private Label createFormBoldLabel(FormToolkit toolkit, Composite parent, String value) {
415 //		// We add a blank space before to workaround the cropping of the word
416 //		// first letter in some OS/Browsers (typically MAC/Firefox 31 )
417 //		Label label = toolkit.createLabel(parent, " " + value, SWT.END);
418 //		label.setFont(EclipseUiUtils.getBoldFont(parent));
419 //		GridData twd = new GridData(SWT.END, SWT.BOTTOM);
420 //		label.setLayoutData(twd);
421 //		return label;
422 //	}
423 }