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.RepositoryException;
10 import javax.jcr.Session;
11 import javax.jcr.query.QueryManager;
12 import javax.jcr.query.QueryResult;
13 import javax.jcr.query.Row;
14 import javax.jcr.query.RowIterator;
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.argeo.cms.ui.eclipse.forms.AbstractFormPart;
25 import org.argeo.connect.ConnectException;
26 import org.argeo.connect.e4.parts.AbstractConnectEditor;
27 import org.argeo.connect.resources.ResourcesService;
28 import org.argeo.connect.ui.ConnectColumnDefinition;
29 import org.argeo.connect.ui.ConnectUiConstants;
30 import org.argeo.connect.ui.ConnectUiSnippets;
31 import org.argeo.connect.ui.ConnectUiUtils;
32 import org.argeo.connect.ui.SystemWorkbenchService;
33 import org.argeo.connect.ui.parts.AbstractPanelFormPart;
34 import org.argeo.connect.ui.util.LazyCTabControl;
35 import org.argeo.connect.ui.util.TitleIconRowLP;
36 import org.argeo.connect.ui.util.VirtualJcrTableViewer;
37 import org.argeo.connect.util.ConnectJcrUtils;
38 import org.argeo.connect.util.ConnectUtils;
39 import org.argeo.eclipse.ui.EclipseUiUtils;
40 import org.argeo.eclipse.ui.dialogs.SingleValue;
41 import org.argeo.eclipse.ui.utils.ViewerUtils;
42 import org.eclipse.jface.dialogs.MessageDialog;
43 import org.eclipse.jface.viewers.ColumnLabelProvider;
44 import org.eclipse.jface.viewers.DoubleClickEvent;
45 import org.eclipse.jface.viewers.IDoubleClickListener;
46 import org.eclipse.jface.viewers.ILazyContentProvider;
47 import org.eclipse.jface.viewers.ISelectionChangedListener;
48 import org.eclipse.jface.viewers.IStructuredContentProvider;
49 import org.eclipse.jface.viewers.IStructuredSelection;
50 import org.eclipse.jface.viewers.SelectionChangedEvent;
51 import org.eclipse.jface.viewers.TableViewer;
52 import org.eclipse.jface.viewers.TableViewerColumn;
53 import org.eclipse.jface.viewers.Viewer;
54 import org.eclipse.jface.window.Window;
55 import org.eclipse.jface.wizard.Wizard;
56 import org.eclipse.jface.wizard.WizardDialog;
57 import org.eclipse.jface.wizard.WizardPage;
58 import org.eclipse.rap.rwt.RWT;
59 import org.eclipse.swt.SWT;
60 import org.eclipse.swt.events.SelectionAdapter;
61 import org.eclipse.swt.events.SelectionEvent;
62 import org.eclipse.swt.layout.GridData;
63 import org.eclipse.swt.layout.GridLayout;
64 import org.eclipse.swt.widgets.Button;
65 import org.eclipse.swt.widgets.Composite;
66 import org.eclipse.swt.widgets.Table;
67 import org.eclipse.swt.widgets.Text;
68
69
70
71
72
73
74
75
76
77 public class TemplateValueCatalogue extends LazyCTabControl {
78 private static final long serialVersionUID = -5018569293721397600L;
79 public final static String CTAB_ID = "org.argeo.connect.ui.ctab.templateValueCatalogue";
80
81
82 private final ResourcesService resourcesService;
83 private final SystemWorkbenchService systemWorkbenchService;
84 private final Node templateNode;
85 private final String propertyName;
86 private final String taggableType;
87
88
89 private final AbstractConnectEditor editor;
90 private MyFormPart myFormPart;
91
92 public TemplateValueCatalogue(Composite parent, int style, AbstractConnectEditor editor,
93 ResourcesService resourcesService, SystemWorkbenchService systemWorkbenchService, Node templateNode,
94 String propertyName, String taggableType) {
95 super(parent, style);
96 this.editor = editor;
97 this.resourcesService = resourcesService;
98 this.systemWorkbenchService = systemWorkbenchService;
99 this.templateNode = templateNode;
100 this.propertyName = propertyName;
101 this.taggableType = taggableType;
102 }
103
104 @Override
105 public void refreshPartControl() {
106 myFormPart.refresh();
107 layout(true, true);
108 }
109
110 @Override
111 public void createPartControl(Composite parent) {
112
113 parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
114 myFormPart = new MyFormPart(this);
115 myFormPart.initialize(editor.getManagedForm());
116 editor.getManagedForm().addPart(myFormPart);
117 }
118
119 private class MyFormPart extends AbstractPanelFormPart {
120 private TableViewer valuesViewer;
121 private TableViewer instancesViewer;
122
123 public MyFormPart(Composite parent) {
124 super(parent, editor, templateNode);
125 }
126
127 protected void reCreateChildComposite(Composite panel, Node editionInfo) {
128
129 Button addBtn = null;
130 if (isEditing()) {
131 panel.setLayout(new GridLayout());
132 addBtn = new Button(panel, SWT.PUSH);
133 addBtn.setText("Add a value");
134 configureAddValueBtn(this, addBtn);
135 } else {
136 GridLayout gl = EclipseUiUtils.noSpaceGridLayout();
137 gl.verticalSpacing = 5;
138 panel.setLayout(gl);
139 }
140
141
142 Composite valuesCmp = new Composite(panel, SWT.NO_FOCUS);
143 GridData gd = EclipseUiUtils.fillWidth();
144 gd.heightHint = 150;
145 valuesCmp.setLayoutData(gd);
146 valuesViewer = createValuesViewer(valuesCmp);
147 valuesViewer.setContentProvider(new ValuesTableCP());
148 valuesViewer.getTable().addSelectionListener(new MyEditRemoveAdapter());
149
150 Composite instancesCmp = new Composite(panel, SWT.NO_FOCUS);
151 instancesCmp.setLayoutData(EclipseUiUtils.fillAll());
152 instancesViewer = createInstancesViewer(instancesCmp);
153 instancesViewer.setContentProvider(new InstancesTableCP(instancesViewer));
154 instancesViewer.addDoubleClickListener(new InstanceDClickAdapter());
155
156
157
158 valuesViewer.addSelectionChangedListener(new ISelectionChangedListener() {
159 @Override
160 public void selectionChanged(SelectionChangedEvent event) {
161 IStructuredSelection selection = (IStructuredSelection) event.getSelection();
162 if (!selection.isEmpty()) {
163 String currSelected = (String) selection.getFirstElement();
164 RowIterator rit = query(currSelected);
165 setViewerInput(instancesViewer, ConnectJcrUtils.rowIteratorToArray(rit));
166 }
167 }
168 });
169
170 refreshContent(panel, editionInfo);
171 }
172
173 protected void refreshContent(Composite parent, Node editionInfo) {
174 try {
175 valuesViewer
176 .setInput(ConnectJcrUtils.getMultiAsList(templateNode, propertyName).toArray(new String[0]));
177 valuesViewer.refresh();
178 setViewerInput(instancesViewer, null);
179 if (editionInfo.getSession().hasPendingChanges())
180 MyFormPart.this.markDirty();
181 } catch (RepositoryException e) {
182 throw new ConnectException("unable to occurrences for " + editionInfo, e);
183 }
184 }
185 }
186
187
188
189
190
191 protected RowIterator query(String currVal) {
192 try {
193 Session session = templateNode.getSession();
194 QueryManager queryManager = session.getWorkspace().getQueryManager();
195
196 QueryObjectModelFactory factory = queryManager.getQOMFactory();
197 Selector source = factory.selector(taggableType, taggableType);
198
199 StaticOperand so = factory.literal(session.getValueFactory().createValue(currVal));
200 DynamicOperand dyo = factory.propertyValue(source.getSelectorName(), propertyName);
201 Constraint constraint = factory.comparison(dyo, QueryObjectModelConstants.JCR_OPERATOR_EQUAL_TO, so);
202
203 Ordering order = factory
204 .ascending(factory.upperCase(factory.propertyValue(source.getSelectorName(), Property.JCR_TITLE)));
205 Ordering[] orderings = { order };
206 QueryObjectModel query = factory.createQuery(source, constraint, orderings, null);
207
208 QueryResult result = query.execute();
209 return result.getRows();
210 } catch (RepositoryException e) {
211 throw new ConnectException("Unable to list entities with property " + currVal + " for property "
212 + propertyName + " of " + templateNode, e);
213 }
214 }
215
216
217 private TableViewer createValuesViewer(Composite parent) {
218 parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
219 final Table table = new Table(parent, SWT.SINGLE | SWT.V_SCROLL | SWT.H_SCROLL);
220 table.setHeaderVisible(true);
221 table.setLinesVisible(true);
222 table.setLayoutData(EclipseUiUtils.fillAll());
223 table.setData(RWT.MARKUP_ENABLED, Boolean.TRUE);
224 table.setData(RWT.CUSTOM_ITEM_HEIGHT, Integer.valueOf(24));
225
226 TableViewer viewer = new TableViewer(table);
227
228 TableViewerColumn col = ViewerUtils.createTableViewerColumn(viewer, "Name", SWT.NONE, 400);
229 col.setLabelProvider(new ColumnLabelProvider() {
230 private static final long serialVersionUID = 1L;
231
232 @Override
233 public String getText(Object element) {
234 return ConnectUtils.replaceAmpersand((String) element);
235 }
236 });
237
238 if (editor.isEditing()) {
239 col = ViewerUtils.createTableViewerColumn(viewer, "", SWT.NONE, 80);
240 col.setLabelProvider(new ColumnLabelProvider() {
241 private static final long serialVersionUID = 1L;
242
243 @Override
244 public String getText(Object element) {
245 String value = ConnectUtils.replaceAmpersand((String) element);
246 String editLink = ConnectUiSnippets.getRWTLink(
247 ConnectUiConstants.CRUD_EDIT + ConnectUiConstants.HREF_SEPARATOR + value,
248 ConnectUiConstants.CRUD_EDIT);
249 String removeLink = ConnectUiSnippets.getRWTLink(
250 ConnectUiConstants.CRUD_DELETE + ConnectUiConstants.HREF_SEPARATOR + value,
251 ConnectUiConstants.CRUD_DELETE);
252 return editLink + ConnectUiConstants.NB_DOUBLE_SPACE + removeLink;
253 }
254 });
255 }
256 return viewer;
257 }
258
259 private class MyEditRemoveAdapter extends SelectionAdapter {
260 private static final long serialVersionUID = 1L;
261
262 public MyEditRemoveAdapter() {
263 }
264
265 public void widgetSelected(SelectionEvent event) {
266 if (event.detail == RWT.HYPERLINK) {
267 String href = event.text;
268 String[] val = href.split(ConnectUiConstants.HREF_SEPARATOR);
269 EditValueWizard wizard = new EditValueWizard(val[0], val[1]);
270 WizardDialog dialog = new WizardDialog(TemplateValueCatalogue.this.getShell(), wizard);
271
272
273
274
275 if (Window.OK == dialog.open()) {
276 try {
277
278
279 if (templateNode.getSession().hasPendingChanges())
280 templateNode.getSession().save();
281 } catch (RepositoryException re) {
282 throw new ConnectException("Unable to save session for " + templateNode, re);
283 }
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303 }
304 }
305 }
306 }
307
308 private class ValuesTableCP implements IStructuredContentProvider {
309 private static final long serialVersionUID = 1L;
310
311 private String[] elements;
312
313
314 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
315 elements = (String[]) newInput;
316 }
317
318 public Object[] getElements(Object arg0) {
319 return elements;
320 }
321
322 @Override
323 public void dispose() {
324 }
325 }
326
327 private TableViewer createInstancesViewer(Composite parent) {
328 parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
329 ArrayList<ConnectColumnDefinition> colDefs = new ArrayList<ConnectColumnDefinition>();
330 colDefs.add(new ConnectColumnDefinition(taggableType, Property.JCR_TITLE, PropertyType.STRING, "Instances",
331 new TitleIconRowLP(systemWorkbenchService, taggableType, Property.JCR_TITLE), 350));
332 VirtualJcrTableViewer tableCmp = new VirtualJcrTableViewer(parent, SWT.MULTI, colDefs);
333 tableCmp.setLayoutData(EclipseUiUtils.fillAll());
334 TableViewer viewer = tableCmp.getTableViewer();
335 viewer.setContentProvider(new InstancesTableCP(viewer));
336 return viewer;
337 }
338
339 private class InstanceDClickAdapter implements IDoubleClickListener {
340 @Override
341 public void doubleClick(DoubleClickEvent event) {
342 Object obj = ((IStructuredSelection) event.getSelection()).getFirstElement();
343 Node occurrence = ConnectJcrUtils.getNodeFromElement(obj, taggableType);
344
345
346
347 systemWorkbenchService.openEntityEditor(occurrence);
348 }
349 }
350
351 private class InstancesTableCP implements ILazyContentProvider {
352 private static final long serialVersionUID = 1L;
353 private TableViewer viewer;
354 private Row[] elements;
355
356 public InstancesTableCP(TableViewer viewer) {
357 this.viewer = viewer;
358 }
359
360 public void dispose() {
361 }
362
363 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
364
365
366 viewer.setSelection(null);
367 if (newInput == null)
368 elements = null;
369 else
370 this.elements = (Row[]) newInput;
371 }
372
373 public void updateElement(int index) {
374 viewer.replace(elements[index], index);
375 }
376 }
377
378
379 private void setViewerInput(TableViewer viewer, Row[] rows) {
380 viewer.setInput(rows);
381
382 int count = 0;
383 if (rows != null)
384 count = rows.length;
385 viewer.setItemCount(count);
386 viewer.refresh();
387 }
388
389 private void configureAddValueBtn(final AbstractFormPart formPart, final Button button) {
390 String tooltip = "Create a new value for this catalogue";
391 button.setToolTipText(tooltip);
392 button.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false));
393
394 button.addSelectionListener(new SelectionAdapter() {
395 private static final long serialVersionUID = 1L;
396
397 @Override
398 public void widgetSelected(SelectionEvent e) {
399 String value = SingleValue.ask("New Option", "Enrich current catalogue with a new option");
400 if (EclipseUiUtils.notEmpty(value)) {
401 String errMsg = ConnectJcrUtils.addMultiPropertyValue(templateNode, propertyName, value);
402 if (EclipseUiUtils.isEmpty(errMsg)) {
403 formPart.markDirty();
404 formPart.refresh();
405 } else
406 MessageDialog.openError(button.getShell(), "Duplicate value", errMsg);
407 }
408
409 }
410 });
411 }
412
413 public class EditValueWizard extends Wizard {
414
415
416 private final String actionType;
417 private final String oldValue;
418
419
420 private Text newValueTxt;
421
422 public EditValueWizard(String actionType, String oldValue) {
423 this.actionType = actionType;
424 this.oldValue = oldValue;
425 }
426
427 @Override
428 public void addPages() {
429 try {
430 setWindowTitle("Update wizard");
431 if (ConnectUiConstants.CRUD_EDIT.equals(actionType)) {
432 MainInfoPage inputPage = new MainInfoPage("Configure");
433 addPage(inputPage);
434 }
435 RecapPage recapPage = new RecapPage("Validate and launch");
436 addPage(recapPage);
437 } catch (Exception e) {
438 throw new ConnectException("Cannot add page to wizard", e);
439 }
440 }
441
442
443
444
445
446 @Override
447 public boolean performFinish() {
448 String errMsg = null;
449 String newValue = null;
450
451 List<String> existingValues = resourcesService.getTemplateCatalogue(templateNode, propertyName, null);
452
453
454 if (ConnectUiConstants.CRUD_EDIT.equals(actionType)) {
455 newValue = newValueTxt.getText();
456
457 if (EclipseUiUtils.isEmpty(newValue))
458 errMsg = "New value cannot be blank or an empty string";
459 else if (oldValue.equals(newValue))
460 errMsg = "New value is the same as old one.\n" + "Either enter a new one or press cancel.";
461 else if (existingValues.contains(newValue))
462 errMsg = "The new chosen value is already used.\n" + "Either enter a new one or press cancel.";
463 }
464 if (errMsg != null) {
465 MessageDialog.openError(getShell(), "Unvalid information", errMsg);
466 return false;
467 }
468
469 resourcesService.updateCatalogueValue(templateNode, taggableType, propertyName, oldValue, newValue);
470 return true;
471 }
472
473 @Override
474 public boolean performCancel() {
475 return true;
476 }
477
478 @Override
479 public boolean canFinish() {
480 return getContainer().getCurrentPage().getNextPage() == null;
481 }
482
483 protected class MainInfoPage extends WizardPage {
484 private static final long serialVersionUID = 1L;
485
486 public MainInfoPage(String pageName) {
487 super(pageName);
488 setTitle("Enter a new value for this catalogue's item");
489 setMessage("As reminder, former value was: \"" + oldValue + "\"");
490 }
491
492 public void createControl(Composite parent) {
493 Composite body = new Composite(parent, SWT.NONE);
494 body.setLayout(new GridLayout(2, false));
495
496
497 ConnectUiUtils.createBoldLabel(body, "New Value");
498 newValueTxt = new Text(body, SWT.BORDER);
499 newValueTxt.setMessage("was: " + oldValue);
500 newValueTxt.setText(oldValue);
501 newValueTxt.setLayoutData(EclipseUiUtils.fillWidth());
502 setControl(body);
503 newValueTxt.setFocus();
504 }
505 }
506
507 protected class RecapPage extends WizardPage {
508 private static final long serialVersionUID = 1L;
509 private TableViewer membersViewer;
510 private Composite body;
511
512 public RecapPage(String pageName) {
513 super(pageName);
514 setTitle("Check and confirm");
515 setMessage("The below listed items will be impacted.\nAre you sure you want to proceed?");
516 }
517
518 public void createControl(Composite parent) {
519 body = new Composite(parent, SWT.NONE);
520 membersViewer = createInstancesViewer(body);
521 setControl(body);
522 }
523
524 @Override
525 public void setVisible(boolean visible) {
526 super.setVisible(visible);
527
528
529
530
531
532
533 RowIterator rit = query(oldValue);
534 List<Row> rows = new ArrayList<Row>();
535 while (rit.hasNext())
536 rows.add(rit.nextRow());
537 setViewerInput(membersViewer, rows.toArray(new Row[0]));
538 }
539 }
540 }
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583 }