View Javadoc
1   /*
2    * Copyright (C) 2007-2012 Argeo GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.argeo.people.ui.dialogs;
17  
18  import java.util.List;
19  
20  import javax.jcr.Node;
21  import javax.jcr.NodeIterator;
22  import javax.jcr.Property;
23  import javax.jcr.RepositoryException;
24  import javax.jcr.Session;
25  import javax.jcr.nodetype.NodeType;
26  import javax.jcr.query.Query;
27  import javax.jcr.query.QueryResult;
28  
29  import org.argeo.cms.util.CmsUtils;
30  import org.argeo.connect.resources.ResourcesService;
31  import org.argeo.connect.ui.ConnectUiConstants;
32  import org.argeo.connect.ui.ConnectUiStyles;
33  import org.argeo.connect.ui.SystemWorkbenchService;
34  import org.argeo.connect.ui.widgets.DelayedText;
35  import org.argeo.connect.util.ConnectJcrUtils;
36  import org.argeo.connect.util.XPathUtils;
37  import org.argeo.eclipse.ui.EclipseUiUtils;
38  import org.argeo.jcr.JcrUtils;
39  import org.argeo.people.PeopleConstants;
40  import org.argeo.people.PeopleException;
41  import org.argeo.people.PeopleNames;
42  import org.argeo.people.PeopleService;
43  import org.argeo.people.PeopleTypes;
44  import org.argeo.people.ui.PeopleMsg;
45  import org.argeo.people.ui.providers.EntitySingleColumnLabelProvider;
46  import org.eclipse.jface.dialogs.MessageDialog;
47  import org.eclipse.jface.dialogs.TrayDialog;
48  import org.eclipse.jface.layout.TableColumnLayout;
49  import org.eclipse.jface.viewers.ColumnWeightData;
50  import org.eclipse.jface.viewers.ILabelProvider;
51  import org.eclipse.jface.viewers.ILazyContentProvider;
52  import org.eclipse.jface.viewers.ISelectionChangedListener;
53  import org.eclipse.jface.viewers.IStructuredSelection;
54  import org.eclipse.jface.viewers.SelectionChangedEvent;
55  import org.eclipse.jface.viewers.TableViewer;
56  import org.eclipse.jface.viewers.Viewer;
57  import org.eclipse.rap.rwt.RWT;
58  import org.eclipse.rap.rwt.service.ServerPushSession;
59  import org.eclipse.swt.SWT;
60  import org.eclipse.swt.events.ModifyEvent;
61  import org.eclipse.swt.events.ModifyListener;
62  import org.eclipse.swt.events.SelectionAdapter;
63  import org.eclipse.swt.events.SelectionEvent;
64  import org.eclipse.swt.events.TraverseEvent;
65  import org.eclipse.swt.events.TraverseListener;
66  import org.eclipse.swt.graphics.Point;
67  import org.eclipse.swt.layout.GridData;
68  import org.eclipse.swt.layout.GridLayout;
69  import org.eclipse.swt.widgets.Button;
70  import org.eclipse.swt.widgets.Composite;
71  import org.eclipse.swt.widgets.Control;
72  import org.eclipse.swt.widgets.Label;
73  import org.eclipse.swt.widgets.Shell;
74  import org.eclipse.swt.widgets.Table;
75  import org.eclipse.swt.widgets.TableColumn;
76  import org.eclipse.swt.widgets.Text;
77  
78  /**
79   * Dialog with a filtered list to create or edit a position for a given person
80   * in an organisation. If editing an existing position, note that both
81   * referenced and referencing entities must be given in order to eventually
82   * remove old reference
83   * 
84   * <p>
85   * It is the dialog duty to correctly initialise what is displayed based on the
86   * parameters passed at instantiation time.
87   * </p>
88   */
89  public class EditJobDialog extends TrayDialog {
90  	private static final long serialVersionUID = -3534660152626908662L;
91  
92  	// Context
93  	private Session session;
94  	private final ResourcesService resourcesService;
95  	private final PeopleService peopleService;
96  	private final SystemWorkbenchService systemWorkbenchService;
97  
98  	// The various field
99  	private Text positionTxt;
100 	private Text selectedItemTxt;
101 	private Node selectedItem = null;
102 	private Text departmentTxt;
103 	private Button isPrimaryBtn;
104 
105 	// Labels
106 	private final String positionLbl = PeopleMsg.position.lead();
107 	private final String chosenItemLbl = PeopleMsg.chosenItem.lead();
108 	private final String departmentLbl = PeopleMsg.department.lead();
109 	private final String primaryLbl = PeopleMsg.isPrimary.lead();
110 
111 	// The search list
112 	private Text filterTxt;
113 	private Button okBtn;
114 	private TableViewer entityViewer;
115 
116 	private final String title;
117 
118 	private boolean isBackward;
119 	private String toSearchNodeType;
120 	private Node oldLinkNode;
121 
122 	// Caches old info to initialize widgets if needed
123 	private String oldPosition = "";
124 	private String oldDepartment = "";
125 	private boolean wasPrimary = false;
126 	private Node oldReferencing;
127 	private Node oldReferenced;
128 
129 	/**
130 	 * 
131 	 * @param parentShell
132 	 * @param title
133 	 * @param peopleService
134 	 * @param referencingNode
135 	 * @param referencedNode
136 	 * @param toSearchNodeType
137 	 * @param isBackward
138 	 *            tells if we must remove referenced (if true) or referencing (if
139 	 *            false) node
140 	 */
141 	public EditJobDialog(Shell parentShell, String title, ResourcesService resourcesService,
142 			PeopleService peopleService, SystemWorkbenchService systemWorkbenchService, Node oldLink, Node toUpdateNode,
143 			boolean isBackward) {
144 		super(parentShell);
145 		this.title = title;
146 		this.resourcesService = resourcesService;
147 		this.peopleService = peopleService;
148 		this.systemWorkbenchService = systemWorkbenchService;
149 
150 		this.isBackward = isBackward;
151 		if (isBackward)
152 			toSearchNodeType = PeopleTypes.PEOPLE_PERSON;
153 		else
154 			toSearchNodeType = PeopleTypes.PEOPLE_ORG;
155 
156 		if (oldLink == null) { // CREATE
157 			session = ConnectJcrUtils.getSession(toUpdateNode);
158 			if (isBackward)
159 				oldReferenced = toUpdateNode;
160 			else
161 				oldReferencing = toUpdateNode;
162 		} else { // UPDATE
163 			this.oldLinkNode = oldLink;
164 			try {
165 				// Initialize with old values
166 				session = oldLink.getSession();
167 				oldPosition = ConnectJcrUtils.get(oldLinkNode, PeopleNames.PEOPLE_ROLE);
168 				oldDepartment = ConnectJcrUtils.get(oldLinkNode, PeopleNames.PEOPLE_DEPARTMENT);
169 				Boolean tmp = ConnectJcrUtils.getBooleanValue(oldLink, PeopleNames.PEOPLE_IS_PRIMARY);
170 				if (tmp != null)
171 					wasPrimary = tmp;
172 
173 				oldReferencing = oldLink.getParent().getParent();
174 				oldReferenced = peopleService.getEntityByUid(session, null,
175 						oldLink.getProperty(PeopleNames.PEOPLE_REF_UID).getString());
176 			} catch (RepositoryException e) {
177 				throw new PeopleException("Unable to initialize link edition", e);
178 			}
179 		}
180 	}
181 
182 	/** Override to provide business specific addition behavior */
183 	protected boolean performFinish() {
184 		// Sanity check
185 		String msg = null;
186 		if (selectedItem == null && oldLinkNode == null)
187 			msg = "Please select an entity.";
188 		if (msg != null) {
189 			MessageDialog.openError(getShell(), "Non valid information", msg);
190 			return false;
191 		}
192 
193 		// Retrieve values
194 		String position = positionTxt.getText();
195 		String department = departmentTxt.getText();
196 		boolean isPrimary = wasPrimary;
197 		if (isPrimaryBtn != null)
198 			isPrimary = isPrimaryBtn.getSelection();
199 		Node person, organisation;
200 
201 		if (isBackward) {
202 			organisation = oldReferenced;
203 			if (selectedItem == null)
204 				person = oldReferencing;
205 			else
206 				person = selectedItem;
207 		} else {
208 			person = oldReferencing;
209 			if (selectedItem == null)
210 				organisation = oldReferenced;
211 			else
212 				organisation = selectedItem;
213 		}
214 
215 		// Real update
216 		peopleService.getPersonService().createOrUpdateJob(oldLinkNode, person, organisation, position, department,
217 				isPrimary);
218 		return true;
219 	}
220 
221 	protected Control createDialogArea(Composite parent) {
222 		// MAIN LAYOUT
223 		Composite dialogarea = (Composite) super.createDialogArea(parent);
224 		dialogarea.setLayout(new GridLayout(2, false));
225 
226 		// The filter
227 		Composite filterCmp = new Composite(dialogarea, SWT.NONE);
228 		filterCmp.setLayoutData(EclipseUiUtils.fillWidth(2));
229 		addFilterPanel(filterCmp);
230 
231 		// The list
232 		Composite listCmp = new Composite(dialogarea, SWT.NONE);
233 		GridData gd = EclipseUiUtils.fillWidth(2);
234 		gd.heightHint = 290;
235 		listCmp.setLayoutData(gd);
236 		entityViewer = createListPart(listCmp,
237 				new EntitySingleColumnLabelProvider(resourcesService, peopleService, systemWorkbenchService));
238 		refreshFilteredList(toSearchNodeType);
239 
240 		// An empty line to give some air to the dialog
241 		Label dummyLbl = new Label(dialogarea, SWT.NONE);
242 		dummyLbl.setText("");
243 		dummyLbl.setLayoutData(EclipseUiUtils.fillWidth(2));
244 
245 		// Display chosen org or person
246 		selectedItemTxt = createLT(dialogarea, chosenItemLbl);
247 		selectedItemTxt.setEnabled(false);
248 		CmsUtils.style(selectedItemTxt, ConnectUiStyles.FORCE_BORDER);
249 
250 		if (isBackward) {
251 			if (oldReferencing != null)
252 				selectedItemTxt.setText(ConnectJcrUtils.get(oldReferencing, Property.JCR_TITLE));
253 		} else {
254 			if (oldReferenced != null)
255 				selectedItemTxt.setText(ConnectJcrUtils.get(oldReferenced, Property.JCR_TITLE));
256 		}
257 		// Role
258 		positionTxt = createLT(dialogarea, positionLbl);
259 		positionTxt.setText(oldPosition);
260 
261 		// Department
262 		departmentTxt = createLT(dialogarea, departmentLbl);
263 		departmentTxt.setText(oldDepartment);
264 
265 		// Is primary
266 		// Display primary check box only when editing a position from a person
267 		// perspective
268 		if (!isBackward) {
269 			isPrimaryBtn = createLC(dialogarea, primaryLbl);
270 			isPrimaryBtn.setSelection(wasPrimary);
271 		}
272 
273 		dialogarea.layout();
274 		// Set the focus on the first field.
275 		filterTxt.setFocus();
276 		if (!systemWorkbenchService.lazyLoadLists())
277 			refreshFilteredList(toSearchNodeType);
278 		return dialogarea;
279 	}
280 
281 	// This dialog life cycle
282 	@Override
283 	protected void okPressed() {
284 		if (performFinish())
285 			super.okPressed();
286 	}
287 
288 	protected Point getInitialSize() {
289 		return new Point(400, 580);
290 	}
291 
292 	protected void configureShell(Shell shell) {
293 		super.configureShell(shell);
294 		shell.setText(title);
295 	}
296 
297 	@Override
298 	public void create() {
299 		super.create();
300 		// prevent calling OK Pressed on filtering
301 		getShell().setDefaultButton(okBtn);
302 	}
303 
304 	/** Overwrite to close session */
305 	public boolean close() {
306 		JcrUtils.logoutQuietly(session);
307 		return super.close();
308 	}
309 
310 	// Specific widgets management
311 
312 	/** Creates label and text. */
313 	protected Text createLT(Composite parent, String label) {
314 		Label lbl = new Label(parent, SWT.RIGHT);
315 		lbl.setText(label);
316 		lbl.setFont(EclipseUiUtils.getBoldFont(parent));
317 		lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
318 		Text text = new Text(parent, SWT.SINGLE | SWT.BORDER);
319 		text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
320 		return text;
321 	}
322 
323 	/** Creates label and check box. */
324 	protected Button createLC(Composite parent, String label) {
325 		Label lbl = new Label(parent, SWT.RIGHT);
326 		lbl.setText(label);
327 		lbl.setFont(EclipseUiUtils.getBoldFont(parent));
328 		lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
329 		Button btn = new Button(parent, SWT.CHECK);
330 		btn.setText("");
331 		btn.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
332 		return btn;
333 	}
334 
335 	protected void addFilterPanel(Composite parent) {
336 		GridLayout layout = EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, false));
337 		layout.horizontalSpacing = 5;
338 		parent.setLayout(layout);
339 
340 		boolean isDyn = systemWorkbenchService.queryWhenTyping();
341 		int style = SWT.BORDER | SWT.SEARCH | SWT.ICON_SEARCH | SWT.ICON_CANCEL;
342 		if (isDyn) {
343 			DelayedText delayedText = new DelayedText(parent, style, ConnectUiConstants.SEARCH_TEXT_DELAY);
344 			final ServerPushSession pushSession = new ServerPushSession();
345 			delayedText.addDelayedModifyListener(pushSession, new ModifyListener() {
346 				private static final long serialVersionUID = 5003010530960334977L;
347 
348 				public void modifyText(ModifyEvent event) {
349 					delayedText.getText().getDisplay().asyncExec(new Runnable() {
350 						@Override
351 						public void run() {
352 							refreshFilteredList(toSearchNodeType);
353 						}
354 					});
355 					pushSession.stop();
356 				}
357 			});
358 			filterTxt = delayedText.getText();
359 		} else
360 			filterTxt = new Text(parent, style);
361 		filterTxt.setMessage(PeopleMsg.searchAndChooseEntity.lead());
362 		filterTxt.setLayoutData(EclipseUiUtils.fillWidth());
363 
364 		okBtn = new Button(parent, SWT.FLAT);
365 		okBtn.setText("Find");
366 
367 		filterTxt.addTraverseListener(new TraverseListener() {
368 			private static final long serialVersionUID = 3886722799404099828L;
369 
370 			@Override
371 			public void keyTraversed(TraverseEvent e) {
372 				if (e.keyCode == SWT.CR) {
373 					e.doit = false;
374 					refreshFilteredList(toSearchNodeType);
375 				}
376 			}
377 		});
378 
379 		okBtn.addSelectionListener(new SelectionAdapter() {
380 			private static final long serialVersionUID = 4305076157959928315L;
381 
382 			@Override
383 			public void widgetSelected(SelectionEvent e) {
384 				refreshFilteredList(toSearchNodeType);
385 			}
386 		});
387 	}
388 
389 	protected TableViewer createListPart(Composite tableComposite, ILabelProvider labelProvider) {
390 		TableViewer v = new TableViewer(tableComposite, SWT.VIRTUAL | SWT.V_SCROLL | SWT.SINGLE);
391 		v.setLabelProvider(labelProvider);
392 
393 		TableColumn singleColumn = new TableColumn(v.getTable(), SWT.LEFT);
394 		TableColumnLayout tableColumnLayout = new TableColumnLayout();
395 		tableColumnLayout.setColumnData(singleColumn, new ColumnWeightData(85));
396 		tableComposite.setLayout(tableColumnLayout);
397 
398 		// Corresponding table & style
399 		Table table = v.getTable();
400 		table.setLinesVisible(true);
401 		table.setHeaderVisible(false);
402 		// Enable markups
403 		table.setData(RWT.MARKUP_ENABLED, Boolean.TRUE);
404 		table.setData(RWT.CUSTOM_ITEM_HEIGHT, Integer.valueOf(24));
405 
406 		// Providers and listeners
407 		v.setContentProvider(new MyLazyContentProvider(v));
408 		v.addSelectionChangedListener(new ISelectionChangedListener() {
409 
410 			@Override
411 			public void selectionChanged(SelectionChangedEvent event) {
412 				// Avoid NPE on filter reset
413 				Object element = ((IStructuredSelection) event.getSelection()).getFirstElement();
414 				if (element == null) {
415 					selectedItem = null;
416 					return;
417 				}
418 
419 				// Only single selection is enabled
420 				Node selectedEntity = (Node) ((IStructuredSelection) event.getSelection()).getFirstElement();
421 				selectedItem = selectedEntity;
422 
423 				try {
424 					if (selectedEntity.isNodeType(NodeType.MIX_TITLE))
425 						selectedItemTxt.setText(ConnectJcrUtils.get(selectedEntity, Property.JCR_TITLE));
426 				} catch (RepositoryException e) {
427 					throw new PeopleException("Unable to update " + "selected item", e);
428 				}
429 				// Sets the focus to next usefull field
430 				positionTxt.setFocus();
431 			}
432 		});
433 		return v;
434 	}
435 
436 	private class MyLazyContentProvider implements ILazyContentProvider {
437 		private static final long serialVersionUID = 1L;
438 		private TableViewer viewer;
439 		private Object[] elements;
440 
441 		public MyLazyContentProvider(TableViewer viewer) {
442 			this.viewer = viewer;
443 		}
444 
445 		public void dispose() {
446 		}
447 
448 		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
449 			// IMPORTANT: don't forget this: an exception will be thrown if a
450 			// selected object is not part of the results anymore.
451 			viewer.setSelection(null);
452 			this.elements = (Object[]) newInput;
453 		}
454 
455 		public void updateElement(int index) {
456 			viewer.replace(elements[index], index);
457 		}
458 	}
459 
460 	protected void refreshFilteredList(String nodeType) {
461 		List<Node> nodes = JcrUtils.nodeIteratorToList(query(nodeType));
462 		entityViewer.setInput(nodes.toArray());
463 		entityViewer.setItemCount(nodes.size());
464 		entityViewer.refresh();
465 	}
466 
467 	protected NodeIterator query(String nodeType) {
468 		String filter = filterTxt.getText();
469 		try {
470 			String xpathQueryStr = "//element(*, " + nodeType + ")";
471 			String attrQuery = XPathUtils.getFreeTextConstraint(filter);
472 			if (EclipseUiUtils.notEmpty(attrQuery))
473 				xpathQueryStr += "[" + attrQuery + "]";
474 			Query xpathQuery = XPathUtils.createQuery(session, xpathQueryStr);
475 			xpathQuery.setLimit(PeopleConstants.QUERY_DEFAULT_LIMIT);
476 			QueryResult result = xpathQuery.execute();
477 			return result.getNodes();
478 		} catch (RepositoryException e) {
479 			throw new PeopleException("Unable to list entities", e);
480 		}
481 	}
482 }