View Javadoc
1   package org.argeo.connect.ui;
2   
3   import java.util.Calendar;
4   import java.util.GregorianCalendar;
5   
6   import javax.jcr.Node;
7   import javax.jcr.PropertyType;
8   import javax.jcr.RepositoryException;
9   import javax.jcr.Session;
10  import javax.jcr.Value;
11  
12  import org.argeo.cms.ui.CmsEditable;
13  import org.argeo.cms.ui.dialogs.CmsWizardDialog;
14  import org.argeo.cms.ui.eclipse.forms.AbstractFormPart;
15  import org.argeo.connect.AppService;
16  import org.argeo.connect.ConnectConstants;
17  import org.argeo.connect.ConnectException;
18  import org.argeo.connect.resources.ResourcesNames;
19  import org.argeo.connect.resources.ResourcesService;
20  import org.argeo.connect.ui.widgets.ConnectAbstractDropDown;
21  import org.argeo.connect.util.ConnectJcrUtils;
22  import org.argeo.connect.util.ConnectUtils;
23  import org.argeo.eclipse.ui.EclipseUiUtils;
24  import org.argeo.eclipse.ui.jcr.lists.NodeViewerComparator;
25  import org.argeo.eclipse.ui.jcr.lists.RowViewerComparator;
26  import org.argeo.jcr.JcrUtils;
27  import org.eclipse.jface.viewers.TableViewer;
28  import org.eclipse.jface.window.Window;
29  import org.eclipse.jface.wizard.Wizard;
30  import org.eclipse.swt.SWT;
31  import org.eclipse.swt.events.ModifyEvent;
32  import org.eclipse.swt.events.ModifyListener;
33  import org.eclipse.swt.events.SelectionAdapter;
34  import org.eclipse.swt.events.SelectionEvent;
35  import org.eclipse.swt.widgets.Button;
36  import org.eclipse.swt.widgets.Combo;
37  import org.eclipse.swt.widgets.Composite;
38  import org.eclipse.swt.widgets.DateTime;
39  import org.eclipse.swt.widgets.Link;
40  import org.eclipse.swt.widgets.Shell;
41  import org.eclipse.swt.widgets.Table;
42  import org.eclipse.swt.widgets.Text;
43  
44  /** Helper methods to ease UI implementation in a Connect Workbench */
45  public class ConnectWorkbenchUtils {
46  	/**
47  	 * Helper to call a command with a few parameter easily. The additional
48  	 * parameters must be Pairs with each time a parameterID and a parameterValue
49  	 * couple in this order
50  	 */
51  	// public static void callCommand(String commandID, String... parameters) {
52  	// Map<String, String> params = new HashMap<String, String>();
53  	// for (int i = 0; i < (parameters.length / 2); i++)
54  	// params.put(parameters[i * 2], parameters[i * 2 + 1]);
55  	// CommandUtils.callCommand(commandID, params);
56  	// }
57  
58  	/**
59  	 * Shortcut to refresh a <code>DateTime</code> widget given a Node in a form and
60  	 * a property Name. Also manages its enable state. Note that, by default, we
61  	 * force setting of the time to noon. Might be later enhanced.
62  	 * 
63  	 * If the property does not yet exits, it is not created and the
64  	 */
65  	public static void refreshFormDateTimeWidget(CmsEditable editable, DateTime dateTime, Node node, String propName) {
66  		try {
67  			Calendar dateToDisplay = null;
68  			if (node.hasProperty(propName))
69  				dateToDisplay = node.getProperty(propName).getDate();
70  			else
71  				dateToDisplay = GregorianCalendar.getInstance();
72  
73  			dateTime.setDate(dateToDisplay.get(Calendar.YEAR), dateToDisplay.get(Calendar.MONTH),
74  					dateToDisplay.get(Calendar.DAY_OF_MONTH));
75  			dateTime.setTime(12, 0, 0);
76  			dateTime.setEnabled(editable.isEditing());
77  		} catch (RepositoryException re) {
78  			throw new ConnectException(
79  					"unable to refresh DateTime widget for node " + node + " and property " + propName, re);
80  		}
81  	}
82  
83  	/**
84  	 * Shortcut to refresh a <code>Text</code> widget given a Node in a form and a
85  	 * property Name. Also manages its enable state
86  	 */
87  	public static String refreshFormTextWidget(CmsEditable editable, Text text, Node node, String propName) {
88  		String newStr = ConnectJcrUtils.get(node, propName);
89  		String oldStr = text.getText();
90  		if (!newStr.equals(oldStr))
91  			text.setText(newStr);
92  		text.setEnabled(editable.isEditing());
93  		return newStr;
94  	}
95  
96  	/**
97  	 * Shortcut to refresh a <code>Text</code> widget given a Node in a form and a
98  	 * property Name. Also manages its enable state and set a default message if
99  	 * corresponding Text value is empty
100 	 */
101 	public static String refreshFormText(CmsEditable editable, Text text, Node entity, String propName,
102 			String defaultMsg) {
103 		String tmpStr = refreshFormTextWidget(editable, text, entity, propName);
104 		if (EclipseUiUtils.isEmpty(tmpStr) && EclipseUiUtils.notEmpty(defaultMsg))
105 			text.setMessage(defaultMsg);
106 		return tmpStr;
107 	}
108 
109 	/**
110 	 * Shortcut to select an item of a <code>Combo</code> widget given a Node in a
111 	 * form, a property Name. Also manages its enable state.
112 	 */
113 	public static void refreshFormCombo(CmsEditable editable, Combo combo, Node node, String propName) {
114 		String currValue = ConnectJcrUtils.get(node, propName);
115 		if (EclipseUiUtils.notEmpty(currValue))
116 			combo.select(combo.indexOf(currValue));
117 		combo.setEnabled(editable.isEditing());
118 	}
119 
120 	/**
121 	 * Shortcut to refresh a Check box <code>Button</code> with an encoded boolean
122 	 * flag widget given a node in a form and a property name.
123 	 */
124 	public static boolean refreshFlagFormCheckBox(CmsEditable editable, Button button, Node entity, String propName,
125 			int cache) {
126 		long val = 0;
127 		Boolean tmp = null;
128 		try {
129 			if (entity.hasProperty(propName)) {
130 				val = entity.getProperty(propName).getLong();
131 				tmp = (val & cache) != 0;
132 				button.setSelection(tmp);
133 			} else
134 				tmp = false;
135 			button.setEnabled(editable.isEditing());
136 		} catch (RepositoryException re) {
137 			throw new ConnectException("unable get boolean value for property " + propName);
138 		}
139 		return tmp;
140 	}
141 
142 	/**
143 	 * Shortcut to refresh a Check box <code>Button</code> widget given a Node in a
144 	 * form and a property Name.
145 	 */
146 	public static boolean refreshFormCheckBox(CmsEditable editable, Button button, Node entity, String propName) {
147 		Boolean tmp = null;
148 		try {
149 			if (entity.hasProperty(propName)) {
150 				tmp = entity.getProperty(propName).getBoolean();
151 				button.setSelection(tmp);
152 			} else
153 				tmp = false;
154 			button.setEnabled(editable.isEditing());
155 		} catch (RepositoryException re) {
156 			throw new ConnectException("unable get boolean value for property " + propName);
157 		}
158 		return tmp;
159 	}
160 
161 	/**
162 	 * Shortcut to refresh the text underlying a DropDown widget given a Node and a
163 	 * property Name.
164 	 */
165 	public static String refreshDropDown(ConnectAbstractDropDown dropDown, Node entity, String propName) {
166 		String tmp = null;
167 		try {
168 			if (entity.hasProperty(propName)) {
169 				tmp = entity.getProperty(propName).getString();
170 				dropDown.reset(ConnectJcrUtils.get(entity, propName));
171 			} else
172 				dropDown.reset(null);
173 		} catch (RepositoryException re) {
174 			throw new ConnectException("unable get boolean value for property " + propName);
175 		}
176 		return tmp;
177 	}
178 
179 	/**
180 	 * Shortcut to refresh a radio <code>Button</code> widget given a Node in a form
181 	 * and a property Name. Also manage its enabled state
182 	 */
183 	public static void refreshFormRadio(CmsEditable editor, Button button, Node entity, String propName) {
184 		Boolean tmp = null;
185 		try {
186 			if (entity.hasProperty(propName)) {
187 				tmp = entity.getProperty(propName).getString().equals(button.getText());
188 			} else
189 				tmp = false;
190 			button.setSelection(tmp);
191 			button.setEnabled(editor.isEditing());
192 		} catch (RepositoryException re) {
193 			throw new ConnectException("Unable to get boolean value for property " + propName + " on " + entity, re);
194 		}
195 	}
196 
197 	/**
198 	 * Creates a new selection adapter in order to provide sort abilities to a table
199 	 * that displays JCR Rows
200 	 * 
201 	 * @param index
202 	 * @param propertyType
203 	 * @param selectorName
204 	 * @param propertyName
205 	 * @param comparator
206 	 * @param viewer
207 	 * @return
208 	 */
209 	public static SelectionAdapter getSelectionAdapter(final int index, final int propertyType,
210 			final String selectorName, final String propertyName, final RowViewerComparator comparator,
211 			final TableViewer viewer) {
212 		SelectionAdapter selectionAdapter = new SelectionAdapter() {
213 			private static final long serialVersionUID = -3452356616673385039L;
214 
215 			@Override
216 			public void widgetSelected(SelectionEvent e) {
217 				Table table = viewer.getTable();
218 				comparator.setColumn(propertyType, selectorName, propertyName);
219 				int dir = table.getSortDirection();
220 				if (table.getSortColumn() == table.getColumn(index)) {
221 					dir = dir == SWT.UP ? SWT.DOWN : SWT.UP;
222 				} else {
223 					dir = SWT.DOWN;
224 				}
225 				table.setSortDirection(dir);
226 				table.setSortColumn(table.getColumn(index));
227 				viewer.refresh();
228 			}
229 		};
230 		return selectionAdapter;
231 	}
232 
233 	/**
234 	 * Creates a new selection adapter in order to provide sort abilities to a table
235 	 * that displays JCR nodes
236 	 * 
237 	 * @param index
238 	 * @param propertyType
239 	 * @param selectorName
240 	 * @param propertyName
241 	 * @param comparator
242 	 * @param viewer
243 	 * @return
244 	 */
245 	public static SelectionAdapter getSelectionAdapter(final int index, final int propertyType,
246 			final String propertyName, final NodeViewerComparator comparator, final TableViewer viewer) {
247 		SelectionAdapter selectionAdapter = new SelectionAdapter() {
248 			private static final long serialVersionUID = -3452356616673385039L;
249 
250 			@Override
251 			public void widgetSelected(SelectionEvent e) {
252 				Table table = viewer.getTable();
253 				comparator.setColumn(propertyType, propertyName);
254 				int dir = table.getSortDirection();
255 				if (table.getSortColumn() == table.getColumn(index)) {
256 					dir = dir == SWT.UP ? SWT.DOWN : SWT.UP;
257 				} else {
258 					dir = SWT.DOWN;
259 				}
260 				table.setSortDirection(dir);
261 				table.setSortColumn(table.getColumn(index));
262 				viewer.refresh();
263 			}
264 		};
265 		return selectionAdapter;
266 	}
267 
268 	/**
269 	 * Shortcut to add a default modify listeners to a <code>DateTime</code> widget
270 	 * that is bound a JCR String Property. Any change in the text is immediately
271 	 * stored in the active session, but no save is done.
272 	 */
273 	public static void addSelectionListener(final DateTime dateTime, final Node node, final String propName,
274 			final AbstractFormPart part) {
275 		dateTime.addSelectionListener(new SelectionAdapter() {
276 			private static final long serialVersionUID = 1L;
277 
278 			@Override
279 			public void widgetSelected(SelectionEvent e) {
280 				Calendar value = GregorianCalendar.getInstance();
281 				value.set(dateTime.getYear(), dateTime.getMonth(), dateTime.getDay(), dateTime.getHours(),
282 						dateTime.getMinutes());
283 				if (ConnectJcrUtils.setJcrProperty(node, propName, PropertyType.DATE, value))
284 					part.markDirty();
285 			}
286 		});
287 	}
288 
289 	/**
290 	 * Shortcut to add a default selection listener to a Check Box
291 	 * <code>Button</code> widget that is bound a JCR boolean property. Any change
292 	 * in the selection is immediately stored in the active session, but not saved
293 	 */
294 	public static void addCheckBoxListener(final Button button, final Node node, final String propName,
295 			final AbstractFormPart part) {
296 		button.addSelectionListener(new SelectionAdapter() {
297 			private static final long serialVersionUID = 1L;
298 
299 			@Override
300 			public void widgetSelected(SelectionEvent e) {
301 				boolean value = button.getSelection();
302 				if (ConnectJcrUtils.setJcrProperty(node, propName, PropertyType.BOOLEAN, value))
303 					part.markDirty();
304 			}
305 		});
306 	}
307 
308 	/**
309 	 * Shortcut to add a default selection listener to a Check Box
310 	 * <code>Button</code> widget that is bound a JCR boolean property. Any change
311 	 * in the selection is immediately stored in the active session, but not saved
312 	 */
313 	public static void addFlagCheckBoxListener(final Button button, final Node node, final String propName,
314 			final int cache, final AbstractFormPart part) {
315 		button.addSelectionListener(new SelectionAdapter() {
316 			private static final long serialVersionUID = 1L;
317 
318 			@Override
319 			public void widgetSelected(SelectionEvent e) {
320 				boolean value = button.getSelection();
321 
322 				Long oldValue = ConnectJcrUtils.getLongValue(node, propName);
323 				if (oldValue == null)
324 					oldValue = 0L;
325 				boolean oldFlag = (oldValue & cache) != 0;
326 				if (value != oldFlag) {
327 					long newValue = oldValue ^ cache;
328 					ConnectJcrUtils.setJcrProperty(node, propName, PropertyType.LONG, newValue);
329 					part.markDirty();
330 				}
331 			}
332 		});
333 	}
334 
335 	/**
336 	 * Shortcut to add a default modify listeners to a <code>Text</code> widget that
337 	 * is bound a JCR String Property. Any change in the text is immediately stored
338 	 * in the active session, but no save is done.
339 	 */
340 	public static void addModifyListener(final Text text, final Node node, final String propName,
341 			final AbstractFormPart part) {
342 		text.addModifyListener(new ModifyListener() {
343 			private static final long serialVersionUID = 1L;
344 
345 			@Override
346 			public void modifyText(ModifyEvent event) {
347 				if (ConnectJcrUtils.setJcrProperty(node, propName, PropertyType.STRING, text.getText()))
348 					part.markDirty();
349 			}
350 		});
351 	}
352 
353 	/**
354 	 * Shortcut to add a {@code ModifyListener} on a {@code Text} that updates a
355 	 * property on a Node
356 	 */
357 	public static void addTxtModifyListener(final AbstractFormPart part, final Text text, final Node entity,
358 			final String propName, final int propType) {
359 		text.addModifyListener(new ModifyListener() {
360 			private static final long serialVersionUID = 1549789407363632491L;
361 
362 			@Override
363 			public void modifyText(ModifyEvent event) {
364 				if (ConnectJcrUtils.setJcrProperty(entity, propName, propType, text.getText()))
365 					part.markDirty();
366 			}
367 		});
368 	}
369 
370 	/**
371 	 * Shortcut to add a {@code ModifyListener} on a {@code Combo} that updates a
372 	 * property on a Node
373 	 */
374 	public static void addTxtModifyListener(final AbstractFormPart part, final Combo combo, final Node entity,
375 			final String propName, final int propType) {
376 		combo.addModifyListener(new ModifyListener() {
377 			private static final long serialVersionUID = 1549789407363632491L;
378 
379 			@Override
380 			public void modifyText(ModifyEvent event) {
381 				if (ConnectJcrUtils.setJcrProperty(entity, propName, propType, combo.getText()))
382 					part.markDirty();
383 			}
384 		});
385 	}
386 
387 	/**
388 	 * Shortcut to add a SelectionListener on a combo that updates a property on a
389 	 * Node
390 	 */
391 	public static void addComboSelectionListener(final AbstractFormPart part, final Combo combo, final Node entity,
392 			final String propName, final int propType) {
393 		combo.addSelectionListener(new SelectionAdapter() {
394 			private static final long serialVersionUID = 1L;
395 
396 			@Override
397 			public void widgetSelected(SelectionEvent e) {
398 				int index = combo.getSelectionIndex();
399 				if (index != -1) {
400 					String selectedCategory = combo.getItem(index);
401 					if (ConnectJcrUtils.setJcrProperty(entity, propName, propType, selectedCategory))
402 						part.markDirty();
403 				}
404 
405 			}
406 		});
407 	}
408 
409 	/**
410 	 * Shortcut to add a Text Modifylistener that updates a LONG property on a Node.
411 	 * Checks the input validity while the user is typing
412 	 */
413 	// public static void addNbOnlyTxtModifyListener(IWorkbench workbench, final
414 	// AbstractFormPart part, final Text text,
415 	// final Node entity, final String propName, final int propType) {
416 	// final ControlDecoration decoration = new ControlDecoration(text, SWT.TOP |
417 	// SWT.LEFT);
418 	// decoration.setImage(workbench.getSharedImages().getImage(ISharedImages.IMG_DEC_FIELD_ERROR));
419 	// decoration.hide();
420 	//
421 	// text.addModifyListener(new ModifyListener() {
422 	// private static final long serialVersionUID = 1L;
423 	//
424 	// public void modifyText(ModifyEvent event) {
425 	// String lengthStr = text.getText();
426 	// if (!ConnectUiUtils.isNumbers(lengthStr)) {
427 	// text.setBackground(new Color(text.getDisplay(), 250, 200, 150));
428 	// decoration.show();
429 	// decoration.setDescriptionText("Length can only be a number: " + lengthStr);
430 	// } else {
431 	// text.setBackground(null);
432 	// decoration.hide();
433 	// Long length = null;
434 	// if (EclipseUiUtils.notEmpty(lengthStr))
435 	// length = new Long(lengthStr);
436 	// if (ConnectJcrUtils.setJcrProperty(entity, propName, propType, length))
437 	// part.markDirty();
438 	// }
439 	// }
440 	// });
441 	// }
442 
443 	/**
444 	 * Simply create a link to open a search editor with the given parameters
445 	 * 
446 	 * @param appWorkbenchService
447 	 * @param parent
448 	 * @param label
449 	 * @param nodeType
450 	 * @return
451 	 */
452 	public static Link createOpenSearchEditorLink(final AppWorkbenchService appWorkbenchService, Composite parent,
453 			final String label, final String nodeType) {
454 		Link link = new Link(parent, SWT.NONE);
455 		link.setText("<a>" + label + "</a>");
456 		link.setLayoutData(EclipseUiUtils.fillWidth());
457 		link.addSelectionListener(new SelectionAdapter() {
458 			private static final long serialVersionUID = 1L;
459 
460 			@Override
461 			public void widgetSelected(final SelectionEvent event) {
462 				appWorkbenchService.openSearchEntityView(nodeType, label);
463 			}
464 		});
465 		return link;
466 	}
467 
468 	/**
469 	 * Simply create a link to open whatever editor
470 	 * 
471 	 * @param iwPage
472 	 * @param parent
473 	 * @param editorInput
474 	 * @param editorId
475 	 * @param label
476 	 * @return
477 	 */
478 	// public static Link createOpenEditorLink(final IWorkbenchPage iwPage,
479 	// Composite parent,
480 	// final IEditorInput editorInput, final String editorId, final String label) {
481 	// Link link = new Link(parent, SWT.NONE);
482 	// link.setText("<a>" + label + "</a>");
483 	// link.setLayoutData(EclipseUiUtils.fillWidth());
484 	// link.addSelectionListener(new SelectionAdapter() {
485 	// private static final long serialVersionUID = 1L;
486 	//
487 	// @Override
488 	// public void widgetSelected(final SelectionEvent event) {
489 	// try {
490 	// IEditorPart iep = iwPage.findEditor(editorInput);
491 	// if (iep == null) {
492 	// iwPage.openEditor(editorInput, editorId);
493 	// } else
494 	// iwPage.activate(iep);
495 	//
496 	// } catch (PartInitException e) {
497 	// throw new ConnectException("Unable to open editor with ID " + editorId, e);
498 	// }
499 	// }
500 	// });
501 	// return link;
502 	// }
503 
504 	/**
505 	 * Simply create a link to open an entity editor for the given entity node
506 	 * 
507 	 * @param parent
508 	 * @param label
509 	 * @param entity
510 	 * @return
511 	 */
512 	public static Link createOpenEntityEditorLink(final AppWorkbenchService appWorkbenchService, Composite parent,
513 			final String label, final Node entity) {
514 		Link link = new Link(parent, SWT.NONE);
515 		link.setText("<a>" + label + "</a>");
516 		link.setLayoutData(EclipseUiUtils.fillWidth());
517 		link.addSelectionListener(new SelectionAdapter() {
518 			private static final long serialVersionUID = 1L;
519 
520 			@Override
521 			public void widgetSelected(final SelectionEvent event) {
522 				// Map<String, String> params = new HashMap<String, String>();
523 				// params.put(ConnectEditor.PARAM_JCR_ID,
524 				// ConnectJcrUtils.getIdentifier(entity));
525 				// CommandUtils.callCommand(appWorkbenchService.getOpenEntityEditorCmdId(),
526 				// params);
527 				appWorkbenchService.openEntityEditor(entity);
528 			}
529 		});
530 		return link;
531 	}
532 
533 	// Layouts and LayoutData
534 
535 	/**
536 	 * Create the text value of a link that enable calling the
537 	 * <code>OpenEditor</code> command from a cell of a HTML list
538 	 */
539 	public static String getOpenEditorSnippet(String commandId, Node relevantNode, String value) {
540 		String toEditJcrId = ConnectJcrUtils.getIdentifier(relevantNode);
541 		String href = commandId + "/" + ConnectEditor.PARAM_JCR_ID + "=" + toEditJcrId;
542 		return ConnectUiSnippets.getRWTLink(href, value);
543 	}
544 
545 	/** display clickable tags that are linked to the current entity */
546 	public static String getTags(ResourcesService resourceService, AppWorkbenchService appWorkbenchService,
547 			Node entity) {
548 		try {
549 			StringBuilder tags = new StringBuilder();
550 			if (entity.hasProperty(ResourcesNames.CONNECT_TAGS)) {
551 				for (Value value : entity.getProperty((ResourcesNames.CONNECT_TAGS)).getValues())
552 					tags.append("#").append(ConnectWorkbenchUtils.getTagLink(ConnectJcrUtils.getSession(entity),
553 							resourceService, appWorkbenchService, ConnectConstants.RESOURCE_TAG, value.getString()))
554 							.append("  ");
555 			}
556 			return ConnectUtils.replaceAmpersand(tags.toString());
557 		} catch (RepositoryException e) {
558 			throw new ConnectException("Error while getting tags for entity", e);
559 		}
560 	}
561 
562 	/**
563 	 * Generate a href link that will call the openEntityEditor Command for this tag
564 	 * if it is already registered. The corresponding Label / List must have a
565 	 * HtmlRWTAdapter to catch when the user click on the link
566 	 */
567 	public static String getTagLink(Session session, ResourcesService resourceService,
568 			AppWorkbenchService appWorkbenchService, String tagId, String value) {
569 		String commandId = appWorkbenchService.getOpenEntityEditorCmdId();
570 		Node tag = resourceService.getRegisteredTag(session, tagId, value);
571 		if (tag == null)
572 			return value;
573 		String tagJcrId = ConnectJcrUtils.getIdentifier(tag);
574 		String href = commandId + ConnectUiConstants.HREF_SEPARATOR;
575 		href += ConnectEditor.PARAM_JCR_ID + "=" + tagJcrId;
576 		return ConnectUiSnippets.getRWTLink(href, value);
577 	}
578 
579 	public static String createAndConfigureEntity(Shell shell, Session referenceSession, AppService appService,
580 			AppWorkbenchService appWorkbenchService, String mainMixin, String... additionnalProps) {
581 
582 		Session tmpSession = null;
583 		Session mainSession = null;
584 		try {
585 			tmpSession = referenceSession.getRepository().login();
586 			Node draftNode = appService.createDraftEntity(tmpSession, mainMixin);
587 			for (int i = 0; i < additionnalProps.length - 1; i += 2) {
588 				draftNode.setProperty(additionnalProps[i], additionnalProps[i + 1]);
589 			}
590 			Wizard wizard = appWorkbenchService.getCreationWizard(draftNode);
591 			CmsWizardDialog dialog = new CmsWizardDialog(shell, wizard);
592 			// WizardDialog dialog = new WizardDialog(shell, wizard);
593 			if (dialog.open() == Window.OK) {
594 				String parentPath = "/" + appService.getBaseRelPath(mainMixin);
595 				mainSession = referenceSession.getRepository().login();
596 				Node parent = mainSession.getNode(parentPath);
597 				Node task = appService.publishEntity(parent, mainMixin, draftNode);
598 				task = appService.saveEntity(task, false);
599 				referenceSession.refresh(true);
600 				return task.getPath();
601 			}
602 			return null;
603 		} catch (RepositoryException e1) {
604 			throw new ConnectException(
605 					"Unable to create " + mainMixin + " entity with session " + referenceSession.toString(), e1);
606 		} finally {
607 			JcrUtils.logoutQuietly(tmpSession);
608 			JcrUtils.logoutQuietly(mainSession);
609 		}
610 	}
611 
612 }