View Javadoc
1   package org.argeo.connect.ui.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.RepositoryException;
9   import javax.jcr.Session;
10  import javax.jcr.Value;
11  import javax.jcr.nodetype.NodeType;
12  import javax.jcr.version.VersionManager;
13  
14  import org.argeo.cms.ui.dialogs.CmsMessageDialog;
15  import org.argeo.cms.ui.eclipse.forms.AbstractFormPart;
16  import org.argeo.cms.ui.eclipse.forms.FormToolkit;
17  import org.argeo.cms.util.CmsUtils;
18  import org.argeo.connect.ConnectException;
19  import org.argeo.connect.resources.ResourcesNames;
20  import org.argeo.connect.resources.ResourcesService;
21  import org.argeo.connect.ui.ConnectEditor;
22  import org.argeo.connect.ui.ConnectImages;
23  import org.argeo.connect.ui.ConnectUiStyles;
24  import org.argeo.connect.ui.ConnectUiUtils;
25  import org.argeo.connect.ui.SystemWorkbenchService;
26  import org.argeo.connect.ui.widgets.TagLikeDropDown;
27  import org.argeo.connect.util.ConnectJcrUtils;
28  import org.argeo.eclipse.ui.EclipseUiUtils;
29  import org.argeo.jcr.JcrUtils;
30  import org.argeo.people.ui.PeopleMsg;
31  import org.eclipse.jface.dialogs.MessageDialog;
32  import org.eclipse.swt.SWT;
33  import org.eclipse.swt.events.SelectionAdapter;
34  import org.eclipse.swt.events.SelectionEvent;
35  import org.eclipse.swt.events.TraverseEvent;
36  import org.eclipse.swt.events.TraverseListener;
37  import org.eclipse.swt.layout.GridData;
38  import org.eclipse.swt.layout.RowData;
39  import org.eclipse.swt.layout.RowLayout;
40  import org.eclipse.swt.widgets.Button;
41  import org.eclipse.swt.widgets.Composite;
42  import org.eclipse.swt.widgets.Label;
43  import org.eclipse.swt.widgets.Link;
44  import org.eclipse.swt.widgets.Shell;
45  import org.eclipse.swt.widgets.Text;
46  //import org.eclipse.ui.forms.AbstractFormPart;
47  //import org.eclipse.ui.forms.widgets.FormToolkit;
48  
49  /**
50   * Wraps an Abstract form part that enable management of a tag like list in a
51   * form editor.
52   */
53  public class TagLikeListPart extends Composite {
54  	private static final long serialVersionUID = -312141685147619814L;
55  	// private final static Log log = LogFactory.getLog(TagLikeListPart.class);
56  
57  	// UI Context
58  	private final ConnectEditor editor;
59  	private final FormToolkit toolkit;
60  	private final String newTagMsg;
61  
62  	// Context
63  	private final ResourcesService resourcesService;
64  	private final SystemWorkbenchService systemWorkbenchService;
65  	private final Node taggable;
66  	private final Node tagParent;
67  	private final String tagId;
68  	private final String taggablePropName;
69  
70  	// Deduced from the context, shortcut for this class
71  	private final Session session;
72  
73  	// Cache to trace newly created versionable tag like objects.
74  	// private final List<String> createdTagPath = new ArrayList<String>();
75  
76  	/**
77  	 * 
78  	 * @param parent
79  	 * @param style
80  	 * @param toolkit
81  	 * @param form
82  	 * @param resourcesService
83  	 * @param systemWorkbenchService
84  	 * @param taggable
85  	 * @param tagId
86  	 * @param newTagMsg
87  	 */
88  	public TagLikeListPart(ConnectEditor editor, Composite parent, int style, ResourcesService resourcesService,
89  			SystemWorkbenchService systemWorkbenchService, String tagId, Node taggable, String taggablePropName,
90  			String newTagMsg) {
91  		super(parent, style);
92  		this.editor = editor;
93  		this.toolkit = editor.getFormToolkit();
94  		this.resourcesService = resourcesService;
95  		this.systemWorkbenchService = systemWorkbenchService;
96  		this.tagId = tagId;
97  		this.taggable = taggable;
98  		this.taggablePropName = taggablePropName;
99  		this.newTagMsg = newTagMsg;
100 
101 		// Cache some context object to ease implementation
102 		session = ConnectJcrUtils.getSession(taggable);
103 		tagParent = resourcesService.getTagLikeResourceParent(session, tagId);
104 
105 		RowLayout rl = new RowLayout(SWT.HORIZONTAL);
106 		rl.wrap = true;
107 		rl.marginLeft = rl.marginTop = rl.marginBottom = 0;
108 		rl.marginRight = 8;
109 		this.setLayout(rl);
110 
111 		AbstractFormPart tagFormPart = new TagFormPart(this);
112 		tagFormPart.initialize(editor.getManagedForm());
113 		editor.getManagedForm().addPart(tagFormPart);
114 	}
115 
116 	private class TagFormPart extends AbstractFormPart {
117 		private Composite parentCmp;
118 
119 		public TagFormPart(Composite parent) {
120 			this.parentCmp = parent;
121 		}
122 
123 		@Override
124 		public void commit(boolean onSave) {
125 
126 			// boolean isEmpty = createdTagPath.isEmpty();
127 			// if (onSave && !isEmpty) {
128 			// try {
129 			// Session session = taggable.getSession();
130 			// if (session.hasPendingChanges()) {
131 			// log.warn("Session have been saved before commit of newly created
132 			// tags when saving node "
133 			// + taggable);
134 			// session.save();
135 			// }
136 			// VersionManager manager =
137 			// session.getWorkspace().getVersionManager();
138 			// for (String path : createdTagPath) {
139 			// Node newTag = session.getNode(path);
140 			// if (newTag.isCheckedOut()) {
141 			// manager.checkin(path);
142 			// }
143 			// }
144 			// createdTagPath.clear();
145 			// } catch (RepositoryException re) {
146 			// throw new ConnectException("Error while committing tagrefreshing
147 			// tag like list for " + taggable,
148 			// re);
149 			// }
150 			// }
151 			super.commit(onSave);
152 		}
153 
154 		public void refresh() {
155 			super.refresh();
156 			EclipseUiUtils.clear(parentCmp);
157 
158 			boolean isCO = editor.isEditing();
159 
160 			try {
161 				if (taggable.hasProperty(taggablePropName)) {
162 					Value[] values = taggable.getProperty(taggablePropName).getValues();
163 					for (final Value value : values) {
164 						final String tagValue = value.getString();
165 
166 						Composite tagCmp = toolkit.createComposite(parentCmp, SWT.NO_FOCUS);
167 						tagCmp.setLayout(ConnectUiUtils.noSpaceGridLayout(2));
168 						// Label hashTagLbl = new Label(tagCmp, SWT.NONE);
169 						// hashTagLbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
170 						// hashTagLbl.setImage(ConnectImages.TAG);
171 						Link link = new Link(tagCmp, SWT.NONE);
172 						// link.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
173 
174 						CmsUtils.markup(link);
175 						CmsUtils.style(link, ConnectUiStyles.ENTITY_HEADER);
176 						link.setText(getLinkText(taggablePropName, tagValue));
177 						link.addSelectionListener(new SelectionAdapter() {
178 							private static final long serialVersionUID = 1L;
179 
180 							@Override
181 							public void widgetSelected(final SelectionEvent event) {
182 								Node tag = resourcesService.getRegisteredTag(tagParent, tagValue);
183 								// try {
184 								// if (createdTagPath.contains(tag.getPath())) {
185 								// String msg = "This category is still in a
186 								// draft state.\n"
187 								// + "Please save first.";
188 								// MessageDialog.openInformation(parentCmp.getShell(),
189 								// "Forbidden action", msg);
190 								// } else
191 
192 								// CommandUtils.callCommand(systemWorkbenchService.getOpenEntityEditorCmdId(),
193 								// ConnectEditor.PARAM_JCR_ID, ConnectJcrUtils.getIdentifier(tag));
194 								systemWorkbenchService.openEntityEditor(tag);
195 
196 								// } catch (RepositoryException e) {
197 								// throw new ConnectException("unable to get
198 								// path for resource tag node " + tag
199 								// + " while editing " + taggable, e);
200 								// }
201 							}
202 						});
203 
204 						if (isCO) {
205 							addDeleteButton(TagFormPart.this, tagCmp, value);
206 						}
207 					}
208 				}
209 				if (isCO) {
210 					final Text tagTxt = toolkit.createText(parentCmp, "", SWT.BORDER);
211 					tagTxt.setMessage(newTagMsg);
212 					RowData rd = new RowData(120, SWT.DEFAULT);
213 					tagTxt.setLayoutData(rd);
214 
215 					final TagLikeDropDown tagDD = new TagLikeDropDown(taggable.getSession(), resourcesService, tagId,
216 							tagTxt);
217 
218 					tagTxt.addTraverseListener(new TraverseListener() {
219 						private static final long serialVersionUID = 1L;
220 
221 						public void keyTraversed(TraverseEvent e) {
222 							if (e.keyCode == SWT.CR) {
223 								String newTag = tagDD.getText();
224 								addTag(tagTxt.getShell(), TagFormPart.this, newTag);
225 								e.doit = false;
226 								// if (!tagTxt.isDisposed())
227 								// tagDD.reset("");
228 								// tagTxt.setText("");
229 							}
230 						}
231 					});
232 
233 					tagTxt.getParent().layout();
234 
235 					Button okBtn = toolkit.createButton(parentCmp, "OK", SWT.BORDER | SWT.PUSH | SWT.BOTTOM);
236 					rd = new RowData(SWT.DEFAULT, tagTxt.getSize().y - 2);
237 					okBtn.setLayoutData(rd);
238 
239 					okBtn.addSelectionListener(new SelectionAdapter() {
240 						private static final long serialVersionUID = 2780819012423622369L;
241 
242 						@Override
243 						public void widgetSelected(SelectionEvent e) {
244 							String newTag = tagDD.getText();
245 							if (EclipseUiUtils.isEmpty(newTag))
246 								return;
247 							else
248 								addTag(parentCmp.getShell(), TagFormPart.this, newTag);
249 						}
250 					});
251 
252 				}
253 				parentCmp.layout(false);
254 				parentCmp.getParent().getParent().layout();
255 
256 			} catch (RepositoryException re) {
257 				throw new ConnectException("Error while refreshing tag like list for " + taggable, re);
258 			}
259 		}
260 	}
261 
262 	protected String getLinkText(String taggablePropName, String value) {
263 		if (taggablePropName.equals(ResourcesNames.CONNECT_TAGS))
264 			return " <a>#" + value + "</a>";
265 		else
266 			return " <a>" + value + "</a>";
267 	}
268 
269 	private void addDeleteButton(final AbstractFormPart part, Composite parent, final Value value) {
270 		final Button deleteBtn = new Button(parent, SWT.FLAT);
271 		deleteBtn.setImage(ConnectImages.DELETE_SMALL);
272 		// deleteBtn.setText("-");
273 		// CmsUtils.style(deleteBtn, ConnectUiStyles.SMALL_DELETE_BTN);
274 		// deleteBtn.setLayoutData(new GridData(24, 24));
275 		deleteBtn.addSelectionListener(new SelectionAdapter() {
276 			private static final long serialVersionUID = 1L;
277 
278 			@Override
279 			public void widgetSelected(final SelectionEvent event) {
280 				try {
281 					String tagToRemove = value.getString();
282 					List<String> tags = new ArrayList<String>();
283 					Value[] values = taggable.getProperty(taggablePropName).getValues();
284 					for (int i = 0; i < values.length; i++) {
285 						String curr = values[i].getString();
286 						if (!tagToRemove.equals(curr))
287 							tags.add(curr);
288 					}
289 					taggable.setProperty(taggablePropName, tags.toArray(new String[tags.size()]));
290 					part.refresh();
291 					part.markDirty();
292 				} catch (RepositoryException e) {
293 					throw new ConnectException("unable to initialise deletion", e);
294 				}
295 			}
296 		});
297 	}
298 
299 	private Node registerNewTag(String newTag) {
300 		// We must create the tag in a distinct session, they are stored in a
301 		// distinct subtree of the workspace as the business object.
302 		// thus session.save will fail for user that does not have read
303 		// privileges on the root node of the workspace (~ all users except
304 		// sysadmin...)
305 		Session tagSession = null;
306 		try {
307 			Session session = taggable.getSession();
308 			tagSession = session.getRepository().login();
309 			Node registered = resourcesService.registerTag(tagSession, tagId, newTag);
310 			tagSession.save();
311 			if (registered.isNodeType(NodeType.MIX_VERSIONABLE)) {
312 				VersionManager manager = session.getWorkspace().getVersionManager();
313 				manager.checkpoint(registered.getPath());
314 			}
315 			session.refresh(true);
316 			Node newTagNode = session.getNode(registered.getPath());
317 			return newTagNode;
318 		} catch (RepositoryException re) {
319 			throw new ConnectException("Unable to set " + taggablePropName + " on " + taggable, re);
320 		} finally {
321 			JcrUtils.logoutQuietly(tagSession);
322 		}
323 	}
324 
325 	private void addTag(Shell shell, final AbstractFormPart part, String newTag) {
326 		String msg = null;
327 
328 		try {
329 			Session session = taggable.getSession();
330 			// Check if such a tag is already registered
331 			Node registered = resourcesService.getRegisteredTag(tagParent, newTag);
332 
333 			if (registered == null) {
334 				if (resourcesService.canCreateTag(session)) {
335 					// Ask end user if we create a new tag
336 					// msg = "\"" + newTag + "\" is not yet registered.\n Are you sure you want to
337 					// create it?";
338 					msg = PeopleMsg.confirmNewTag.format(new String[] { newTag });
339 					// if (MessageDialog.openConfirm(shell, "Confirm creation", msg)) {
340 					if (CmsMessageDialog.openConfirm(msg)) {
341 						registered = registerNewTag(newTag);
342 					} else
343 						return;
344 				} else {
345 					// msg = "\"" + newTag + "\" is not yet registered "
346 					// + "and you don't have sufficient rights to create it.\n"
347 					// + "Please contact a Business Admin and ask him " + "to register it for you if
348 					// it is valid.";
349 					msg = PeopleMsg.cannotCreateTag.format(new String[] { newTag });
350 					CmsMessageDialog.openWarning(msg);
351 					// MessageDialog.openError(shell, "Unvalid choice", msg);
352 					return;
353 				}
354 			}
355 
356 			Value[] values;
357 			String[] valuesStr;
358 			if (taggable.hasProperty(taggablePropName)) {
359 				values = taggable.getProperty(taggablePropName).getValues();
360 
361 				// Check duplicates
362 				for (Value tag : values) {
363 					String curTagUpperCase = tag.getString().toUpperCase().trim();
364 					if (newTag.toUpperCase().trim().equals(curTagUpperCase)) {
365 						msg = "\"" + ConnectJcrUtils.get(taggable, Property.JCR_TITLE) + "\" is already linked with \""
366 								+ tag.getString() + "\". Nothing has been done.";
367 						MessageDialog.openError(shell, "Duplicate link", msg);
368 						return;
369 					}
370 				}
371 
372 				valuesStr = new String[values.length + 1];
373 				int i;
374 				for (i = 0; i < values.length; i++) {
375 					valuesStr[i] = values[i].getString();
376 				}
377 				valuesStr[i] = newTag;
378 			} else {
379 				valuesStr = new String[1];
380 				valuesStr[0] = newTag;
381 			}
382 			taggable.setProperty(taggablePropName, valuesStr);
383 			part.refresh();
384 			part.markDirty();
385 		} catch (RepositoryException re) {
386 			throw new ConnectException("Unable to set " + taggablePropName + " on " + taggable, re);
387 		}
388 	}
389 }