View Javadoc
1   package org.argeo.connect.e4.resources.parts;
2   
3   import java.util.ArrayList;
4   import java.util.List;
5   
6   import javax.jcr.Node;
7   import javax.jcr.Property;
8   import javax.jcr.PropertyType;
9   import javax.jcr.Repository;
10  import javax.jcr.RepositoryException;
11  import javax.jcr.Session;
12  
13  import org.argeo.cms.e4.PrivilegedJob;
14  import org.argeo.connect.ConnectException;
15  import org.argeo.connect.resources.ResourcesNames;
16  import org.argeo.connect.resources.ResourcesService;
17  import org.argeo.connect.ui.ConnectColumnDefinition;
18  import org.argeo.connect.ui.SystemWorkbenchService;
19  import org.argeo.connect.ui.util.TitleIconRowLP;
20  import org.argeo.connect.ui.util.VirtualJcrTableViewer;
21  import org.argeo.connect.ui.widgets.SimpleJcrTableComposite;
22  import org.argeo.connect.util.ConnectJcrUtils;
23  import org.argeo.eclipse.ui.EclipseJcrMonitor;
24  import org.argeo.eclipse.ui.EclipseUiUtils;
25  import org.argeo.eclipse.ui.jcr.lists.JcrColumnDefinition;
26  import org.argeo.jcr.JcrMonitor;
27  import org.argeo.jcr.JcrUtils;
28  import org.eclipse.core.runtime.IProgressMonitor;
29  import org.eclipse.core.runtime.IStatus;
30  import org.eclipse.core.runtime.Status;
31  import org.eclipse.jface.dialogs.MessageDialog;
32  import org.eclipse.jface.viewers.DoubleClickEvent;
33  import org.eclipse.jface.viewers.IDoubleClickListener;
34  import org.eclipse.jface.viewers.ILazyContentProvider;
35  import org.eclipse.jface.viewers.ISelectionChangedListener;
36  import org.eclipse.jface.viewers.IStructuredSelection;
37  import org.eclipse.jface.viewers.SelectionChangedEvent;
38  import org.eclipse.jface.viewers.TableViewer;
39  import org.eclipse.jface.viewers.Viewer;
40  import org.eclipse.jface.wizard.Wizard;
41  import org.eclipse.jface.wizard.WizardPage;
42  import org.eclipse.swt.SWT;
43  import org.eclipse.swt.layout.GridData;
44  import org.eclipse.swt.widgets.Composite;
45  import org.eclipse.swt.widgets.Display;
46  
47  /**
48   * Generic wizard to add a tag like property entities retrieved in the passed
49   * Rows
50   * 
51   * This will return SWT.OK only if the value has been changed, in that case,
52   * underlying session is saved
53   */
54  
55  public class TagOrUntagInstancesWizard extends Wizard {
56  	// private final static Log log = LogFactory.getLog(EditTagWizard.class);
57  
58  	// To be cleaned:
59  	public final static int TYPE_ADD = 1;
60  	public final static int TYPE_REMOVE = 2;
61  
62  	// Context
63  	private SystemWorkbenchService systemWorkbenchService;
64  
65  	// Enable refresh of the calling editor at the end of the job
66  	private Display callingDisplay;
67  
68  	private String tagId;
69  	private Node tagInstance;
70  
71  	private String tagPropName;
72  
73  	private Object[] elements;
74  	private final String selectorName;
75  	private final int actionType;
76  
77  	// Cache to ease implementation
78  	private Session session;
79  	private ResourcesService resourcesService;
80  	private Node tagParent;
81  	private String tagInstanceType;
82  
83  	/**
84  	 * @param actionType
85  	 * @param session
86  	 * @param resourcesService
87  	 * @param systemWorkbenchService
88  	 * @param elements
89  	 * @param selectorName
90  	 * @param tagId
91  	 * @param tagPropName
92  	 */
93  	public TagOrUntagInstancesWizard(Display callingDisplay, int actionType, Session session,
94  			ResourcesService resourcesService, SystemWorkbenchService systemWorkbenchService, Object[] elements,
95  			String selectorName, String tagId, String tagPropName) {
96  
97  		this.callingDisplay = callingDisplay;
98  		this.session = session;
99  		this.resourcesService = resourcesService;
100 		this.systemWorkbenchService = systemWorkbenchService;
101 		this.tagId = tagId;
102 		this.tagPropName = tagPropName;
103 
104 		this.elements = elements;
105 		this.selectorName = selectorName;
106 
107 		this.actionType = actionType;
108 
109 		tagParent = resourcesService.getTagLikeResourceParent(session, tagId);
110 		tagInstanceType = ConnectJcrUtils.get(tagParent, ResourcesNames.RESOURCES_TAG_INSTANCE_TYPE);
111 	}
112 
113 	@Override
114 	public void addPages() {
115 		try {
116 			// configure container
117 			String title = "Batch " + (actionType == TYPE_ADD ? "addition" : "remove");
118 			setWindowTitle(title);
119 			MainInfoPage inputPage = new MainInfoPage("Configure");
120 			addPage(inputPage);
121 			RecapPage recapPage = new RecapPage("Validate and launch");
122 			addPage(recapPage);
123 		} catch (Exception e) {
124 			throw new ConnectException("Cannot add page to wizard", e);
125 		}
126 	}
127 
128 	/**
129 	 * Called when the user click on 'Finish' in the wizard. The task is then
130 	 * created and the corresponding session saved.
131 	 */
132 	@Override
133 	public boolean performFinish() {
134 		// try {
135 		// Sanity checks
136 		String errMsg = null;
137 		if (tagInstance == null)
138 			errMsg = "Please choose the tag to use";
139 
140 		if (errMsg != null) {
141 			MessageDialog.openError(getShell(), "Unvalid information", errMsg);
142 			return false;
143 		}
144 		new UpdateTagAndInstancesJob(callingDisplay, actionType, tagInstance, elements, selectorName, tagPropName)
145 				.schedule();
146 		return true;
147 	}
148 
149 	@Override
150 	public boolean performCancel() {
151 		return true;
152 	}
153 
154 	@Override
155 	public boolean canFinish() {
156 		return tagInstance != null && getContainer().getCurrentPage().getNextPage() == null;
157 	}
158 
159 	protected class MainInfoPage extends WizardPage {
160 		private static final long serialVersionUID = 1L;
161 
162 		private SimpleJcrTableComposite tableCmp;
163 
164 		private List<JcrColumnDefinition> colDefs = new ArrayList<JcrColumnDefinition>();
165 		{ // By default, it displays only title
166 			colDefs.add(new JcrColumnDefinition(null, Property.JCR_TITLE, PropertyType.STRING, "Label", 420));
167 		};
168 
169 		public MainInfoPage(String pageName) {
170 			super(pageName);
171 			setTitle("Select a tag");
172 			setMessage("Choose the value that will be " + (actionType == TYPE_ADD ? "added to " : "removed from ")
173 					+ "the previously selected items.");
174 		}
175 
176 		public void createControl(Composite parent) {
177 			Composite body = new Composite(parent, SWT.NONE);
178 			body.setLayout(EclipseUiUtils.noSpaceGridLayout());
179 			Node tagParent = resourcesService.getTagLikeResourceParent(session, tagId);
180 			int style = SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL;
181 			tableCmp = new SimpleJcrTableComposite(body, style, session, ConnectJcrUtils.getPath(tagParent),
182 					tagInstanceType, colDefs, true, false);
183 			GridData gd = new GridData(SWT.FILL, SWT.FILL, true, false);
184 			gd.heightHint = 400;
185 			tableCmp.setLayoutData(gd);
186 
187 			// Add listeners
188 			tableCmp.getTableViewer().addDoubleClickListener(new MyDoubleClickListener());
189 			tableCmp.getTableViewer().addSelectionChangedListener(new MySelectionChangedListener());
190 
191 			setControl(body);
192 			tableCmp.setFocus();
193 		}
194 
195 		class MySelectionChangedListener implements ISelectionChangedListener {
196 			@Override
197 			public void selectionChanged(SelectionChangedEvent event) {
198 				if (event.getSelection().isEmpty())
199 					tagInstance = null;
200 				else {
201 					Object obj = ((IStructuredSelection) event.getSelection()).getFirstElement();
202 					if (obj instanceof Node) {
203 						tagInstance = (Node) obj;
204 					}
205 				}
206 			}
207 		}
208 
209 		class MyDoubleClickListener implements IDoubleClickListener {
210 			public void doubleClick(DoubleClickEvent evt) {
211 				if (evt.getSelection().isEmpty()) {
212 					tagInstance = null;
213 					return;
214 				} else {
215 					Object obj = ((IStructuredSelection) evt.getSelection()).getFirstElement();
216 					if (obj instanceof Node) {
217 						tagInstance = (Node) obj;
218 						getContainer().showPage(getNextPage());
219 					}
220 				}
221 			}
222 		}
223 	}
224 
225 	protected class RecapPage extends WizardPage {
226 		private static final long serialVersionUID = 1L;
227 
228 		public RecapPage(String pageName) {
229 			super(pageName);
230 			setTitle("Check and confirm");
231 		}
232 
233 		public void setVisible(boolean visible) {
234 			super.setVisible(visible);
235 
236 			if (visible == true) {
237 				if (tagInstance == null)
238 					setErrorMessage("Please choose a tag value to be used");
239 				else {
240 					setErrorMessage(null);
241 					String name = ConnectJcrUtils.get(tagInstance, Property.JCR_TITLE);
242 					if (actionType == TYPE_ADD)
243 						setMessage("Your are about to add [" + name + "] to the below listed " + elements.length
244 								+ " items. " + "Are you sure you want to proceed ?");
245 					else
246 						setMessage("Your are about to remove [" + name + "] from the below listed " + elements.length
247 								+ " items. " + "Are you sure you want to proceed ?");
248 				}
249 			}
250 		}
251 
252 		public void createControl(Composite parent) {
253 			Composite body = new Composite(parent, SWT.NONE);
254 			body.setLayout(EclipseUiUtils.noSpaceGridLayout());
255 			ArrayList<ConnectColumnDefinition> colDefs = new ArrayList<ConnectColumnDefinition>();
256 			colDefs.add(new ConnectColumnDefinition("Display Name",
257 					new TitleIconRowLP(systemWorkbenchService, selectorName, Property.JCR_TITLE), 300));
258 
259 			VirtualJcrTableViewer tableCmp = new VirtualJcrTableViewer(body, SWT.READ_ONLY, colDefs);
260 			TableViewer membersViewer = tableCmp.getTableViewer();
261 			membersViewer.setContentProvider(new MyLazyContentProvider(membersViewer));
262 			setViewerInput(membersViewer, elements);
263 			GridData gd = new GridData(SWT.FILL, SWT.FILL, true, false);
264 			gd.heightHint = 400;
265 			tableCmp.setLayoutData(gd);
266 			setControl(body);
267 		}
268 	}
269 
270 	/** Use this method to update the result table */
271 	protected void setViewerInput(TableViewer membersViewer, Object[] elements) {
272 		membersViewer.setInput(elements);
273 		// we must explicitly set the items count
274 		membersViewer.setItemCount(elements.length);
275 		membersViewer.refresh();
276 	}
277 
278 	private class MyLazyContentProvider implements ILazyContentProvider {
279 		private static final long serialVersionUID = 1L;
280 		private TableViewer viewer;
281 		private Object[] elements;
282 
283 		public MyLazyContentProvider(TableViewer viewer) {
284 			this.viewer = viewer;
285 		}
286 
287 		public void dispose() {
288 		}
289 
290 		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
291 			// IMPORTANT: don't forget this: an exception will be thrown if a
292 			// selected object is not part of the results anymore.
293 			viewer.setSelection(null);
294 			this.elements = (Object[]) newInput;
295 		}
296 
297 		public void updateElement(int index) {
298 			viewer.replace(elements[index], index);
299 		}
300 	}
301 
302 	/** Privileged job that performs the update asynchronously */
303 	private class UpdateTagAndInstancesJob extends PrivilegedJob {
304 
305 		private int actionType;
306 		private Repository repository;
307 		private String tagPath;
308 		private String tagPropName;
309 		private List<String> pathes = new ArrayList<String>();
310 		private Display display;
311 
312 		public UpdateTagAndInstancesJob(Display display, int actionType, Node taginstance, Object[] toUpdateElements,
313 				String selectorName, String tagPropName) {
314 			super("Updating");
315 
316 			this.display = display;
317 			this.actionType = actionType;
318 			this.tagPropName = tagPropName;
319 
320 			try {
321 				this.tagPath = tagInstance.getPath();
322 				repository = tagInstance.getSession().getRepository();
323 				for (Object element : toUpdateElements) {
324 					Node currNode = ConnectJcrUtils.getNodeFromElement(element, selectorName);
325 					pathes.add(currNode.getPath());
326 				}
327 			} catch (RepositoryException e) {
328 				throw new ConnectException("Unable to init " + "tag instance batch update for " + tagInstance, e);
329 			}
330 		}
331 
332 		protected IStatus doRun(IProgressMonitor progressMonitor) {
333 			Session session = null;
334 			try {
335 				JcrMonitor monitor = new EclipseJcrMonitor(progressMonitor);
336 				if (monitor != null && !monitor.isCanceled()) {
337 					monitor.beginTask("Updating objects", -1);
338 
339 					session = repository.login();
340 					Node targetTagInstance = session.getNode(tagPath);
341 
342 					// TODO use transaction
343 					// Legacy insure the node is checked out before update
344 					ConnectJcrUtils.checkCOStatusBeforeUpdate(tagInstance);
345 
346 					// TODO hardcoded prop name
347 					String value = targetTagInstance.getProperty(Property.JCR_TITLE).getString();
348 
349 					for (String currPath : pathes) {
350 						Node currNode = session.getNode(currPath);
351 						ConnectJcrUtils.checkCOStatusBeforeUpdate(currNode);
352 						if (actionType == TYPE_ADD) {
353 							// Duplication will return an error message that we
354 							// ignore
355 							ConnectJcrUtils.addStringToMultiValuedProp(currNode, tagPropName, value);
356 						} else if (actionType == TYPE_REMOVE) {
357 							// Duplication will return an error message that we
358 							// ignore
359 							ConnectJcrUtils.removeStringFromMultiValuedProp(currNode, tagPropName, value);
360 						}
361 						ConnectJcrUtils.saveAndPublish(currNode, true);
362 					}
363 					monitor.worked(1);
364 
365 					// FIXME asynchronous refresh does not yet work
366 					display.asyncExec(new Runnable() {
367 						@Override
368 						public void run() {
369 							// CommandUtils.callCommand(ForceRefresh.ID);
370 						}
371 					});
372 				}
373 			} catch (Exception e) {
374 				return new Status(IStatus.ERROR, "", "Unable to perform batch update on " + tagPath + " for "
375 						+ (selectorName == null ? "single node " : selectorName) + " row list with " + tagInstanceType,
376 						e);
377 			} finally {
378 				JcrUtils.logoutQuietly(session);
379 			}
380 			return Status.OK_STATUS;
381 		}
382 	}
383 }