1 package org.argeo.connect.e4.parts;
2
3 import java.text.DateFormat;
4 import java.text.SimpleDateFormat;
5
6 import javax.annotation.PostConstruct;
7 import javax.annotation.PreDestroy;
8 import javax.inject.Inject;
9 import javax.jcr.Node;
10 import javax.jcr.Property;
11 import javax.jcr.Repository;
12 import javax.jcr.RepositoryException;
13 import javax.jcr.Session;
14 import javax.jcr.nodetype.NodeType;
15 import javax.jcr.security.AccessControlManager;
16 import javax.jcr.security.Privilege;
17
18 import org.apache.commons.logging.Log;
19 import org.apache.commons.logging.LogFactory;
20 import org.argeo.cms.ui.eclipse.forms.AbstractFormPart;
21 import org.argeo.cms.ui.eclipse.forms.FormToolkit;
22 import org.argeo.cms.ui.eclipse.forms.IFormPart;
23 import org.argeo.connect.ConnectConstants;
24 import org.argeo.connect.ConnectException;
25 import org.argeo.connect.SystemAppService;
26 import org.argeo.connect.UserAdminService;
27 import org.argeo.connect.e4.ConnectE4Constants;
28 import org.argeo.connect.e4.ConnectE4Msg;
29 import org.argeo.connect.resources.ResourcesService;
30 import org.argeo.connect.ui.ConnectEditor;
31 import org.argeo.connect.ui.ConnectUiUtils;
32 import org.argeo.connect.ui.SystemWorkbenchService;
33 import org.argeo.connect.ui.parts.CompositeManagedForm;
34 import org.argeo.connect.util.ConnectJcrUtils;
35 import org.argeo.eclipse.ui.EclipseUiUtils;
36 import org.argeo.jcr.JcrUtils;
37 import org.eclipse.core.runtime.IProgressMonitor;
38 import org.eclipse.e4.core.di.annotations.Optional;
39 import org.eclipse.e4.ui.di.Focus;
40 import org.eclipse.e4.ui.di.Persist;
41 import org.eclipse.e4.ui.model.application.ui.MDirtyable;
42 import org.eclipse.e4.ui.model.application.ui.basic.MPart;
43 import org.eclipse.rap.rwt.RWT;
44 import org.eclipse.rap.rwt.client.service.BrowserNavigation;
45 import org.eclipse.swt.SWT;
46 import org.eclipse.swt.events.SelectionAdapter;
47 import org.eclipse.swt.events.SelectionEvent;
48 import org.eclipse.swt.layout.FormLayout;
49 import org.eclipse.swt.layout.GridData;
50 import org.eclipse.swt.layout.GridLayout;
51 import org.eclipse.swt.widgets.Button;
52 import org.eclipse.swt.widgets.Composite;
53
54
55
56
57
58
59
60 public abstract class AbstractConnectEditor implements ConnectEditor {
61 private final static Log log = LogFactory.getLog(AbstractConnectEditor.class);
62
63
64
65
66
67 @Inject
68 private Repository repository;
69 @Inject
70 private UserAdminService userAdminService;
71 @Inject
72 private ResourcesService resourcesService;
73 @Inject
74 private SystemAppService systemAppService;
75
76 private Session session;
77
78
79 @Inject
80 private SystemWorkbenchService systemWorkbenchService;
81 @Inject
82 private MPart mPart;
83 @Inject
84 private MDirtyable mDirtyable;
85
86
87
88
89
90
91
92
93 private Boolean isEditing = false;
94
95
96
97 protected final static int SHORT_NAME_LENGHT = 10;
98
99 private final static DateFormat df = new SimpleDateFormat(ConnectConstants.DEFAULT_DATE_TIME_FORMAT);
100
101
102 private Node node;
103 private String partName;
104
105
106 private ConnectManagedForm mForm;
107 private FormToolkit toolkit;
108 private Composite main;
109
110
111 private BrowserNavigation browserNavigation;
112
113
114 private void init(String entityId) {
115
116
117 try {
118 session = repository.login();
119
120
121 node = getSession().getNodeByIdentifier(entityId);
122
123 updatePartName();
124 updateToolTip();
125 } catch (RepositoryException e) {
126 throw new ConnectException("Unable to create new session" + " to use with current editor", e);
127 }
128 }
129
130 protected void init() {
131
132 }
133
134 @PostConstruct
135 public void createPartControl(Composite parent) {
136 String entityId = mPart.getPersistedState().get(ConnectE4Constants.ENTITY_ID);
137 init(entityId);
138 init();
139
140
141
142
143 toolkit = new FormToolkit(parent.getDisplay());
144
145 mForm = new ConnectManagedForm(parent, toolkit);
146 mForm.setContainer(AbstractConnectEditor.this);
147
148 main = toolkit.createComposite(parent);
149 createMainLayout(main);
150 forceRefresh();
151
152 browserNavigation = RWT.getClient().getService(BrowserNavigation.class);
153 }
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168 protected void createMainLayout(Composite parent) {
169 parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
170
171
172
173 Composite header = toolkit.createComposite(parent, SWT.NO_FOCUS | SWT.NO_SCROLL | SWT.NO_TRIM);
174 GridLayout gl = ConnectUiUtils.noSpaceGridLayout(2);
175 gl.marginRight = 5;
176 header.setLayout(gl);
177 header.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
178
179
180 Composite left = toolkit.createComposite(header, SWT.NO_FOCUS | SWT.NO_SCROLL | SWT.NO_TRIM);
181 left.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
182 populateHeader(left);
183
184
185 Composite right = toolkit.createComposite(header, SWT.NO_FOCUS | SWT.NO_SCROLL | SWT.NO_TRIM);
186 GridData gd = new GridData(SWT.CENTER, SWT.TOP, false, false);
187 gd.verticalIndent = 5;
188 right.setLayoutData(gd);
189 populateButtonsComposite(right);
190
191
192 Composite body = toolkit.createComposite(parent, SWT.NO_FOCUS);
193 body.setLayoutData(EclipseUiUtils.fillAll());
194 populateBody(body);
195 }
196
197
198 protected void updatePartName() {
199 String name = ConnectJcrUtils.get(node, Property.JCR_TITLE);
200 if (EclipseUiUtils.notEmpty(name)) {
201 if (name.length() > SHORT_NAME_LENGHT)
202 name = name.substring(0, SHORT_NAME_LENGHT - 1) + "...";
203 setPartName(name);
204 }
205 }
206
207 protected void setPartName(String name) {
208 this.partName = name;
209 mPart.setLabel(name);
210 }
211
212
213 protected void updateToolTip() {
214 String displayName = ConnectJcrUtils.get(node, Property.JCR_TITLE);
215 if (EclipseUiUtils.isEmpty(displayName))
216 displayName = "current item";
217
218
219 }
220
221
222 protected abstract void populateBody(Composite parent);
223
224
225 protected abstract void populateHeader(Composite parent);
226
227 protected void populateButtonsComposite(final Composite buttons) {
228 buttons.setLayout(new FormLayout());
229
230
231 final Composite roPanelCmp = toolkit.createComposite(buttons, SWT.NO_FOCUS);
232 ConnectUiUtils.setSwitchingFormData(roPanelCmp);
233
234 roPanelCmp.setLayout(new GridLayout());
235
236
237
238 if (canEdit()) {
239 Button editBtn = toolkit.createButton(roPanelCmp, ConnectE4Msg.edit.lead(), SWT.PUSH);
240
241 editBtn.setFont(EclipseUiUtils.getBoldFont(roPanelCmp));
242 editBtn.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
243 editBtn.addSelectionListener(new SelectionAdapter() {
244 private static final long serialVersionUID = 1L;
245
246 @Override
247 public void widgetSelected(SelectionEvent e) {
248
249 if (canEdit()) {
250
251
252
253
254
255 changeEditingState(PRIOR_ACTION_CHECKOUT, EDITING);
256 }
257 }
258 });
259 }
260
261
262 if (showRefreshButton()) {
263 Button refreshBtn = toolkit.createButton(roPanelCmp, ConnectE4Msg.refresh.lead(), SWT.PUSH);
264
265 refreshBtn.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
266 refreshBtn.addSelectionListener(new SelectionAdapter() {
267 private static final long serialVersionUID = 1L;
268
269 @Override
270 public void widgetSelected(SelectionEvent e) {
271 forceRefresh();
272 }
273 });
274 }
275
276 final Composite editPanelCmp = toolkit.createComposite(buttons, SWT.NONE);
277 ConnectUiUtils.setSwitchingFormData(editPanelCmp);
278
279 editPanelCmp.setLayout(new GridLayout());
280
281 Button saveBtn = toolkit.createButton(editPanelCmp, ConnectE4Msg.save.lead(), SWT.PUSH);
282
283 saveBtn.setFont(EclipseUiUtils.getBoldFont(editPanelCmp));
284 saveBtn.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
285 saveBtn.addSelectionListener(new SelectionAdapter() {
286 private static final long serialVersionUID = 1L;
287
288 @Override
289 public void widgetSelected(SelectionEvent e) {
290 try {
291 if (isEditing())
292 if (node.getSession().hasPendingChanges()) {
293
294
295 doSave(null);
296 } else {
297
298
299
300
301
302
303
304 changeEditingState(PRIOR_ACTION_CANCEL, NOT_EDITING);
305 }
306 } catch (RepositoryException re) {
307 throw new ConnectException("Unable to save pending changes on " + node, re);
308 }
309 }
310 });
311
312 Button cancelBtn = toolkit.createButton(editPanelCmp, ConnectE4Msg.cancel.lead(), SWT.PUSH);
313
314 cancelBtn.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
315 cancelBtn.addSelectionListener(new SelectionAdapter() {
316 private static final long serialVersionUID = 1L;
317
318 @Override
319 public void widgetSelected(SelectionEvent e) {
320 if (isEditing()) {
321
322
323
324
325
326
327 changeEditingState(PRIOR_ACTION_CANCEL, NOT_EDITING);
328 }
329 }
330 });
331
332 if (showDeleteButton()) {
333 Button deleteBtn = toolkit.createButton(editPanelCmp, ConnectE4Msg.delete.lead(), SWT.PUSH);
334
335 deleteBtn.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
336 deleteBtn.addSelectionListener(new SelectionAdapter() {
337 private static final long serialVersionUID = 1L;
338
339 @Override
340 public void widgetSelected(SelectionEvent e) {
341
342
343
344
345 getSystemWorkbenchService().callCommand(getSystemWorkbenchService().getDeleteEntityCmdId(), null);
346 }
347 });
348 }
349
350
351 addEditButtons(editPanelCmp);
352
353 editPanelCmp.layout();
354 roPanelCmp.layout();
355
356 AbstractFormPart editPart = new AbstractFormPart() {
357
358 public void refresh() {
359 if (isEditing())
360 editPanelCmp.moveAbove(roPanelCmp);
361 else
362 editPanelCmp.moveBelow(roPanelCmp);
363 roPanelCmp.getParent().layout();
364 }
365 };
366 editPart.initialize(mForm);
367 mForm.addPart(editPart);
368 }
369
370
371 protected void addEditButtons(Composite parent) {
372 }
373
374 final static String EDITING = "editing";
375 final static String NOT_EDITING = "notEditing";
376 final static String PRIOR_ACTION_SAVE = "save";
377 final static String PRIOR_ACTION_CHECKOUT = "checkout";
378 final static String PRIOR_ACTION_CANCEL = "cancel";
379
380 protected void changeEditingState(String priorAction, String newState) {
381
382 Node node = getNode();
383 if (PRIOR_ACTION_SAVE.equals(priorAction))
384 ConnectJcrUtils.saveAndPublish(node, true);
385 else if (PRIOR_ACTION_CANCEL.equals(priorAction))
386 JcrUtils.discardUnderlyingSessionQuietly(node);
387 else if (PRIOR_ACTION_CHECKOUT.equals(priorAction)) {
388 if (!ConnectJcrUtils.checkCOStatusBeforeUpdate(node))
389 log.warn("Referencing node " + node + " was checked in when we wanted to update");
390 }
391
392 if (EDITING.equals(newState))
393 startEditing();
394 else if (NOT_EDITING.equals(newState))
395 stopEditing();
396
397 }
398
399
400
401 public void setNode(Node node) {
402 this.node = node;
403 }
404
405 public FormToolkit getFormToolkit() {
406 return toolkit;
407 }
408
409 public ConnectManagedForm getManagedForm() {
410 return mForm;
411 }
412
413
414 protected boolean showDeleteButton() {
415 return false;
416 }
417
418 protected boolean showRefreshButton() {
419 return false;
420 }
421
422 @Persist
423 public void doSave(@Optional IProgressMonitor monitor) {
424 if (canSave())
425 mForm.commit(true);
426 stopEditing();
427 }
428
429
430
431
432
433
434 protected void commitInternalLinkedForm(boolean onSave) {
435
436
437 for (IFormPart part : mForm.getParts()) {
438 if (part.isDirty())
439 part.commit(onSave);
440 }
441 if (onSave) {
442 systemAppService.saveEntity(node, true);
443 updatePartName();
444 }
445 }
446
447 public void startEditing() {
448 if (!isEditing) {
449 isEditing = true;
450 markAllStale();
451
452
453 forceRefresh();
454 notifyEditionStateChange();
455 main.layout(true, true);
456 }
457 }
458
459 public void stopEditing() {
460 if (isEditing) {
461 isEditing = false;
462 markAllStale();
463 forceRefresh();
464 notifyEditionStateChange();
465 main.layout(true, true);
466 }
467 }
468
469 protected void markAllStale() {
470 if (mForm != null)
471 for (IFormPart part : mForm.getParts())
472 if (part instanceof AbstractFormPart)
473 ((AbstractFormPart) part).markStale();
474 }
475
476
477
478
479
480 protected void notifyEditionStateChange() {
481 mDirtyable.setDirty(isEditing);
482
483
484
485
486
487
488
489 }
490
491 public Boolean isEditing() {
492 return isEditing;
493 }
494
495 public Boolean canEdit() {
496 try {
497 AccessControlManager acm = session.getAccessControlManager();
498 Privilege[] privs = { acm.privilegeFromName(Privilege.JCR_WRITE) };
499 return acm.hasPrivileges(node.getPath(), privs);
500 } catch (RepositoryException e) {
501 throw new ConnectException("Unable to check privilege on " + node, e);
502 }
503 }
504
505
506 public void forceRefresh(Object object) {
507 if (log.isTraceEnabled())
508 log.trace("Starting Editor refresh");
509 long start, lap, lapEnd, end;
510 start = System.currentTimeMillis();
511 for (IFormPart part : mForm.getParts()) {
512 lap = System.currentTimeMillis();
513 part.refresh();
514 lapEnd = System.currentTimeMillis();
515 if (log.isTraceEnabled())
516 log.trace("FormPart " + part.getClass().getName() + " refreshed in " + (lapEnd - lap) + " ms");
517 }
518 lap = System.currentTimeMillis();
519 mForm.reflow(true);
520 end = System.currentTimeMillis();
521 if (log.isTraceEnabled()) {
522 log.trace("Reflow done in " + (end - lap) + " ms");
523 log.trace("Full refresh of " + this.getClass().getName() + " in " + (end - start) + " ms");
524 }
525 }
526
527 public void forceRefresh() {
528 forceRefresh(null);
529 }
530
531
532
533
534 protected boolean canSave() {
535 return true;
536 }
537
538 public String getStatusLineMessage() {
539 return getLastModifiedMessage();
540 }
541
542 public String getLastModifiedMessage() {
543 Node currNode = getNode();
544 StringBuilder builder = new StringBuilder();
545 try {
546 if (currNode.isNodeType(NodeType.MIX_TITLE)) {
547 builder.append(ConnectJcrUtils.get(currNode, Property.JCR_TITLE)).append(" - ");
548 }
549 if (currNode.isNodeType(NodeType.MIX_LAST_MODIFIED)) {
550 builder.append("Last updated on ");
551 builder.append(df.format(currNode.getProperty(Property.JCR_LAST_MODIFIED).getDate().getTime()));
552 builder.append(", by ");
553 String lstModByDn = currNode.getProperty(Property.JCR_LAST_MODIFIED_BY).getString();
554 builder.append(userAdminService.getUserDisplayName(lstModByDn));
555 builder.append(". ");
556 }
557 return builder.toString();
558 } catch (RepositoryException re) {
559 throw new ConnectException("Unable to create last " + "modified message for " + currNode, re);
560 }
561 }
562
563 public boolean isDirty() {
564 try {
565 boolean isDirty = session.hasPendingChanges();
566 return isDirty;
567 } catch (Exception e) {
568 throw new ConnectException("Error getting session status on " + node, e);
569 }
570 }
571
572 @PreDestroy
573 public void dispose() {
574 try {
575 if (node != null)
576 JcrUtils.discardUnderlyingSessionQuietly(node);
577 } finally {
578 JcrUtils.logoutQuietly(session);
579 }
580 browserNavigation.pushState("~", null);
581
582 }
583
584 @Focus
585 public void setFocus() {
586 try {
587 browserNavigation.pushState(node.getPath(), partName);
588 } catch (RepositoryException e) {
589 log.error("Cannot set client state", e);
590 }
591 }
592
593 public void doSaveAs() {
594 }
595
596 public boolean isSaveAsAllowed() {
597 return false;
598 }
599
600 public class ConnectManagedForm extends CompositeManagedForm {
601 public ConnectManagedForm(Composite parent, FormToolkit toolkit) {
602 super(parent, toolkit);
603 }
604
605
606 public void dirtyStateChanged() {
607
608 }
609
610
611
612
613 public void commit(boolean onSave) {
614 commitInternalLinkedForm(onSave);
615 }
616
617 public AbstractConnectEditor getEditor() {
618 return (AbstractConnectEditor) getContainer();
619 }
620 }
621
622
623 protected Session getSession() {
624 return session;
625 }
626
627 protected Repository getRepository() {
628 return repository;
629 }
630
631
632 public Node getNode() {
633 return node;
634 }
635
636 protected UserAdminService getUserAdminService() {
637 return userAdminService;
638 }
639
640 protected ResourcesService getResourcesService() {
641 return resourcesService;
642 }
643
644 protected SystemAppService getSystemAppService() {
645 return systemAppService;
646 }
647
648 protected SystemWorkbenchService getSystemWorkbenchService() {
649 return systemWorkbenchService;
650 }
651
652
653 public void setRepository(Repository repository) {
654 log.trace("setRepository is deprecated and ignored");
655
656 }
657
658 public void setUserAdminService(UserAdminService userAdminService) {
659 log.trace("setUserAdminService is deprecated and ignored");
660
661 }
662
663 public void setResourcesService(ResourcesService resourcesService) {
664 log.trace("setResourcesService is deprecated and ignored");
665
666 }
667
668 public void setSystemAppService(SystemAppService systemAppService) {
669 log.trace("setSystemAppService is deprecated and ignored");
670
671 }
672
673 public void setSystemWorkbenchService(SystemWorkbenchService systemWorkbenchService) {
674 log.trace("setSystemWorkbenchService is deprecated and ignored");
675
676 }
677 }