View Javadoc
1   package org.argeo.connect.e4.resources.parts;
2   
3   import java.util.ArrayList;
4   
5   import javax.jcr.Node;
6   import javax.jcr.Property;
7   import javax.jcr.PropertyType;
8   import javax.jcr.Repository;
9   import javax.jcr.RepositoryException;
10  import javax.jcr.Session;
11  import javax.jcr.nodetype.NodeType;
12  import javax.jcr.query.QueryManager;
13  import javax.jcr.query.QueryResult;
14  import javax.jcr.query.Row;
15  import javax.jcr.query.qom.Constraint;
16  import javax.jcr.query.qom.DynamicOperand;
17  import javax.jcr.query.qom.Ordering;
18  import javax.jcr.query.qom.QueryObjectModel;
19  import javax.jcr.query.qom.QueryObjectModelConstants;
20  import javax.jcr.query.qom.QueryObjectModelFactory;
21  import javax.jcr.query.qom.Selector;
22  import javax.jcr.query.qom.StaticOperand;
23  
24  import org.apache.commons.logging.Log;
25  import org.apache.commons.logging.LogFactory;
26  import org.argeo.cms.e4.PrivilegedJob;
27  import org.argeo.connect.ConnectException;
28  import org.argeo.connect.resources.ResourcesNames;
29  import org.argeo.connect.resources.ResourcesService;
30  import org.argeo.connect.ui.ConnectColumnDefinition;
31  import org.argeo.connect.ui.ConnectUiUtils;
32  import org.argeo.connect.ui.SystemWorkbenchService;
33  import org.argeo.connect.ui.util.TitleIconRowLP;
34  import org.argeo.connect.ui.util.VirtualJcrTableViewer;
35  import org.argeo.connect.util.ConnectJcrUtils;
36  import org.argeo.eclipse.ui.EclipseJcrMonitor;
37  import org.argeo.eclipse.ui.EclipseUiUtils;
38  import org.argeo.jcr.JcrMonitor;
39  import org.argeo.jcr.JcrUtils;
40  import org.eclipse.core.runtime.IProgressMonitor;
41  import org.eclipse.core.runtime.IStatus;
42  import org.eclipse.core.runtime.Status;
43  import org.eclipse.jface.dialogs.MessageDialog;
44  import org.eclipse.jface.viewers.ILazyContentProvider;
45  import org.eclipse.jface.viewers.TableViewer;
46  import org.eclipse.jface.viewers.Viewer;
47  import org.eclipse.jface.wizard.Wizard;
48  import org.eclipse.jface.wizard.WizardPage;
49  import org.eclipse.swt.SWT;
50  import org.eclipse.swt.layout.GridData;
51  import org.eclipse.swt.layout.GridLayout;
52  import org.eclipse.swt.widgets.Composite;
53  import org.eclipse.swt.widgets.Text;
54  
55  /**
56   * Generic wizard to edit the value/title of a tag like property
57   * 
58   * This will return SWT.OK only if the value has been changed, in that case,
59   * underlying session is saved and the node checked in to ease life cycle
60   * management.
61   */
62  
63  public class EditTagWizard extends Wizard {
64  	private final static Log log = LogFactory.getLog(EditTagWizard.class);
65  
66  	// Context
67  	private ResourcesService resourcesService;
68  	private SystemWorkbenchService systemWorkbenchService;
69  
70  	private String tagId;
71  	private Node tagInstance;
72  	private String tagPropName;
73  
74  	// Cache to ease implementation
75  	private Session session;
76  	private Node tagParent;
77  	private String taggableNodeType;
78  	private String taggableParentPath;
79  
80  	// This part widgets
81  	private Text newTitleTxt;
82  	private Text newDescTxt;
83  
84  	/**
85  	 * 
86  	 * @param peopleService
87  	 * @param systemWorkbenchService
88  	 * @param tagInstanceNode
89  	 * @param tagId
90  	 * @param tagPropName
91  	 */
92  	public EditTagWizard(ResourcesService resourcesService, SystemWorkbenchService systemWorkbenchService,
93  			Node tagInstanceNode, String tagId, String tagPropName) {
94  		this.resourcesService = resourcesService;
95  		this.systemWorkbenchService = systemWorkbenchService;
96  		this.tagId = tagId;
97  		this.tagInstance = tagInstanceNode;
98  		this.tagPropName = tagPropName;
99  
100 		session = ConnectJcrUtils.getSession(tagInstance);
101 		tagParent = resourcesService.getTagLikeResourceParent(session, tagId);
102 		taggableNodeType = ConnectJcrUtils.get(tagParent, ResourcesNames.RESOURCES_TAGGABLE_NODE_TYPE);
103 		taggableParentPath = ConnectJcrUtils.get(tagParent, ResourcesNames.RESOURCES_TAGGABLE_PARENT_PATH);
104 	}
105 
106 	@Override
107 	public void addPages() {
108 		try {
109 			// configure container
110 			String title = "Rename [" + ConnectJcrUtils.get(tagInstance, Property.JCR_TITLE)
111 					+ "] and update existing related contacts";
112 			setWindowTitle(title);
113 			MainInfoPage inputPage = new MainInfoPage("Configure");
114 			addPage(inputPage);
115 			RecapPage recapPage = new RecapPage("Validate and launch");
116 			addPage(recapPage);
117 		} catch (Exception e) {
118 			throw new ConnectException("Cannot add page to wizard", e);
119 		}
120 	}
121 
122 	/**
123 	 * Called when the user click on 'Finish' in the wizard. The task is then
124 	 * created and the corresponding session saved.
125 	 */
126 	@Override
127 	public boolean performFinish() {
128 		try {
129 			String oldTitle = ConnectJcrUtils.get(tagInstance, Property.JCR_TITLE);
130 			String newTitle = newTitleTxt.getText();
131 			String newDesc = newDescTxt.getText();
132 
133 			// Sanity checks
134 			String errMsg = null;
135 			if (EclipseUiUtils.isEmpty(newTitle))
136 				errMsg = "New value cannot be blank or an empty string";
137 			else if (oldTitle.equals(newTitle))
138 				errMsg = "New value is the same as old one.\n" + "Either enter a new one or press cancel.";
139 			else if (resourcesService.getRegisteredTag(tagInstance.getSession(), tagId, newTitle) != null)
140 				errMsg = "The new chosen value is already used.\n" + "Either enter a new one or press cancel.";
141 
142 			if (errMsg != null) {
143 				MessageDialog.openError(getShell(), "Unvalid information", errMsg);
144 				return false;
145 			}
146 
147 			new UpdateTagAndInstancesJob(resourcesService, tagInstance, newTitle, newDesc).schedule();
148 			return true;
149 		} catch (RepositoryException re) {
150 			throw new ConnectException("unable to update title for tag like resource " + tagInstance, re);
151 		}
152 	}
153 
154 	@Override
155 	public boolean performCancel() {
156 		return true;
157 	}
158 
159 	@Override
160 	public boolean canFinish() {
161 		return getContainer().getCurrentPage().getNextPage() == null;
162 	}
163 
164 	protected class MainInfoPage extends WizardPage {
165 		private static final long serialVersionUID = 1L;
166 
167 		public MainInfoPage(String pageName) {
168 			super(pageName);
169 			setTitle("Enter a new title");
170 			setMessage("As reminder, former value was: " + ConnectJcrUtils.get(tagInstance, Property.JCR_TITLE));
171 		}
172 
173 		public void createControl(Composite parent) {
174 			Composite body = new Composite(parent, SWT.NONE);
175 			body.setLayout(new GridLayout(2, false));
176 
177 			// New Title Value
178 			ConnectUiUtils.createBoldLabel(body, "Title");
179 			newTitleTxt = new Text(body, SWT.BORDER);
180 			newTitleTxt.setMessage("was: " + ConnectJcrUtils.get(tagInstance, Property.JCR_TITLE));
181 			newTitleTxt.setText(ConnectJcrUtils.get(tagInstance, Property.JCR_TITLE));
182 			newTitleTxt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
183 
184 			// New Description Value
185 			ConnectUiUtils.createBoldLabel(body, "Description", SWT.TOP);
186 			newDescTxt = new Text(body, SWT.BORDER | SWT.MULTI | SWT.WRAP);
187 			newDescTxt.setMessage("was: " + ConnectJcrUtils.get(tagInstance, Property.JCR_DESCRIPTION));
188 			newDescTxt.setText(ConnectJcrUtils.get(tagInstance, Property.JCR_DESCRIPTION));
189 			newDescTxt.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
190 
191 			setControl(body);
192 			newTitleTxt.setFocus();
193 		}
194 	}
195 
196 	protected class RecapPage extends WizardPage {
197 		private static final long serialVersionUID = 1L;
198 
199 		public RecapPage(String pageName) {
200 			super(pageName);
201 			setTitle("Check and confirm");
202 			setMessage("The below listed items will be impacted.\nAre you sure you want to procede ?");
203 		}
204 
205 		public void createControl(Composite parent) {
206 			Composite body = new Composite(parent, SWT.NONE);
207 			body.setLayout(EclipseUiUtils.noSpaceGridLayout());
208 			ArrayList<ConnectColumnDefinition> colDefs = new ArrayList<ConnectColumnDefinition>();
209 			colDefs.add(new ConnectColumnDefinition(taggableNodeType, Property.JCR_TITLE, PropertyType.STRING,
210 					"Display Name", new TitleIconRowLP(systemWorkbenchService, taggableNodeType, Property.JCR_TITLE),
211 					300));
212 
213 			VirtualJcrTableViewer tableCmp = new VirtualJcrTableViewer(body, SWT.MULTI, colDefs);
214 			TableViewer membersViewer = tableCmp.getTableViewer();
215 			membersViewer.setContentProvider(new MyLazyContentProvider(membersViewer));
216 			refreshFilteredList(membersViewer);
217 			tableCmp.setLayoutData(EclipseUiUtils.fillAll());
218 			setControl(body);
219 		}
220 	}
221 
222 	/** Refresh the table viewer based on the free text search field */
223 	protected void refreshFilteredList(TableViewer membersViewer) {
224 		String currVal = ConnectJcrUtils.get(tagInstance, Property.JCR_TITLE);
225 		try {
226 			QueryManager queryManager = session.getWorkspace().getQueryManager();
227 			QueryObjectModelFactory factory = queryManager.getQOMFactory();
228 			Selector source = factory.selector(taggableNodeType, taggableNodeType);
229 
230 			StaticOperand so = factory.literal(session.getValueFactory().createValue(currVal));
231 			DynamicOperand dyo = factory.propertyValue(source.getSelectorName(), tagPropName);
232 			Constraint constraint = factory.comparison(dyo, QueryObjectModelConstants.JCR_OPERATOR_EQUAL_TO, so);
233 
234 			Constraint subTree = factory.descendantNode(source.getSelectorName(), taggableParentPath);
235 			constraint = ConnectJcrUtils.localAnd(factory, constraint, subTree);
236 
237 			Ordering order = factory.ascending(factory.propertyValue(source.getSelectorName(), Property.JCR_TITLE));
238 			Ordering[] orderings = { order };
239 			QueryObjectModel query = factory.createQuery(source, constraint, orderings, null);
240 			QueryResult result = query.execute();
241 			Row[] rows = ConnectJcrUtils.rowIteratorToArray(result.getRows());
242 			setViewerInput(membersViewer, rows);
243 
244 		} catch (RepositoryException e) {
245 			throw new ConnectException("Unable to list entities for tag like property instance " + currVal, e);
246 		}
247 	}
248 
249 	/** Use this method to update the result table */
250 	protected void setViewerInput(TableViewer membersViewer, Row[] rows) {
251 		membersViewer.setInput(rows);
252 		// we must explicitly set the items count
253 		membersViewer.setItemCount(rows.length);
254 		membersViewer.refresh();
255 	}
256 
257 	private class MyLazyContentProvider implements ILazyContentProvider {
258 		private static final long serialVersionUID = 1L;
259 		private TableViewer viewer;
260 		private Row[] elements;
261 
262 		public MyLazyContentProvider(TableViewer viewer) {
263 			this.viewer = viewer;
264 		}
265 
266 		public void dispose() {
267 		}
268 
269 		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
270 			// IMPORTANT: don't forget this: an exception will be thrown if a
271 			// selected object is not part of the results anymore.
272 			viewer.setSelection(null);
273 			this.elements = (Row[]) newInput;
274 		}
275 
276 		public void updateElement(int index) {
277 			viewer.replace(elements[index], index);
278 		}
279 	}
280 
281 	/** Privileged job that asynchronously performs the update */
282 	private class UpdateTagAndInstancesJob extends PrivilegedJob {
283 
284 		private Repository repository;
285 		private ResourcesService resourcesService;
286 		private String tagPath;
287 		private String newTitle, newDesc;
288 
289 		// To refresh the calling editor after refresh
290 		private String tagJcrId;
291 //		private IWorkbenchPage callingPage;
292 
293 		public UpdateTagAndInstancesJob(ResourcesService resourceService, Node tagInstance, String newTitle,
294 				String newDesc) {
295 			super("Updating");
296 			this.resourcesService = resourceService;
297 			this.newTitle = newTitle;
298 			this.newDesc = newDesc;
299 			try {
300 				repository = tagInstance.getSession().getRepository();
301 				tagPath = tagInstance.getPath();
302 			} catch (RepositoryException e) {
303 				throw new ConnectException("Unable to init " + "tag instance batch update for " + tagId, e);
304 			}
305 //			callingPage = ConnectUiPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getActivePage();
306 		}
307 
308 		protected IStatus doRun(IProgressMonitor progressMonitor) {
309 			Session session = null;
310 			try {
311 				JcrMonitor monitor = new EclipseJcrMonitor(progressMonitor);
312 				if (monitor != null && !monitor.isCanceled()) {
313 					monitor.beginTask("Updating objects", -1);
314 
315 					session = repository.login();
316 					Node tagInstance = session.getNode(tagPath);
317 
318 					// TODO use transaction
319 					// Legacy insure the node is checked out before update
320 					ConnectJcrUtils.checkCOStatusBeforeUpdate(tagInstance);
321 
322 					resourcesService.updateTag(tagInstance, newTitle);
323 
324 					if (EclipseUiUtils.notEmpty(newDesc))
325 						tagInstance.setProperty(Property.JCR_DESCRIPTION, newDesc);
326 					else if (tagInstance.hasProperty(Property.JCR_DESCRIPTION))
327 						tagInstance.getProperty(Property.JCR_DESCRIPTION).remove();
328 
329 					// Do we really want a new version at each and every time
330 					if (tagInstance.isNodeType(NodeType.MIX_VERSIONABLE))
331 						ConnectJcrUtils.saveAndPublish(tagInstance, true);
332 					else
333 						tagInstance.getSession().save();
334 					monitor.worked(1);
335 					tagJcrId = tagInstance.getIdentifier();
336 					doRefresh();
337 				}
338 			} catch (Exception e) {
339 				e.printStackTrace();
340 				return new Status(IStatus.ERROR, "",
341 						"Cannot edit tag and corresponding instances", e);
342 			} finally {
343 				JcrUtils.logoutQuietly(session);
344 			}
345 			return Status.OK_STATUS;
346 		}
347 
348 		private void doRefresh() {
349 			// Refresh asynchronously the corresponfding tag editor if it is
350 			// still opened
351 
352 //			Display currDisplay = callingPage.getWorkbenchWindow().getShell().getDisplay();
353 //			currDisplay.asyncExec(new Runnable() {
354 //				public void run() {
355 //					try {
356 //						EntityEditorInput eei = new EntityEditorInput(tagJcrId);
357 //						IEditorPart iep = callingPage.findEditor(eei);
358 //						if (iep != null && iep instanceof Refreshable)
359 //							((Refreshable) iep).forceRefresh(null);
360 //						CommandUtils.callCommand(ForceRefresh.ID);
361 //					} catch (Exception e) {
362 //						// Fail without notifying the user
363 //						log.error("Unable to refresh the workbench after merge");
364 //						e.printStackTrace();
365 //					}
366 //				}
367 //			});
368 		}
369 	}
370 }