From: Mathieu Baudier Date: Tue, 18 Oct 2022 08:14:05 +0000 (+0200) Subject: Move SWT components to their directory X-Git-Tag: v2.3.10~10 X-Git-Url: https://git.argeo.org/?a=commitdiff_plain;h=777e96758c36e1cfdecedb8749e384f6fe124a06;p=gpl%2Fargeo-jcr.git Move SWT components to their directory --- diff --git a/Makefile b/Makefile index 7f3e732..df27ce8 100644 --- a/Makefile +++ b/Makefile @@ -6,8 +6,8 @@ A2_CATEGORY = org.argeo.cms.jcr BUNDLES = \ org.argeo.cms.jcr \ -org.argeo.cms.jcr.ui \ -org.argeo.cms.jcr.e4 \ +swt/org.argeo.cms.jcr.ui \ +swt/org.argeo.cms.jcr.e4 \ DEP_CATEGORIES = \ org.argeo.tp \ diff --git a/org.argeo.cms.jcr.e4/.classpath b/org.argeo.cms.jcr.e4/.classpath deleted file mode 100644 index 81fe078..0000000 --- a/org.argeo.cms.jcr.e4/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/org.argeo.cms.jcr.e4/.project b/org.argeo.cms.jcr.e4/.project deleted file mode 100644 index 01f8dd2..0000000 --- a/org.argeo.cms.jcr.e4/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - org.argeo.cms.jcr.e4 - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/org.argeo.cms.jcr.e4/bnd.bnd b/org.argeo.cms.jcr.e4/bnd.bnd deleted file mode 100644 index fde6c09..0000000 --- a/org.argeo.cms.jcr.e4/bnd.bnd +++ /dev/null @@ -1,12 +0,0 @@ -Import-Package: \ -org.eclipse.swt,\ -org.eclipse.swt.widgets;version="0.0.0",\ -org.eclipse.jface.window,\ -org.eclipse.core.commands.common,\ -org.eclipse.e4.ui.model.application.ui,\ -org.eclipse.e4.ui.model.application,\ -javax.jcr.nodetype,\ -org.apache.jackrabbit.*;version="[2,3)",\ -org.argeo.cms,\ -org.argeo.jcr,\ -* \ No newline at end of file diff --git a/org.argeo.cms.jcr.e4/build.properties b/org.argeo.cms.jcr.e4/build.properties deleted file mode 100644 index 34d2e4d..0000000 --- a/org.argeo.cms.jcr.e4/build.properties +++ /dev/null @@ -1,4 +0,0 @@ -source.. = src/ -output.. = bin/ -bin.includes = META-INF/,\ - . diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/EclipseJcrMonitor.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/EclipseJcrMonitor.java deleted file mode 100644 index e10738e..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/EclipseJcrMonitor.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.argeo.cms.e4.jcr; - -import org.argeo.jcr.JcrMonitor; -import org.eclipse.core.runtime.IProgressMonitor; - -/** - * Wraps an Eclipse {@link IProgressMonitor} so that it can be passed to - * framework agnostic Argeo routines. - */ -public class EclipseJcrMonitor implements JcrMonitor { - private final IProgressMonitor progressMonitor; - - public EclipseJcrMonitor(IProgressMonitor progressMonitor) { - this.progressMonitor = progressMonitor; - } - - public void beginTask(String name, int totalWork) { - progressMonitor.beginTask(name, totalWork); - } - - public void done() { - progressMonitor.done(); - } - - public boolean isCanceled() { - return progressMonitor.isCanceled(); - } - - public void setCanceled(boolean value) { - progressMonitor.setCanceled(value); - } - - public void setTaskName(String name) { - progressMonitor.setTaskName(name); - } - - public void subTask(String name) { - progressMonitor.subTask(name); - } - - public void worked(int work) { - progressMonitor.worked(work); - } -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/GenericPropertyPage.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/GenericPropertyPage.java deleted file mode 100644 index e17f17b..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/GenericPropertyPage.java +++ /dev/null @@ -1,141 +0,0 @@ -package org.argeo.cms.e4.jcr; - -import java.util.ArrayList; -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.PropertyIterator; -import javax.jcr.RepositoryException; - -import org.argeo.cms.ui.jcr.PropertyLabelProvider; -import org.argeo.eclipse.ui.EclipseUiException; -import org.eclipse.jface.layout.TreeColumnLayout; -import org.eclipse.jface.viewers.ColumnWeightData; -import org.eclipse.jface.viewers.IBaseLabelProvider; -import org.eclipse.jface.viewers.ITreeContentProvider; -import org.eclipse.jface.viewers.TreeViewer; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.ScrolledComposite; -import org.eclipse.swt.layout.FillLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Tree; -import org.eclipse.swt.widgets.TreeColumn; - -/** - * Generic editor property page. Lists all properties of current node as a - * complex tree. TODO: enable editing - */ -public class GenericPropertyPage { - - // Main business Objects - private Node currentNode; - - public GenericPropertyPage(Node currentNode) { - this.currentNode = currentNode; - } - - protected void createFormContent(Composite parent) { - Composite innerBox = new Composite(parent, SWT.NONE); - // Composite innerBox = new Composite(body, SWT.NO_FOCUS); - FillLayout layout = new FillLayout(); - layout.marginHeight = 5; - layout.marginWidth = 5; - innerBox.setLayout(layout); - createComplexTree(innerBox); - // TODO TreeColumnLayout triggers a scroll issue with the form: - // The inside body is always to big and a scroll bar is shown - // Composite tableCmp = new Composite(body, SWT.NO_FOCUS); - // createComplexTree(tableCmp); - } - - private TreeViewer createComplexTree(Composite parent) { - int style = SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION; - Tree tree = new Tree(parent, style); - TreeColumnLayout tableColumnLayout = new TreeColumnLayout(); - - createColumn(tree, tableColumnLayout, "Property", SWT.LEFT, 200, 30); - createColumn(tree, tableColumnLayout, "Value(s)", SWT.LEFT, 300, 60); - createColumn(tree, tableColumnLayout, "Type", SWT.LEFT, 75, 10); - createColumn(tree, tableColumnLayout, "Attributes", SWT.LEFT, 75, 0); - // Do not apply the treeColumnLayout it does not work yet - // parent.setLayout(tableColumnLayout); - - tree.setLinesVisible(true); - tree.setHeaderVisible(true); - - TreeViewer treeViewer = new TreeViewer(tree); - treeViewer.setContentProvider(new TreeContentProvider()); - treeViewer.setLabelProvider((IBaseLabelProvider) new PropertyLabelProvider()); - treeViewer.setInput(currentNode); - treeViewer.expandAll(); - return treeViewer; - } - - private static TreeColumn createColumn(Tree parent, TreeColumnLayout tableColumnLayout, String name, int style, - int width, int weight) { - TreeColumn column = new TreeColumn(parent, style); - column.setText(name); - column.setWidth(width); - column.setMoveable(true); - column.setResizable(true); - tableColumnLayout.setColumnData(column, new ColumnWeightData(weight, width, true)); - return column; - } - - private class TreeContentProvider implements ITreeContentProvider { - private static final long serialVersionUID = -6162736530019406214L; - - public Object[] getElements(Object parent) { - Object[] props = null; - try { - - if (parent instanceof Node) { - Node node = (Node) parent; - PropertyIterator pi; - pi = node.getProperties(); - List propList = new ArrayList(); - while (pi.hasNext()) { - propList.add(pi.nextProperty()); - } - props = propList.toArray(); - } - } catch (RepositoryException e) { - throw new EclipseUiException("Unexpected exception while listing node properties", e); - } - return props; - } - - public Object getParent(Object child) { - return null; - } - - public Object[] getChildren(Object parent) { - if (parent instanceof Property) { - Property prop = (Property) parent; - try { - if (prop.isMultiple()) - return prop.getValues(); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot get multi-prop values on " + prop, e); - } - } - return null; - } - - public boolean hasChildren(Object parent) { - try { - return (parent instanceof Property && ((Property) parent).isMultiple()); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot check if property is multiple for " + parent, e); - } - } - - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - } - - public void dispose() { - } - } -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/JcrBrowserView.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/JcrBrowserView.java deleted file mode 100644 index 0b77c07..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/JcrBrowserView.java +++ /dev/null @@ -1,349 +0,0 @@ -package org.argeo.cms.e4.jcr; - -import java.util.List; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import javax.inject.Inject; -import javax.jcr.Property; -import javax.jcr.PropertyType; -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.jcr.RepositoryFactory; -import javax.jcr.Session; -import javax.jcr.Value; -import javax.jcr.observation.Event; -import javax.jcr.observation.EventListener; -import javax.jcr.observation.ObservationManager; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.cms.security.CryptoKeyring; -import org.argeo.cms.security.Keyring; -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ui.jcr.JcrBrowserUtils; -import org.argeo.cms.ui.jcr.NodeContentProvider; -import org.argeo.cms.ui.jcr.NodeLabelProvider; -import org.argeo.cms.ui.jcr.OsgiRepositoryRegister; -import org.argeo.cms.ui.jcr.PropertiesContentProvider; -import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; -import org.argeo.cms.ux.widgets.TreeParent; -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.eclipse.ui.jcr.AsyncUiEventListener; -import org.argeo.eclipse.ui.jcr.util.NodeViewerComparer; -import org.argeo.jcr.JcrUtils; -import org.eclipse.e4.core.contexts.IEclipseContext; -import org.eclipse.e4.core.di.annotations.Optional; -import org.eclipse.e4.ui.services.EMenuService; -import org.eclipse.e4.ui.workbench.modeling.EPartService; -import org.eclipse.e4.ui.workbench.modeling.ESelectionService; -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.jface.viewers.IBaseLabelProvider; -import org.eclipse.jface.viewers.ISelectionChangedListener; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.ITreeContentProvider; -import org.eclipse.jface.viewers.SelectionChangedEvent; -import org.eclipse.jface.viewers.StructuredSelection; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.jface.viewers.TableViewerColumn; -import org.eclipse.jface.viewers.TreeViewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.SashForm; -import org.eclipse.swt.layout.FillLayout; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; - -/** - * Basic View to display a sash form to browse a JCR compliant multiple - * repository environment - */ -public class JcrBrowserView { - final static String ID = "org.argeo.cms.e4.jcrbrowser"; - final static String NODE_VIEWER_POPUP_MENU_ID = "org.argeo.cms.e4.popupmenu.nodeViewer"; - - private boolean sortChildNodes = true; - - /* DEPENDENCY INJECTION */ - @Inject - @Optional - private Keyring keyring; - @Inject - private RepositoryFactory repositoryFactory; - @Inject - private Repository nodeRepository; - - // Current user session on the home repository default workspace - private Session userSession; - - private OsgiRepositoryRegister repositoryRegister = new OsgiRepositoryRegister(); - - // This page widgets - private TreeViewer nodesViewer; - private NodeContentProvider nodeContentProvider; - private TableViewer propertiesViewer; - private EventListener resultsObserver; - - @PostConstruct - public void createPartControl(Composite parent, IEclipseContext context, EPartService partService, - ESelectionService selectionService, EMenuService menuService) { - repositoryRegister.init(); - - parent.setLayout(new FillLayout()); - SashForm sashForm = new SashForm(parent, SWT.VERTICAL); - // sashForm.setSashWidth(4); - // sashForm.setLayout(new FillLayout()); - - // Create the tree on top of the view - Composite top = new Composite(sashForm, SWT.NONE); - // GridLayout gl = new GridLayout(1, false); - top.setLayout(CmsSwtUtils.noSpaceGridLayout()); - - try { - this.userSession = this.nodeRepository.login(CmsConstants.HOME_WORKSPACE); - } catch (RepositoryException e) { - throw new CmsException("Cannot open user session", e); - } - - nodeContentProvider = new NodeContentProvider(userSession, keyring, repositoryRegister, repositoryFactory, - sortChildNodes); - - // nodes viewer - nodesViewer = createNodeViewer(top, nodeContentProvider); - - // context menu : it is completely defined in the plugin.xml file. - // MenuManager menuManager = new MenuManager(); - // Menu menu = menuManager.createContextMenu(nodesViewer.getTree()); - - // nodesViewer.getTree().setMenu(menu); - - nodesViewer.setInput(""); - - // Create the property viewer on the bottom - Composite bottom = new Composite(sashForm, SWT.NONE); - bottom.setLayout(CmsSwtUtils.noSpaceGridLayout()); - propertiesViewer = createPropertiesViewer(bottom); - - sashForm.setWeights(getWeights()); - nodesViewer.setComparer(new NodeViewerComparer()); - nodesViewer.addSelectionChangedListener(new ISelectionChangedListener() { - public void selectionChanged(SelectionChangedEvent event) { - IStructuredSelection selection = (IStructuredSelection) event.getSelection(); - selectionService.setSelection(selection.toList()); - } - }); - nodesViewer.addDoubleClickListener(new JcrE4DClickListener(nodesViewer, partService)); - menuService.registerContextMenu(nodesViewer.getControl(), NODE_VIEWER_POPUP_MENU_ID); - // getSite().registerContextMenu(menuManager, nodesViewer); - // getSite().setSelectionProvider(nodesViewer); - } - - @PreDestroy - public void dispose() { - JcrUtils.logoutQuietly(userSession); - repositoryRegister.destroy(); - } - - public void refresh(Object obj) { - // Enable full refresh from a command when no element of the tree is - // selected - if (obj == null) { - Object[] elements = nodeContentProvider.getElements(null); - for (Object el : elements) { - if (el instanceof TreeParent) - JcrBrowserUtils.forceRefreshIfNeeded((TreeParent) el); - getNodeViewer().refresh(el); - } - } else - getNodeViewer().refresh(obj); - } - - /** - * To be overridden to adapt size of form and result frames. - */ - protected int[] getWeights() { - return new int[] { 70, 30 }; - } - - protected TreeViewer createNodeViewer(Composite parent, final ITreeContentProvider nodeContentProvider) { - - final TreeViewer tmpNodeViewer = new TreeViewer(parent, SWT.MULTI); - - tmpNodeViewer.getTree().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - - tmpNodeViewer.setContentProvider(nodeContentProvider); - tmpNodeViewer.setLabelProvider((IBaseLabelProvider) new NodeLabelProvider()); - tmpNodeViewer.addSelectionChangedListener(new ISelectionChangedListener() { - public void selectionChanged(SelectionChangedEvent event) { - if (!event.getSelection().isEmpty()) { - IStructuredSelection sel = (IStructuredSelection) event.getSelection(); - Object firstItem = sel.getFirstElement(); - if (firstItem instanceof SingleJcrNodeElem) - propertiesViewer.setInput(((SingleJcrNodeElem) firstItem).getNode()); - } else { - propertiesViewer.setInput(""); - } - } - }); - - resultsObserver = new TreeObserver(tmpNodeViewer.getTree().getDisplay()); - if (keyring != null) - try { - ObservationManager observationManager = userSession.getWorkspace().getObservationManager(); - observationManager.addEventListener(resultsObserver, Event.PROPERTY_ADDED | Event.PROPERTY_CHANGED, "/", - true, null, null, false); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot register listeners", e); - } - - // tmpNodeViewer.addDoubleClickListener(new JcrDClickListener(tmpNodeViewer)); - return tmpNodeViewer; - } - - protected TableViewer createPropertiesViewer(Composite parent) { - propertiesViewer = new TableViewer(parent, SWT.NONE); - propertiesViewer.getTable().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - propertiesViewer.getTable().setHeaderVisible(true); - propertiesViewer.setContentProvider(new PropertiesContentProvider()); - TableViewerColumn col = new TableViewerColumn(propertiesViewer, SWT.NONE); - col.getColumn().setText("Name"); - col.getColumn().setWidth(200); - col.setLabelProvider(new ColumnLabelProvider() { - private static final long serialVersionUID = -6684361063107478595L; - - public String getText(Object element) { - try { - return ((Property) element).getName(); - } catch (RepositoryException e) { - throw new EclipseUiException("Unexpected exception in label provider", e); - } - } - }); - col = new TableViewerColumn(propertiesViewer, SWT.NONE); - col.getColumn().setText("Value"); - col.getColumn().setWidth(400); - col.setLabelProvider(new ColumnLabelProvider() { - private static final long serialVersionUID = -8201994187693336657L; - - public String getText(Object element) { - try { - Property property = (Property) element; - if (property.getType() == PropertyType.BINARY) - return ""; - else if (property.isMultiple()) { - StringBuffer buf = new StringBuffer("["); - Value[] values = property.getValues(); - for (int i = 0; i < values.length; i++) { - if (i != 0) - buf.append(", "); - buf.append(values[i].getString()); - } - buf.append(']'); - return buf.toString(); - } else - return property.getValue().getString(); - } catch (RepositoryException e) { - throw new EclipseUiException("Unexpected exception in label provider", e); - } - } - }); - col = new TableViewerColumn(propertiesViewer, SWT.NONE); - col.getColumn().setText("Type"); - col.getColumn().setWidth(200); - col.setLabelProvider(new ColumnLabelProvider() { - private static final long serialVersionUID = -6009599998150286070L; - - public String getText(Object element) { - return JcrBrowserUtils.getPropertyTypeAsString((Property) element); - } - }); - propertiesViewer.setInput(""); - return propertiesViewer; - } - - protected TreeViewer getNodeViewer() { - return nodesViewer; - } - - /** - * Resets the tree content provider - * - * @param sortChildNodes if true the content provider will use a comparer to - * sort nodes that might slow down the display - */ - public void setSortChildNodes(boolean sortChildNodes) { - this.sortChildNodes = sortChildNodes; - ((NodeContentProvider) nodesViewer.getContentProvider()).setSortChildren(sortChildNodes); - nodesViewer.setInput(""); - } - - /** Notifies the current view that a node has been added */ - public void nodeAdded(TreeParent parentNode) { - // insure that Ui objects have been correctly created: - JcrBrowserUtils.forceRefreshIfNeeded(parentNode); - getNodeViewer().refresh(parentNode); - getNodeViewer().expandToLevel(parentNode, 1); - } - - /** Notifies the current view that a node has been removed */ - public void nodeRemoved(TreeParent parentNode) { - IStructuredSelection newSel = new StructuredSelection(parentNode); - getNodeViewer().setSelection(newSel, true); - // Force refresh - IStructuredSelection tmpSel = (IStructuredSelection) getNodeViewer().getSelection(); - getNodeViewer().refresh(tmpSel.getFirstElement()); - } - - class TreeObserver extends AsyncUiEventListener { - - public TreeObserver(Display display) { - super(display); - } - - @Override - protected Boolean willProcessInUiThread(List events) throws RepositoryException { - for (Event event : events) { - if (getLog().isTraceEnabled()) - getLog().debug("Received event " + event); - String path = event.getPath(); - int index = path.lastIndexOf('/'); - String propertyName = path.substring(index + 1); - if (getLog().isTraceEnabled()) - getLog().debug("Concerned property " + propertyName); - } - return false; - } - - protected void onEventInUiThread(List events) throws RepositoryException { - if (getLog().isTraceEnabled()) - getLog().trace("Refresh result list"); - nodesViewer.refresh(); - } - - } - - public boolean getSortChildNodes() { - return sortChildNodes; - } - - public void setFocus() { - getNodeViewer().getTree().setFocus(); - } - - /* DEPENDENCY INJECTION */ - // public void setRepositoryRegister(RepositoryRegister repositoryRegister) { - // this.repositoryRegister = repositoryRegister; - // } - - public void setKeyring(CryptoKeyring keyring) { - this.keyring = keyring; - } - - public void setRepositoryFactory(RepositoryFactory repositoryFactory) { - this.repositoryFactory = repositoryFactory; - } - - public void setNodeRepository(Repository nodeRepository) { - this.nodeRepository = nodeRepository; - } -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/JcrE4DClickListener.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/JcrE4DClickListener.java deleted file mode 100644 index f4ee2e8..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/JcrE4DClickListener.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.argeo.cms.e4.jcr; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.ui.jcr.JcrDClickListener; -import org.eclipse.e4.ui.model.application.ui.basic.MPart; -import org.eclipse.e4.ui.workbench.modeling.EPartService; -import org.eclipse.e4.ui.workbench.modeling.EPartService.PartState; -import org.eclipse.jface.viewers.TreeViewer; - -public class JcrE4DClickListener extends JcrDClickListener { - EPartService partService; - - public JcrE4DClickListener(TreeViewer nodeViewer, EPartService partService) { - super(nodeViewer); - this.partService = partService; - } - - @Override - protected void openNode(Node node) { - MPart part = partService.createPart(JcrNodeEditor.DESCRIPTOR_ID); - try { - part.setLabel(node.getName()); - part.getPersistedState().put("nodeWorkspace", node.getSession().getWorkspace().getName()); - part.getPersistedState().put("nodePath", node.getPath()); - } catch (RepositoryException e) { - throw new CmsException("Cannot open " + node, e); - } - - // the provided part is be shown - partService.showPart(part, PartState.ACTIVATE); - } - -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/JcrNodeEditor.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/JcrNodeEditor.java deleted file mode 100644 index ae2b325..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/JcrNodeEditor.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.argeo.cms.e4.jcr; - -import java.util.List; - -import javax.annotation.PostConstruct; -import javax.jcr.Node; - -import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; -import org.eclipse.e4.ui.model.application.ui.basic.MPart; -import org.eclipse.e4.ui.workbench.modeling.ESelectionService; -import org.eclipse.swt.layout.FillLayout; -import org.eclipse.swt.widgets.Composite; - -public class JcrNodeEditor { - final static String DESCRIPTOR_ID = "org.argeo.cms.e4.partdescriptor.nodeEditor"; - - @PostConstruct - public void createUi(Composite parent, MPart part, ESelectionService selectionService) { - parent.setLayout(new FillLayout()); - List selection = (List) selectionService.getSelection(); - Node node = ((SingleJcrNodeElem) selection.get(0)).getNode(); - GenericPropertyPage propertyPage = new GenericPropertyPage(node); - propertyPage.createFormContent(parent); - } - -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/SimplePart.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/SimplePart.java deleted file mode 100644 index 17d8d2a..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/SimplePart.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.argeo.cms.e4.jcr; - -import javax.annotation.PostConstruct; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; - -public class SimplePart { - - @PostConstruct - void init(Composite parent) { - parent.setLayout(new GridLayout()); - Label label = new Label(parent, SWT.NONE); - label.setText("Hello e4 World"); - } - -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/AddFolderNode.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/AddFolderNode.java deleted file mode 100644 index 09fa760..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/AddFolderNode.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.argeo.cms.e4.jcr.handlers; - -import java.util.List; - -import javax.inject.Named; -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.nodetype.NodeType; - -import org.argeo.cms.e4.jcr.JcrBrowserView; -import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; -import org.argeo.cms.ui.jcr.model.WorkspaceElem; -import org.argeo.cms.ux.widgets.TreeParent; -import org.argeo.eclipse.ui.dialogs.ErrorFeedback; -import org.argeo.eclipse.ui.dialogs.SingleValue; -import org.eclipse.e4.core.di.annotations.Execute; -import org.eclipse.e4.ui.model.application.ui.basic.MPart; -import org.eclipse.e4.ui.services.IServiceConstants; -import org.eclipse.e4.ui.workbench.modeling.ESelectionService; - -/** - * Adds a node of type nt:folder, only on {@link SingleJcrNodeElem} and - * {@link WorkspaceElem} TreeObject types. - * - * This handler assumes that a selection provider is available and picks only - * first selected item. It is UI's job to enable the command only when the - * selection contains one and only one element. Thus no parameter is passed - * through the command. - */ -public class AddFolderNode { - @Execute - public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) { - List selection = (List) selectionService.getSelection(); - JcrBrowserView view = (JcrBrowserView) part.getObject(); - - if (selection != null && selection.size() == 1) { - TreeParent treeParentNode = null; - Node jcrParentNode = null; - Object obj = selection.get(0); - - if (obj instanceof SingleJcrNodeElem) { - treeParentNode = (TreeParent) obj; - jcrParentNode = ((SingleJcrNodeElem) treeParentNode).getNode(); - } else if (obj instanceof WorkspaceElem) { - treeParentNode = (TreeParent) obj; - jcrParentNode = ((WorkspaceElem) treeParentNode).getRootNode(); - } else - return; - - String folderName = SingleValue.ask("Folder name", "Enter folder name"); - if (folderName != null) { - try { - jcrParentNode.addNode(folderName, NodeType.NT_FOLDER); - jcrParentNode.getSession().save(); - view.nodeAdded(treeParentNode); - } catch (RepositoryException e) { - ErrorFeedback.show("Cannot create folder " + folderName + " under " + treeParentNode, e); - } - } - } else { - // ErrorFeedback.show(WorkbenchUiPlugin - // .getMessage("errorUnvalidNtFolderNodeType")); - ErrorFeedback.show("Invalid NT folder node type"); - } - } - -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/AddRemoteRepository.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/AddRemoteRepository.java deleted file mode 100644 index dc47f6e..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/AddRemoteRepository.java +++ /dev/null @@ -1,210 +0,0 @@ -package org.argeo.cms.e4.jcr.handlers; - -import java.net.URI; -import java.util.Hashtable; - -import javax.inject.Inject; -import javax.inject.Named; -import javax.jcr.Node; -import javax.jcr.Repository; -import javax.jcr.RepositoryFactory; -import javax.jcr.Session; -import javax.jcr.SimpleCredentials; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.cms.ArgeoNames; -import org.argeo.cms.ArgeoTypes; -import org.argeo.cms.e4.jcr.JcrBrowserView; -import org.argeo.cms.jcr.CmsJcrUtils; -import org.argeo.cms.security.Keyring; -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.eclipse.ui.dialogs.ErrorFeedback; -import org.argeo.jcr.JcrUtils; -import org.eclipse.e4.core.di.annotations.Execute; -import org.eclipse.e4.core.di.annotations.Optional; -import org.eclipse.e4.ui.model.application.ui.basic.MPart; -import org.eclipse.e4.ui.services.IServiceConstants; -import org.eclipse.jface.dialogs.Dialog; -import org.eclipse.jface.dialogs.IMessageProvider; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.dialogs.TitleAreaDialog; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Text; - -/** - * Connect to a remote repository and, if successful publish it as an OSGi - * service. - */ -public class AddRemoteRepository { - - @Inject - private RepositoryFactory repositoryFactory; - @Inject - private Repository nodeRepository; - @Inject - @Optional - private Keyring keyring; - - @Execute - public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part) { - JcrBrowserView view = (JcrBrowserView) part.getObject(); - RemoteRepositoryLoginDialog dlg = new RemoteRepositoryLoginDialog(Display.getDefault().getActiveShell()); - if (dlg.open() == Dialog.OK) { - view.refresh(null); - } - } - - // public void setRepositoryFactory(RepositoryFactory repositoryFactory) { - // this.repositoryFactory = repositoryFactory; - // } - // - // public void setKeyring(Keyring keyring) { - // this.keyring = keyring; - // } - // - // public void setNodeRepository(Repository nodeRepository) { - // this.nodeRepository = nodeRepository; - // } - - class RemoteRepositoryLoginDialog extends TitleAreaDialog { - private static final long serialVersionUID = 2234006887750103399L; - private Text name; - private Text uri; - private Text username; - private Text password; - private Button saveInKeyring; - - public RemoteRepositoryLoginDialog(Shell parentShell) { - super(parentShell); - } - - protected Point getInitialSize() { - return new Point(600, 400); - } - - protected Control createDialogArea(Composite parent) { - Composite dialogarea = (Composite) super.createDialogArea(parent); - dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - Composite composite = new Composite(dialogarea, SWT.NONE); - composite.setLayout(new GridLayout(2, false)); - composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); - setMessage("Login to remote repository", IMessageProvider.NONE); - name = createLT(composite, "Name", "remoteRepository"); - uri = createLT(composite, "URI", "http://localhost:7070/jcr/node"); - username = createLT(composite, "User", ""); - password = createLP(composite, "Password"); - - saveInKeyring = createLC(composite, "Remember password", false); - parent.pack(); - return composite; - } - - @Override - protected void createButtonsForButtonBar(Composite parent) { - super.createButtonsForButtonBar(parent); - Button test = createButton(parent, 2, "Test", false); - test.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = -1829962269440419560L; - - public void widgetSelected(SelectionEvent arg0) { - testConnection(); - } - }); - } - - void testConnection() { - Session session = null; - try { - URI checkedUri = new URI(uri.getText()); - String checkedUriStr = checkedUri.toString(); - - Hashtable params = new Hashtable(); - params.put(CmsConstants.LABELED_URI, checkedUriStr); - Repository repository = repositoryFactory.getRepository(params); - if (username.getText().trim().equals("")) {// anonymous - // FIXME make it more generic - session = repository.login(CmsConstants.SYS_WORKSPACE); - } else { - // FIXME use getTextChars() when upgrading to 3.7 - // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=297412 - char[] pwd = password.getText().toCharArray(); - SimpleCredentials sc = new SimpleCredentials(username.getText(), pwd); - session = repository.login(sc, "main"); - MessageDialog.openInformation(getParentShell(), "Success", - "Connection to '" + uri.getText() + "' successful"); - } - } catch (Exception e) { - ErrorFeedback.show("Connection test failed for " + uri.getText(), e); - } finally { - JcrUtils.logoutQuietly(session); - } - } - - @Override - protected void okPressed() { - Session nodeSession = null; - try { - nodeSession = nodeRepository.login(); - Node home = CmsJcrUtils.getUserHome(nodeSession); - - Node remote = home.hasNode(ArgeoNames.ARGEO_REMOTE) ? home.getNode(ArgeoNames.ARGEO_REMOTE) - : home.addNode(ArgeoNames.ARGEO_REMOTE); - if (remote.hasNode(name.getText())) - throw new EclipseUiException("There is already a remote repository named " + name.getText()); - Node remoteRepository = remote.addNode(name.getText(), ArgeoTypes.ARGEO_REMOTE_REPOSITORY); - remoteRepository.setProperty(ArgeoNames.ARGEO_URI, uri.getText()); - remoteRepository.setProperty(ArgeoNames.ARGEO_USER_ID, username.getText()); - nodeSession.save(); - if (saveInKeyring.getSelection()) { - String pwdPath = remoteRepository.getPath() + '/' + ArgeoNames.ARGEO_PASSWORD; - keyring.set(pwdPath, password.getText().toCharArray()); - } - nodeSession.save(); - MessageDialog.openInformation(getParentShell(), "Repository Added", - "Remote repository '" + username.getText() + "@" + uri.getText() + "' added"); - - super.okPressed(); - } catch (Exception e) { - ErrorFeedback.show("Cannot add remote repository", e); - } finally { - JcrUtils.logoutQuietly(nodeSession); - } - } - - /** Creates label and text. */ - protected Text createLT(Composite parent, String label, String initial) { - new Label(parent, SWT.NONE).setText(label); - Text text = new Text(parent, SWT.SINGLE | SWT.LEAD | SWT.BORDER); - text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - text.setText(initial); - return text; - } - - /** Creates label and check. */ - protected Button createLC(Composite parent, String label, Boolean initial) { - new Label(parent, SWT.NONE).setText(label); - Button check = new Button(parent, SWT.CHECK); - check.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - check.setSelection(initial); - return check; - } - - protected Text createLP(Composite parent, String label) { - new Label(parent, SWT.NONE).setText(label); - Text text = new Text(parent, SWT.SINGLE | SWT.LEAD | SWT.BORDER | SWT.PASSWORD); - text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - return text; - } - } -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/DeleteNodes.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/DeleteNodes.java deleted file mode 100644 index b8de06b..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/DeleteNodes.java +++ /dev/null @@ -1,95 +0,0 @@ -package org.argeo.cms.e4.jcr.handlers; - -import java.util.List; - -import javax.inject.Named; -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.e4.jcr.JcrBrowserView; -import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; -import org.argeo.cms.ui.jcr.model.WorkspaceElem; -import org.argeo.cms.ux.widgets.TreeParent; -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.eclipse.ui.dialogs.ErrorFeedback; -import org.eclipse.e4.core.di.annotations.Execute; -import org.eclipse.e4.ui.model.application.ui.basic.MPart; -import org.eclipse.e4.ui.services.IServiceConstants; -import org.eclipse.e4.ui.workbench.modeling.ESelectionService; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.swt.widgets.Display; - -/** - * Delete the selected nodes: both in the JCR repository and in the UI view. - * Warning no check is done, except implementation dependent native checks, - * handle with care. - * - * This handler is still 'hard linked' to a GenericJcrBrowser view to enable - * correct tree refresh when a node is added. This must be corrected in future - * versions. - */ -public class DeleteNodes { - @Execute - public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) { - List selection = (List) selectionService.getSelection(); - if (selection == null) - return; - - JcrBrowserView view = (JcrBrowserView) part.getObject(); - - // confirmation - StringBuffer buf = new StringBuffer(""); - for (Object o : selection) { - SingleJcrNodeElem sjn = (SingleJcrNodeElem) o; - buf.append(sjn.getName()).append(' '); - } - Boolean doRemove = MessageDialog.openConfirm(Display.getCurrent().getActiveShell(), "Confirm deletion", - "Do you want to delete " + buf + "?"); - - // operation - if (doRemove) { - SingleJcrNodeElem ancestor = null; - WorkspaceElem rootAncestor = null; - try { - for (Object obj : selection) { - if (obj instanceof SingleJcrNodeElem) { - // Cache objects - SingleJcrNodeElem sjn = (SingleJcrNodeElem) obj; - TreeParent tp = (TreeParent) sjn.getParent(); - Node node = sjn.getNode(); - - // Jcr Remove - node.remove(); - node.getSession().save(); - // UI remove - tp.removeChild(sjn); - - // Check if the parent is the root node - if (tp instanceof WorkspaceElem) - rootAncestor = (WorkspaceElem) tp; - else - ancestor = getOlder(ancestor, (SingleJcrNodeElem) tp); - } - } - if (rootAncestor != null) - view.nodeRemoved(rootAncestor); - else if (ancestor != null) - view.nodeRemoved(ancestor); - } catch (Exception e) { - ErrorFeedback.show("Cannot delete selected node ", e); - } - } - } - - private SingleJcrNodeElem getOlder(SingleJcrNodeElem A, SingleJcrNodeElem B) { - try { - if (A == null) - return B == null ? null : B; - // Todo enhanced this method - else - return A.getNode().getDepth() <= B.getNode().getDepth() ? A : B; - } catch (RepositoryException re) { - throw new EclipseUiException("Cannot find ancestor", re); - } - } -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/Refresh.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/Refresh.java deleted file mode 100644 index 036e70a..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/Refresh.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.argeo.cms.e4.jcr.handlers; - -import java.util.List; - -import javax.inject.Named; - -import org.argeo.cms.e4.jcr.JcrBrowserView; -import org.argeo.cms.ui.jcr.JcrBrowserUtils; -import org.argeo.cms.ux.widgets.TreeParent; -import org.eclipse.e4.core.di.annotations.Execute; -import org.eclipse.e4.ui.model.application.ui.basic.MPart; -import org.eclipse.e4.ui.services.IServiceConstants; -import org.eclipse.e4.ui.workbench.modeling.EPartService; -import org.eclipse.e4.ui.workbench.modeling.ESelectionService; - -/** - * Force the selected objects of the active view to be refreshed doing the - * following: - *
    - *
  1. The model objects are recomputed
  2. - *
  3. the view is refreshed
  4. - *
- */ -public class Refresh { - - @Execute - public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, EPartService partService, - ESelectionService selectionService) { - - JcrBrowserView view = (JcrBrowserView) part.getObject(); - List selection = (List) selectionService.getSelection(); - - if (selection != null && !selection.isEmpty()) { - for (Object obj : selection) - if (obj instanceof TreeParent) { - TreeParent tp = (TreeParent) obj; - JcrBrowserUtils.forceRefreshIfNeeded(tp); - view.refresh(obj); - } - } else if (view instanceof JcrBrowserView) - view.refresh(null); // force full refresh - } -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/RenameNode.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/RenameNode.java deleted file mode 100644 index 97674ab..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/RenameNode.java +++ /dev/null @@ -1,59 +0,0 @@ -package org.argeo.cms.e4.jcr.handlers; - -import java.util.List; - -import javax.inject.Named; -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.Session; - -import org.argeo.cms.e4.jcr.JcrBrowserView; -import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.eclipse.ui.dialogs.SingleValue; -import org.argeo.jcr.JcrUtils; -import org.eclipse.e4.core.di.annotations.Execute; -import org.eclipse.e4.ui.model.application.ui.basic.MPart; -import org.eclipse.e4.ui.services.IServiceConstants; -import org.eclipse.e4.ui.workbench.modeling.EPartService; -import org.eclipse.e4.ui.workbench.modeling.ESelectionService; - -/** - * Canonically call JCR Session#move(String, String) on the first element - * returned by HandlerUtil#getActiveWorkbenchWindow() - * (...getActivePage().getSelection()), if it is a {@link SingleJcrNodeElem}. - * The user must then fill a new name in and confirm - */ -public class RenameNode { - @Execute - public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, EPartService partService, - ESelectionService selectionService) { - List selection = (List) selectionService.getSelection(); - if (selection == null || selection.size() != 1) - return; - JcrBrowserView view = (JcrBrowserView) part.getObject(); - - Object element = selection.get(0); - if (element instanceof SingleJcrNodeElem) { - SingleJcrNodeElem sjn = (SingleJcrNodeElem) element; - Node node = sjn.getNode(); - Session session = null; - String newName = null; - String oldPath = null; - try { - newName = SingleValue.ask("New node name", "Please provide a new name for [" + node.getName() + "]"); - // TODO sanity check and user feedback - newName = JcrUtils.replaceInvalidChars(newName); - oldPath = node.getPath(); - session = node.getSession(); - session.move(oldPath, JcrUtils.parentPath(oldPath) + "/" + newName); - session.save(); - - // Manually refresh the browser view. Must be enhanced - view.refresh(sjn); - } catch (RepositoryException e) { - throw new EclipseUiException("Unable to rename " + node + " to " + newName, e); - } - } - } -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/package-info.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/package-info.java deleted file mode 100644 index 4e075e2..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** JCR browser handlers. */ -package org.argeo.cms.e4.jcr.handlers; \ No newline at end of file diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/package-info.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/package-info.java deleted file mode 100644 index 3e92fb0..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** JCR browser perspective. */ -package org.argeo.cms.e4.jcr; \ No newline at end of file diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/AbstractRoleEditor.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/AbstractRoleEditor.java deleted file mode 100644 index 137f762..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/AbstractRoleEditor.java +++ /dev/null @@ -1,287 +0,0 @@ -package org.argeo.cms.e4.users; - -import java.util.ArrayList; -import java.util.List; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import javax.inject.Inject; - -import org.argeo.cms.auth.UserAdminUtils; -import org.argeo.cms.ui.eclipse.forms.AbstractFormPart; -import org.argeo.cms.ui.eclipse.forms.IManagedForm; -import org.argeo.cms.ui.eclipse.forms.ManagedForm; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.util.naming.LdapAttrs; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.e4.ui.di.Persist; -import org.eclipse.e4.ui.model.application.ui.basic.MPart; -import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.ScrolledComposite; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; -import org.osgi.service.useradmin.Authorization; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdmin; -import org.osgi.service.useradmin.UserAdminEvent; - -/** Editor for a user, might be a user or a group. */ -public abstract class AbstractRoleEditor { - - // public final static String USER_EDITOR_ID = WorkbenchUiPlugin.PLUGIN_ID + - // ".userEditor"; - // public final static String GROUP_EDITOR_ID = WorkbenchUiPlugin.PLUGIN_ID + - // ".groupEditor"; - - /* DEPENDENCY INJECTION */ - @Inject - protected UserAdminWrapper userAdminWrapper; - - @Inject - private MPart mPart; - - // @Inject - // Composite parent; - - private UserAdmin userAdmin; - - // Context - private User user; - private String username; - - private NameChangeListener listener; - - private ManagedForm managedForm; - - // public void init(IEditorSite site, IEditorInput input) throws - // PartInitException { - @PostConstruct - public void init(Composite parent) { - this.userAdmin = userAdminWrapper.getUserAdmin(); - username = mPart.getPersistedState().get(LdapAttrs.uid.name()); - user = (User) userAdmin.getRole(username); - - listener = new NameChangeListener(Display.getCurrent()); - userAdminWrapper.addListener(listener); - updateEditorTitle(null); - - managedForm = new ManagedForm(parent) { - - @Override - public void staleStateChanged() { - refresh(); - } - }; - ScrolledComposite scrolled = managedForm.getForm(); - Composite body = new Composite(scrolled, SWT.NONE); - scrolled.setContent(body); - createUi(body); - managedForm.refresh(); - } - - abstract void createUi(Composite parent); - - /** - * returns the list of all authorizations for the given user or of the current - * displayed user if parameter is null - */ - protected List getFlatGroups(User aUser) { - Authorization currAuth; - if (aUser == null) - currAuth = userAdmin.getAuthorization(this.user); - else - currAuth = userAdmin.getAuthorization(aUser); - - String[] roles = currAuth.getRoles(); - - List groups = new ArrayList(); - for (String roleStr : roles) { - User currRole = (User) userAdmin.getRole(roleStr); - if (currRole != null && !groups.contains(currRole)) - groups.add(currRole); - } - return groups; - } - - protected IManagedForm getManagedForm() { - return managedForm; - } - - /** Exposes the user (or group) that is displayed by the current editor */ - protected User getDisplayedUser() { - return user; - } - - private void setDisplayedUser(User user) { - this.user = user; - } - - void updateEditorTitle(String title) { - if (title == null) { - String commonName = UserAdminUtils.getProperty(user, LdapAttrs.cn.name()); - title = "".equals(commonName) ? user.getName() : commonName; - } - setPartName(title); - } - - protected void setPartName(String name) { - mPart.setLabel(name); - } - - // protected void addPages() { - // try { - // if (user.getType() == Role.GROUP) - // addPage(new GroupMainPage(this, userAdminWrapper, repository, nodeInstance)); - // else - // addPage(new UserMainPage(this, userAdminWrapper)); - // } catch (Exception e) { - // throw new CmsException("Cannot add pages", e); - // } - // } - - @Persist - public void doSave(IProgressMonitor monitor) { - userAdminWrapper.beginTransactionIfNeeded(); - commitPages(true); - userAdminWrapper.commitOrNotifyTransactionStateChange(); - // firePropertyChange(PROP_DIRTY); - userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_REMOVED, user)); - } - - protected void commitPages(boolean b) { - managedForm.commit(b); - } - - @PreDestroy - public void dispose() { - userAdminWrapper.removeListener(listener); - managedForm.dispose(); - } - - // CONTROLERS FOR THIS EDITOR AND ITS PAGES - - class NameChangeListener extends UiUserAdminListener { - public NameChangeListener(Display display) { - super(display); - } - - @Override - public void roleChangedToUiThread(UserAdminEvent event) { - Role changedRole = event.getRole(); - if (changedRole == null || changedRole.equals(user)) { - updateEditorTitle(null); - User reloadedUser = (User) userAdminWrapper.getUserAdmin().getRole(user.getName()); - setDisplayedUser(reloadedUser); - } - } - } - - class MainInfoListener extends UiUserAdminListener { - private final AbstractFormPart part; - - public MainInfoListener(Display display, AbstractFormPart part) { - super(display); - this.part = part; - } - - @Override - public void roleChangedToUiThread(UserAdminEvent event) { - // Rollback - if (event.getRole() == null) - part.markStale(); - } - } - - class GroupChangeListener extends UiUserAdminListener { - private final AbstractFormPart part; - - public GroupChangeListener(Display display, AbstractFormPart part) { - super(display); - this.part = part; - } - - @Override - public void roleChangedToUiThread(UserAdminEvent event) { - // always mark as stale - part.markStale(); - } - } - - /** Registers a listener that will notify this part */ - class FormPartML implements ModifyListener { - private static final long serialVersionUID = 6299808129505381333L; - private AbstractFormPart formPart; - - public FormPartML(AbstractFormPart generalPart) { - this.formPart = generalPart; - } - - public void modifyText(ModifyEvent e) { - // Discard event when the control does not have the focus, typically - // to avoid all editors being marked as dirty during a Rollback - if (((Control) e.widget).isFocusControl()) - formPart.markDirty(); - } - } - - /* DEPENDENCY INJECTION */ - public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { - this.userAdminWrapper = userAdminWrapper; - } - - /** Creates label and multiline text. */ - Text createLMT(Composite parent, String label, String value) { - Label lbl = new Label(parent, SWT.NONE); - lbl.setText(label); - lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false)); - Text text = new Text(parent, SWT.NONE); - text.setText(value); - text.setLayoutData(new GridData(SWT.LEAD, SWT.FILL, true, true)); - return text; - } - - /** Creates label and password. */ - Text createLP(Composite parent, String label, String value) { - Label lbl = new Label(parent, SWT.NONE); - lbl.setText(label); - lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false)); - Text text = new Text(parent, SWT.PASSWORD | SWT.BORDER); - text.setText(value); - text.setLayoutData(new GridData(SWT.LEAD, SWT.FILL, true, false)); - return text; - } - - /** Creates label and text. */ - Text createLT(Composite parent, String label, String value) { - Label lbl = new Label(parent, SWT.NONE); - lbl.setText(label); - lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false)); - lbl.setFont(EclipseUiUtils.getBoldFont(parent)); - Text text = new Text(parent, SWT.BORDER); - text.setText(value); - text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); - // CmsUiUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT); - return text; - } - - Text createReadOnlyLT(Composite parent, String label, String value) { - Label lbl = new Label(parent, SWT.NONE); - lbl.setText(label); - lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false)); - lbl.setFont(EclipseUiUtils.getBoldFont(parent)); - Text text = new Text(parent, SWT.NONE); - text.setText(value); - text.setLayoutData(new GridData(SWT.LEAD, SWT.FILL, true, false)); - text.setEditable(false); - // CmsUiUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT); - return text; - } - -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/CmsWorkbenchStyles.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/CmsWorkbenchStyles.java deleted file mode 100644 index 07df312..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/CmsWorkbenchStyles.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.argeo.cms.e4.users; - -/** Centralize the declaration of Workbench specific CSS Styles */ -interface CmsWorkbenchStyles { - - // Specific People layouting - String WORKBENCH_FORM_TEXT = "workbench_form_text"; -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/GroupEditor.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/GroupEditor.java deleted file mode 100644 index d54f8bc..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/GroupEditor.java +++ /dev/null @@ -1,566 +0,0 @@ -package org.argeo.cms.e4.users; - -import static org.argeo.api.cms.CmsContext.WORKGROUP; -import static org.argeo.cms.auth.UserAdminUtils.setProperty; -import static org.argeo.util.naming.LdapAttrs.businessCategory; -import static org.argeo.util.naming.LdapAttrs.description; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import javax.annotation.PreDestroy; -import javax.inject.Inject; -import javax.jcr.Node; -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.jcr.Session; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.api.cms.CmsContext; -import org.argeo.cms.auth.UserAdminUtils; -import org.argeo.cms.e4.users.providers.CommonNameLP; -import org.argeo.cms.e4.users.providers.MailLP; -import org.argeo.cms.e4.users.providers.RoleIconLP; -import org.argeo.cms.e4.users.providers.UserFilter; -import org.argeo.cms.jcr.CmsJcrUtils; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ui.eclipse.forms.AbstractFormPart; -import org.argeo.cms.ui.eclipse.forms.IManagedForm; -import org.argeo.eclipse.ui.ColumnDefinition; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.parts.LdifUsersTable; -import org.argeo.jcr.JcrException; -import org.argeo.jcr.JcrUtils; -import org.argeo.util.naming.LdapAttrs; -import org.argeo.util.transaction.WorkTransaction; -import org.eclipse.e4.ui.workbench.modeling.EPartService; -import org.eclipse.jface.action.Action; -import org.eclipse.jface.action.ToolBarManager; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.jface.viewers.ViewerDropAdapter; -import org.eclipse.swt.SWT; -import org.eclipse.swt.dnd.DND; -import org.eclipse.swt.dnd.DropTargetEvent; -import org.eclipse.swt.dnd.TextTransfer; -import org.eclipse.swt.dnd.Transfer; -import org.eclipse.swt.dnd.TransferData; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Link; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Text; -import org.eclipse.swt.widgets.ToolBar; -import org.osgi.service.useradmin.Group; -import org.osgi.service.useradmin.Role; -//import org.eclipse.ui.forms.AbstractFormPart; -//import org.eclipse.ui.forms.IManagedForm; -//import org.eclipse.ui.forms.SectionPart; -//import org.eclipse.ui.forms.editor.FormEditor; -//import org.eclipse.ui.forms.editor.FormPage; -//import org.eclipse.ui.forms.widgets.FormToolkit; -//import org.eclipse.ui.forms.widgets.ScrolledForm; -//import org.eclipse.ui.forms.widgets.Section; -import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdmin; -import org.osgi.service.useradmin.UserAdminEvent; - -/** Display/edit main properties of a given group */ -public class GroupEditor extends AbstractRoleEditor { - // final static String ID = "GroupEditor.mainPage"; - - @Inject - private EPartService partService; - - // private final UserEditor editor; - @Inject - private Repository repository; - @Inject - private CmsContext nodeInstance; - // private final UserAdminWrapper userAdminWrapper; - private Session groupsSession; - - // public GroupMainPage(FormEditor editor, UserAdminWrapper userAdminWrapper, - // Repository repository, - // NodeInstance nodeInstance) { - // super(editor, ID, "Main"); - // try { - // session = repository.login(); - // } catch (RepositoryException e) { - // throw new CmsException("Cannot retrieve session of in MainGroupPage - // constructor", e); - // } - // this.editor = (UserEditor) editor; - // this.userAdminWrapper = userAdminWrapper; - // this.nodeInstance = nodeInstance; - // } - - // protected void createFormContent(final IManagedForm mf) { - // ScrolledForm form = mf.getForm(); - // Composite body = form.getBody(); - // GridLayout mainLayout = new GridLayout(); - // body.setLayout(mainLayout); - // Group group = (Group) editor.getDisplayedUser(); - // appendOverviewPart(body, group); - // appendMembersPart(body, group); - // } - - @Override - protected void createUi(Composite parent) { - try { - groupsSession = repository.login(CmsConstants.SRV_WORKSPACE); - } catch (RepositoryException e) { - throw new JcrException("Cannot retrieve session", e); - } - // ScrolledForm form = mf.getForm(); - // Composite body = form.getBody(); - // Composite body = new Composite(parent, SWT.NONE); - Composite body = parent; - GridLayout mainLayout = new GridLayout(); - body.setLayout(mainLayout); - Group group = (Group) getDisplayedUser(); - appendOverviewPart(body, group); - appendMembersPart(body, group); - } - - @PreDestroy - public void dispose() { - JcrUtils.logoutQuietly(groupsSession); - super.dispose(); - } - - /** Creates the general section */ - protected void appendOverviewPart(final Composite parent, final Group group) { - Composite body = new Composite(parent, SWT.NONE); - // GridLayout layout = new GridLayout(5, false); - GridLayout layout = new GridLayout(2, false); - body.setLayout(layout); - body.setLayoutData(CmsSwtUtils.fillWidth()); - - String cn = UserAdminUtils.getProperty(group, LdapAttrs.cn.name()); - createReadOnlyLT(body, "Name", cn); - createReadOnlyLT(body, "DN", group.getName()); - createReadOnlyLT(body, "Domain", UserAdminUtils.getDomainName(group)); - - // Description - Label descLbl = new Label(body, SWT.LEAD); - descLbl.setFont(EclipseUiUtils.getBoldFont(body)); - descLbl.setText("Description"); - descLbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, true, false, 2, 1)); - final Text descTxt = new Text(body, SWT.LEAD | SWT.MULTI | SWT.WRAP | SWT.BORDER); - GridData gd = EclipseUiUtils.fillWidth(); - gd.heightHint = 50; - gd.horizontalSpan = 2; - descTxt.setLayoutData(gd); - - // Mark as workgroup - Link markAsWorkgroupLk = new Link(body, SWT.NONE); - markAsWorkgroupLk.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1)); - - // create form part (controller) - final AbstractFormPart part = new AbstractFormPart() { - - private MainInfoListener listener; - - @Override - public void initialize(IManagedForm form) { - super.initialize(form); - listener = new MainInfoListener(parent.getDisplay(), this); - userAdminWrapper.addListener(listener); - } - - @Override - public void dispose() { - userAdminWrapper.removeListener(listener); - super.dispose(); - } - - public void commit(boolean onSave) { - // group.getProperties().put(LdapAttrs.description.name(), descTxt.getText()); - setProperty(group, description, descTxt.getText()); - super.commit(onSave); - } - - @Override - public void refresh() { - // dnTxt.setText(group.getName()); - // cnTxt.setText(UserAdminUtils.getProperty(group, LdapAttrs.cn.name())); - descTxt.setText(UserAdminUtils.getProperty(group, LdapAttrs.description.name())); - Node workgroupHome = CmsJcrUtils.getGroupHome(groupsSession, cn); - if (workgroupHome == null) - markAsWorkgroupLk.setText("Mark as workgroup"); - else - markAsWorkgroupLk.setText("Configured as workgroup"); - parent.layout(true, true); - super.refresh(); - } - }; - - markAsWorkgroupLk.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = -6439340898096365078L; - - @Override - public void widgetSelected(SelectionEvent e) { - - boolean confirmed = MessageDialog.openConfirm(parent.getShell(), "Mark as workgroup", - "Are you sure you want to mark " + cn + " as being a workgroup? "); - if (confirmed) { - Node workgroupHome = CmsJcrUtils.getGroupHome(groupsSession, cn); - if (workgroupHome != null) - return; // already marked as workgroup, do nothing - else { - // improve transaction management - userAdminWrapper.beginTransactionIfNeeded(); - nodeInstance.createWorkgroup(group.getName()); - setProperty(group, businessCategory, WORKGROUP); - userAdminWrapper.commitOrNotifyTransactionStateChange(); - userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, group)); - part.refresh(); - } - } - } - }); - - ModifyListener defaultListener = new FormPartML(part); - descTxt.addModifyListener(defaultListener); - getManagedForm().addPart(part); - } - - /** Filtered table with members. Has drag and drop ability */ - protected void appendMembersPart(Composite parent, Group group) { - // Section section = tk.createSection(parent, Section.TITLE_BAR); - // section.setText("Members"); - // section.setLayoutData(EclipseUiUtils.fillAll()); - - Composite body = new Composite(parent, SWT.BORDER); - body.setLayout(new GridLayout()); - // section.setClient(body); - body.setLayoutData(EclipseUiUtils.fillAll()); - - // Define the displayed columns - List columnDefs = new ArrayList(); - columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 0, 24)); - columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Name", 150)); - columnDefs.add(new ColumnDefinition(new MailLP(), "Mail", 150)); - // columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", - // 240)); - - // Create and configure the table - LdifUsersTable userViewerCmp = new MyUserTableViewer(body, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL, - userAdminWrapper.getUserAdmin()); - - userViewerCmp.setColumnDefinitions(columnDefs); - userViewerCmp.populate(true, false); - userViewerCmp.setLayoutData(EclipseUiUtils.fillAll()); - - // Controllers - TableViewer userViewer = userViewerCmp.getTableViewer(); - userViewer.addDoubleClickListener(new UserTableDefaultDClickListener(partService)); - int operations = DND.DROP_COPY | DND.DROP_MOVE; - Transfer[] tt = new Transfer[] { TextTransfer.getInstance() }; - userViewer.addDropSupport(operations, tt, - new GroupDropListener(userAdminWrapper, userViewerCmp, (Group) getDisplayedUser())); - - AbstractFormPart part = new GroupMembersPart(userViewerCmp); - getManagedForm().addPart(part); - - // remove button - // addRemoveAbility(toolBarManager, userViewerCmp.getTableViewer(), group); - Action action = new RemoveMembershipAction(userViewer, group, "Remove selected items from this group", - SecurityAdminImages.ICON_REMOVE_DESC); - - ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT); - ToolBar toolBar = toolBarManager.createControl(body); - toolBar.setLayoutData(CmsSwtUtils.fillWidth()); - - toolBarManager.add(action); - toolBarManager.update(true); - - } - - // private LdifUsersTable createMemberPart(Composite parent, Group group) { - // - // // Define the displayed columns - // List columnDefs = new ArrayList(); - // columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 0, 24)); - // columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Name", 150)); - // columnDefs.add(new ColumnDefinition(new MailLP(), "Mail", 150)); - // // columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished - // Name", - // // 240)); - // - // // Create and configure the table - // LdifUsersTable userViewerCmp = new MyUserTableViewer(parent, SWT.MULTI | - // SWT.H_SCROLL | SWT.V_SCROLL, - // userAdminWrapper.getUserAdmin()); - // - // userViewerCmp.setColumnDefinitions(columnDefs); - // userViewerCmp.populate(true, false); - // userViewerCmp.setLayoutData(EclipseUiUtils.fillAll()); - // - // // Controllers - // TableViewer userViewer = userViewerCmp.getTableViewer(); - // userViewer.addDoubleClickListener(new - // UserTableDefaultDClickListener(partService)); - // int operations = DND.DROP_COPY | DND.DROP_MOVE; - // Transfer[] tt = new Transfer[] { TextTransfer.getInstance() }; - // userViewer.addDropSupport(operations, tt, - // new GroupDropListener(userAdminWrapper, userViewerCmp, (Group) - // getDisplayedUser())); - // - // // userViewerCmp.refresh(); - // return userViewerCmp; - // } - - // Local viewers - private class MyUserTableViewer extends LdifUsersTable { - private static final long serialVersionUID = 8467999509931900367L; - - private final UserFilter userFilter; - - public MyUserTableViewer(Composite parent, int style, UserAdmin userAdmin) { - super(parent, style, true); - userFilter = new UserFilter(); - - } - - @Override - protected List listFilteredElements(String filter) { - // reload user and set it in the editor - Group group = (Group) getDisplayedUser(); - Role[] roles = group.getMembers(); - List users = new ArrayList(); - userFilter.setSearchText(filter); - // userFilter.setShowSystemRole(true); - for (Role role : roles) - // if (role.getType() == Role.GROUP) - if (userFilter.select(null, null, role)) - users.add((User) role); - return users; - } - } - - // private void addRemoveAbility(ToolBarManager toolBarManager, TableViewer - // userViewer, Group group) { - // // Section section = sectionPart.getSection(); - // // ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT); - // // ToolBar toolbar = toolBarManager.createControl(parent); - // // ToolBar toolbar = toolBarManager.getControl(); - // // final Cursor handCursor = new Cursor(toolbar.getDisplay(), - // SWT.CURSOR_HAND); - // // toolbar.setCursor(handCursor); - // // toolbar.addDisposeListener(new DisposeListener() { - // // private static final long serialVersionUID = 3882131405820522925L; - // // - // // public void widgetDisposed(DisposeEvent e) { - // // if ((handCursor != null) && (handCursor.isDisposed() == false)) { - // // handCursor.dispose(); - // // } - // // } - // // }); - // - // Action action = new RemoveMembershipAction(userViewer, group, "Remove - // selected items from this group", - // SecurityAdminImages.ICON_REMOVE_DESC); - // toolBarManager.add(action); - // toolBarManager.update(true); - // // section.setTextClient(toolbar); - // } - - private class RemoveMembershipAction extends Action { - private static final long serialVersionUID = -1337713097184522588L; - - private final TableViewer userViewer; - private final Group group; - - RemoveMembershipAction(TableViewer userViewer, Group group, String name, ImageDescriptor img) { - super(name, img); - this.userViewer = userViewer; - this.group = group; - } - - @Override - public void run() { - ISelection selection = userViewer.getSelection(); - if (selection.isEmpty()) - return; - - @SuppressWarnings("unchecked") - Iterator it = ((IStructuredSelection) selection).iterator(); - List users = new ArrayList(); - while (it.hasNext()) { - User currUser = it.next(); - users.add(currUser); - } - - userAdminWrapper.beginTransactionIfNeeded(); - for (User user : users) { - group.removeMember(user); - } - userAdminWrapper.commitOrNotifyTransactionStateChange(); - userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, group)); - } - } - - // LOCAL CONTROLLERS - private class GroupMembersPart extends AbstractFormPart { - private final LdifUsersTable userViewer; - // private final Group group; - - private GroupChangeListener listener; - - public GroupMembersPart(LdifUsersTable userViewer) { - // super(section); - this.userViewer = userViewer; - // this.group = group; - } - - @Override - public void initialize(IManagedForm form) { - super.initialize(form); - listener = new GroupChangeListener(userViewer.getDisplay(), GroupMembersPart.this); - userAdminWrapper.addListener(listener); - } - - @Override - public void dispose() { - userAdminWrapper.removeListener(listener); - super.dispose(); - } - - @Override - public void refresh() { - userViewer.refresh(); - super.refresh(); - } - } - - /** - * Defines this table as being a potential target to add group membership - * (roles) to this group - */ - private class GroupDropListener extends ViewerDropAdapter { - private static final long serialVersionUID = 2893468717831451621L; - - private final UserAdminWrapper userAdminWrapper; - // private final LdifUsersTable myUserViewerCmp; - private final Group myGroup; - - public GroupDropListener(UserAdminWrapper userAdminWrapper, LdifUsersTable userTableViewerCmp, Group group) { - super(userTableViewerCmp.getTableViewer()); - this.userAdminWrapper = userAdminWrapper; - this.myGroup = group; - // this.myUserViewerCmp = userTableViewerCmp; - } - - @Override - public boolean validateDrop(Object target, int operation, TransferData transferType) { - // Target is always OK in a list only view - // TODO check if not a string - boolean validDrop = true; - return validDrop; - } - - @Override - public void drop(DropTargetEvent event) { - // TODO Is there an opportunity to perform the check before? - String newUserName = (String) event.data; - UserAdmin myUserAdmin = userAdminWrapper.getUserAdmin(); - Role role = myUserAdmin.getRole(newUserName); - if (role.getType() == Role.GROUP) { - Group newGroup = (Group) role; - Shell shell = getViewer().getControl().getShell(); - // Sanity checks - if (myGroup == newGroup) { // Equality - MessageDialog.openError(shell, "Forbidden addition ", "A group cannot be a member of itself."); - return; - } - - // Cycle - String myName = myGroup.getName(); - List myMemberships = getFlatGroups(myGroup); - if (myMemberships.contains(newGroup)) { - MessageDialog.openError(shell, "Forbidden addition: cycle", - "Cannot add " + newUserName + " to group " + myName + ". This would create a cycle"); - return; - } - - // Already member - List newGroupMemberships = getFlatGroups(newGroup); - if (newGroupMemberships.contains(myGroup)) { - MessageDialog.openError(shell, "Forbidden addition", - "Cannot add " + newUserName + " to group " + myName + ", this membership already exists"); - return; - } - userAdminWrapper.beginTransactionIfNeeded(); - myGroup.addMember(newGroup); - userAdminWrapper.commitOrNotifyTransactionStateChange(); - userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, myGroup)); - } else if (role.getType() == Role.USER) { - // TODO check if the group is already member of this group - WorkTransaction transaction = userAdminWrapper.beginTransactionIfNeeded(); - User user = (User) role; - myGroup.addMember(user); - if (UserAdminWrapper.COMMIT_ON_SAVE) - try { - transaction.commit(); - } catch (Exception e) { - throw new IllegalStateException( - "Cannot commit transaction " + "after user group membership update", e); - } - userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, myGroup)); - } - super.drop(event); - } - - @Override - public boolean performDrop(Object data) { - // myUserViewerCmp.refresh(); - return true; - } - } - - // LOCAL HELPERS - // private Composite addSection(FormToolkit tk, Composite parent) { - // Section section = tk.createSection(parent, SWT.NO_FOCUS); - // section.setLayoutData(EclipseUiUtils.fillWidth()); - // Composite body = tk.createComposite(section, SWT.WRAP); - // body.setLayoutData(EclipseUiUtils.fillAll()); - // section.setClient(body); - // return body; - // } - - /** Creates label and text. */ - // private Text createLT(Composite parent, String label, String value) { - // FormToolkit toolkit = getManagedForm().getToolkit(); - // Label lbl = toolkit.createLabel(parent, label); - // lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false)); - // lbl.setFont(EclipseUiUtils.getBoldFont(parent)); - // Text text = toolkit.createText(parent, value, SWT.BORDER); - // text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); - // CmsUiUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT); - // return text; - // } - // - // Text createReadOnlyLT(Composite parent, String label, String value) { - // FormToolkit toolkit = getManagedForm().getToolkit(); - // Label lbl = toolkit.createLabel(parent, label); - // lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false)); - // lbl.setFont(EclipseUiUtils.getBoldFont(parent)); - // Text text = toolkit.createText(parent, value, SWT.NONE); - // text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); - // text.setEditable(false); - // CmsUiUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT); - // return text; - // } - -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/GroupsView.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/GroupsView.java deleted file mode 100644 index 73e4f5d..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/GroupsView.java +++ /dev/null @@ -1,251 +0,0 @@ -package org.argeo.cms.e4.users; - -import java.util.ArrayList; -import java.util.List; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import javax.inject.Inject; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.api.cms.CmsLog; -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.e4.users.providers.CommonNameLP; -import org.argeo.cms.e4.users.providers.DomainNameLP; -import org.argeo.cms.e4.users.providers.RoleIconLP; -import org.argeo.cms.e4.users.providers.UserDragListener; -import org.argeo.cms.swt.CmsException; -//import org.argeo.cms.ui.workbench.WorkbenchUiPlugin; -//import org.argeo.cms.ui.workbench.internal.useradmin.UiUserAdminListener; -//import org.argeo.cms.ui.workbench.internal.useradmin.UserAdminWrapper; -//import org.argeo.cms.ui.workbench.internal.useradmin.providers.CommonNameLP; -//import org.argeo.cms.ui.workbench.internal.useradmin.providers.DomainNameLP; -//import org.argeo.cms.ui.workbench.internal.useradmin.providers.RoleIconLP; -//import org.argeo.cms.ui.workbench.internal.useradmin.providers.UserDragListener; -//import org.argeo.cms.ui.workbench.internal.useradmin.providers.UserTableDefaultDClickListener; -import org.argeo.eclipse.ui.ColumnDefinition; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.parts.LdifUsersTable; -import org.argeo.util.naming.LdapAttrs; -import org.argeo.util.naming.LdapObjs; -import org.eclipse.e4.ui.di.Focus; -import org.eclipse.e4.ui.workbench.modeling.EPartService; -import org.eclipse.e4.ui.workbench.modeling.ESelectionService; -import org.eclipse.jface.viewers.ISelectionChangedListener; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.SelectionChangedEvent; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.dnd.DND; -import org.eclipse.swt.dnd.TextTransfer; -import org.eclipse.swt.dnd.Transfer; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -//import org.eclipse.ui.part.ViewPart; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdminEvent; -import org.osgi.service.useradmin.UserAdminListener; - -/** List all groups with filter */ -public class GroupsView { - private final static CmsLog log = CmsLog.getLog(GroupsView.class); - // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".groupsView"; - - @Inject - private EPartService partService; - @Inject - private UserAdminWrapper userAdminWrapper; - - // UI Objects - private LdifUsersTable groupTableViewerCmp; - private TableViewer userViewer; - private List columnDefs = new ArrayList(); - - private UserAdminListener listener; - - @PostConstruct - public void createPartControl(Composite parent, ESelectionService selectionService) { - parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); - - // boolean isAdmin = CurrentUser.isInRole(NodeConstants.ROLE_ADMIN); - - // Define the displayed columns - columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 19)); - columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Name", 150)); - columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 100)); - // Only show technical DN to admin - // if (isAdmin) - // columnDefs.add(new ColumnDefinition(new UserNameLP(), - // "Distinguished Name", 300)); - - // Create and configure the table - groupTableViewerCmp = new MyUserTableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); - - groupTableViewerCmp.setColumnDefinitions(columnDefs); - // if (isAdmin) - // groupTableViewerCmp.populateWithStaticFilters(false, false); - // else - groupTableViewerCmp.populate(true, false); - - groupTableViewerCmp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - - // Links - userViewer = groupTableViewerCmp.getTableViewer(); - userViewer.addDoubleClickListener(new UserTableDefaultDClickListener(partService)); - // getViewSite().setSelectionProvider(userViewer); - userViewer.addSelectionChangedListener(new ISelectionChangedListener() { - - @Override - public void selectionChanged(SelectionChangedEvent event) { - IStructuredSelection selection = (IStructuredSelection) event.getSelection(); - selectionService.setSelection(selection.toList()); - } - }); - - // Really? - groupTableViewerCmp.refresh(); - - // Drag and drop - int operations = DND.DROP_COPY | DND.DROP_MOVE; - Transfer[] tt = new Transfer[] { TextTransfer.getInstance() }; - userViewer.addDragSupport(operations, tt, new UserDragListener(userViewer)); - - // // Register a useradmin listener - // listener = new UserAdminListener() { - // @Override - // public void roleChanged(UserAdminEvent event) { - // if (userViewer != null && !userViewer.getTable().isDisposed()) - // refresh(); - // } - // }; - // userAdminWrapper.addListener(listener); - // } - - // Register a useradmin listener - listener = new MyUiUAListener(parent.getDisplay()); - userAdminWrapper.addListener(listener); - } - - private class MyUiUAListener extends UiUserAdminListener { - public MyUiUAListener(Display display) { - super(display); - } - - @Override - public void roleChangedToUiThread(UserAdminEvent event) { - if (userViewer != null && !userViewer.getTable().isDisposed()) - refresh(); - } - } - - private class MyUserTableViewer extends LdifUsersTable { - private static final long serialVersionUID = 8467999509931900367L; - - private boolean showSystemRoles = true; - - private final String[] knownProps = { LdapAttrs.uid.name(), LdapAttrs.cn.name(), LdapAttrs.DN }; - - public MyUserTableViewer(Composite parent, int style) { - super(parent, style); - showSystemRoles = CurrentUser.isInRole(CmsConstants.ROLE_ADMIN); - } - - protected void populateStaticFilters(Composite staticFilterCmp) { - staticFilterCmp.setLayout(new GridLayout()); - final Button showSystemRoleBtn = new Button(staticFilterCmp, SWT.CHECK); - showSystemRoleBtn.setText("Show system roles"); - showSystemRoles = CurrentUser.isInRole(CmsConstants.ROLE_ADMIN); - showSystemRoleBtn.setSelection(showSystemRoles); - - showSystemRoleBtn.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = -7033424592697691676L; - - @Override - public void widgetSelected(SelectionEvent e) { - showSystemRoles = showSystemRoleBtn.getSelection(); - refresh(); - } - - }); - } - - @Override - protected List listFilteredElements(String filter) { - Role[] roles; - try { - StringBuilder builder = new StringBuilder(); - StringBuilder tmpBuilder = new StringBuilder(); - if (EclipseUiUtils.notEmpty(filter)) - for (String prop : knownProps) { - tmpBuilder.append("("); - tmpBuilder.append(prop); - tmpBuilder.append("=*"); - tmpBuilder.append(filter); - tmpBuilder.append("*)"); - } - if (tmpBuilder.length() > 1) { - builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=") - .append(LdapObjs.groupOfNames.name()).append(")"); - // hide tokens - builder.append("(!(").append(LdapAttrs.DN).append("=*").append(CmsConstants.TOKENS_BASEDN) - .append("))"); - - if (!showSystemRoles) - builder.append("(!(").append(LdapAttrs.DN).append("=*").append(CmsConstants.SYSTEM_ROLES_BASEDN) - .append("))"); - builder.append("(|"); - builder.append(tmpBuilder.toString()); - builder.append("))"); - } else { - if (!showSystemRoles) - builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=") - .append(LdapObjs.groupOfNames.name()).append(")(!(").append(LdapAttrs.DN).append("=*") - .append(CmsConstants.SYSTEM_ROLES_BASEDN).append("))(!(").append(LdapAttrs.DN).append("=*") - .append(CmsConstants.TOKENS_BASEDN).append(")))"); - else - builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=") - .append(LdapObjs.groupOfNames.name()).append(")(!(").append(LdapAttrs.DN).append("=*") - .append(CmsConstants.TOKENS_BASEDN).append(")))"); - - } - roles = userAdminWrapper.getUserAdmin().getRoles(builder.toString()); - } catch (InvalidSyntaxException e) { - throw new CmsException("Unable to get roles with filter: " + filter, e); - } - List users = new ArrayList(); - for (Role role : roles) - if (!users.contains(role)) - users.add((User) role); - else - log.warn("Duplicated role: " + role); - - return users; - } - } - - public void refresh() { - groupTableViewerCmp.refresh(); - } - - @PreDestroy - public void dispose() { - userAdminWrapper.removeListener(listener); - } - - @Focus - public void setFocus() { - groupTableViewerCmp.setFocus(); - } - - /* DEPENDENCY INJECTION */ - public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { - this.userAdminWrapper = userAdminWrapper; - } -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/SecurityAdminImages.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/SecurityAdminImages.java deleted file mode 100644 index 7bbe3c7..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/SecurityAdminImages.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.argeo.cms.e4.users; - -import org.argeo.cms.ui.theme.CmsImages; -import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.swt.graphics.Image; - -/** Shared icons that must be declared programmatically . */ -public class SecurityAdminImages extends CmsImages { - private final static String PREFIX = "icons/"; - - public final static ImageDescriptor ICON_REMOVE_DESC = createDesc(PREFIX + "delete.png"); - public final static ImageDescriptor ICON_USER_DESC = createDesc(PREFIX + "person.png"); - - public final static Image ICON_USER = ICON_USER_DESC.createImage(); - public final static Image ICON_GROUP = createImg(PREFIX + "group.png"); - public final static Image ICON_WORKGROUP = createImg(PREFIX + "workgroup.png"); - public final static Image ICON_ROLE = createImg(PREFIX + "role.gif"); - -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UiAdminUtils.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UiAdminUtils.java deleted file mode 100644 index fb48a47..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UiAdminUtils.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.argeo.cms.e4.users; - -import org.argeo.util.transaction.WorkTransaction; - -/** First effort to centralize back end methods used by the user admin UI */ -public class UiAdminUtils { - /* - * INTERNAL METHODS: Below methods are meant to stay here and are not part - * of a potential generic backend to manage the useradmin - */ - /** Easily notify the ActiveWindow that the transaction had a state change */ - public final static void notifyTransactionStateChange( - WorkTransaction userTransaction) { -// try { -// IWorkbenchWindow aww = PlatformUI.getWorkbench() -// .getActiveWorkbenchWindow(); -// ISourceProviderService sourceProviderService = (ISourceProviderService) aww -// .getService(ISourceProviderService.class); -// UserTransactionProvider esp = (UserTransactionProvider) sourceProviderService -// .getSourceProvider(UserTransactionProvider.TRANSACTION_STATE); -// esp.fireTransactionStateChange(); -// } catch (Exception e) { -// throw new CmsException("Unable to begin transaction", e); -// } - } - - /** - * Email addresses must match this regexp pattern ({@value #EMAIL_PATTERN}. - * Thanks to this tip. - */ - public final static String EMAIL_PATTERN = "^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$"; -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UiUserAdminListener.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UiUserAdminListener.java deleted file mode 100644 index eb64aba..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UiUserAdminListener.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.argeo.cms.e4.users; - -import org.eclipse.swt.widgets.Display; -import org.osgi.service.useradmin.UserAdminEvent; -import org.osgi.service.useradmin.UserAdminListener; - -/** Convenience class to insure the call to refresh is done in the UI thread */ -public abstract class UiUserAdminListener implements UserAdminListener { - - private final Display display; - - public UiUserAdminListener(Display display) { - this.display = display; - } - - @Override - public void roleChanged(final UserAdminEvent event) { - display.asyncExec(new Runnable() { - @Override - public void run() { - roleChangedToUiThread(event); - } - }); - } - - public abstract void roleChangedToUiThread(UserAdminEvent event); -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java deleted file mode 100644 index dbb629c..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java +++ /dev/null @@ -1,153 +0,0 @@ -package org.argeo.cms.e4.users; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.cms.swt.CmsException; -import org.argeo.osgi.useradmin.UserDirectory; -import org.argeo.util.directory.DirectoryConf; -import org.argeo.util.transaction.WorkTransaction; -import org.osgi.service.useradmin.UserAdmin; -import org.osgi.service.useradmin.UserAdminEvent; -import org.osgi.service.useradmin.UserAdminListener; - -/** Centralise interaction with the UserAdmin in this bundle */ -public class UserAdminWrapper { - - private UserAdmin userAdmin; - // private ServiceReference userAdminServiceReference; -// private Set uris; - private Map> userDirectories = Collections - .synchronizedMap(new LinkedHashMap<>()); - private WorkTransaction userTransaction; - - // First effort to simplify UX while managing users and groups - public final static boolean COMMIT_ON_SAVE = true; - - // Registered listeners - List listeners = new ArrayList(); - - /** - * Starts a transaction if necessary. Should always been called together with - * {@link UserAdminWrapper#commitOrNotifyTransactionStateChange()} once the - * security model changes have been performed. - */ - public WorkTransaction beginTransactionIfNeeded() { - try { - // UserTransaction userTransaction = getUserTransaction(); - if (userTransaction.isNoTransactionStatus()) { - userTransaction.begin(); - // UiAdminUtils.notifyTransactionStateChange(userTransaction); - } - return userTransaction; - } catch (Exception e) { - throw new CmsException("Unable to begin transaction", e); - } - } - - /** - * Depending on the current application configuration, it will either commit the - * current transaction or throw a notification that the transaction state has - * changed (In the later case, it must be called from the UI thread). - */ - public void commitOrNotifyTransactionStateChange() { - try { - // UserTransaction userTransaction = getUserTransaction(); - if (userTransaction.isNoTransactionStatus()) - return; - - if (UserAdminWrapper.COMMIT_ON_SAVE) - userTransaction.commit(); - else - UiAdminUtils.notifyTransactionStateChange(userTransaction); - } catch (Exception e) { - throw new CmsException("Unable to clean transaction", e); - } - } - - // TODO implement safer mechanism - public void addListener(UserAdminListener userAdminListener) { - if (!listeners.contains(userAdminListener)) - listeners.add(userAdminListener); - } - - public void removeListener(UserAdminListener userAdminListener) { - if (listeners.contains(userAdminListener)) - listeners.remove(userAdminListener); - } - - public void notifyListeners(UserAdminEvent event) { - for (UserAdminListener listener : listeners) - listener.roleChanged(event); - } - - public Map getKnownBaseDns(boolean onlyWritable) { - Map dns = new HashMap(); - for (UserDirectory userDirectory : userDirectories.keySet()) { - Boolean readOnly = userDirectory.isReadOnly(); - String baseDn = userDirectory.getBase(); - - if (onlyWritable && readOnly) - continue; - if (baseDn.equalsIgnoreCase(CmsConstants.SYSTEM_ROLES_BASEDN)) - continue; - if (baseDn.equalsIgnoreCase(CmsConstants.TOKENS_BASEDN)) - continue; - dns.put(baseDn, DirectoryConf.propertiesAsUri(userDirectories.get(userDirectory)).toString()); - - } -// for (String uri : uris) { -// if (!uri.startsWith("/")) -// continue; -// Dictionary props = UserAdminConf.uriAsProperties(uri); -// String readOnly = UserAdminConf.readOnly.getValue(props); -// String baseDn = UserAdminConf.baseDn.getValue(props); -// -// if (onlyWritable && "true".equals(readOnly)) -// continue; -// if (baseDn.equalsIgnoreCase(NodeConstants.ROLES_BASEDN)) -// continue; -// if (baseDn.equalsIgnoreCase(NodeConstants.TOKENS_BASEDN)) -// continue; -// dns.put(baseDn, uri); -// } - return dns; - } - - public UserAdmin getUserAdmin() { - return userAdmin; - } - - public WorkTransaction getUserTransaction() { - return userTransaction; - } - - /* DEPENDENCY INJECTION */ - public void setUserAdmin(UserAdmin userAdmin, Map properties) { - this.userAdmin = userAdmin; -// this.uris = Collections.unmodifiableSortedSet(new TreeSet<>(properties.keySet())); - } - - public void setUserTransaction(WorkTransaction userTransaction) { - this.userTransaction = userTransaction; - } - - public void addUserDirectory(UserDirectory userDirectory, Map properties) { - userDirectories.put(userDirectory, new Hashtable<>(properties)); - } - - public void removeUserDirectory(UserDirectory userDirectory, Map properties) { - userDirectories.remove(userDirectory); - } - - // public void setUserAdminServiceReference( - // ServiceReference userAdminServiceReference) { - // this.userAdminServiceReference = userAdminServiceReference; - // } -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UserBatchUpdateWizard.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UserBatchUpdateWizard.java deleted file mode 100644 index 4fc59d3..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UserBatchUpdateWizard.java +++ /dev/null @@ -1,606 +0,0 @@ -package org.argeo.cms.e4.users; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.api.cms.CmsLog; -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.auth.UserAdminUtils; -import org.argeo.cms.e4.users.providers.CommonNameLP; -import org.argeo.cms.e4.users.providers.DomainNameLP; -import org.argeo.cms.e4.users.providers.MailLP; -import org.argeo.cms.e4.users.providers.UserNameLP; -import org.argeo.cms.swt.CmsException; -import org.argeo.eclipse.ui.ColumnDefinition; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.parts.LdifUsersTable; -import org.argeo.util.naming.LdapAttrs; -import org.argeo.util.naming.LdapObjs; -import org.argeo.util.transaction.WorkTransaction; -import org.eclipse.jface.dialogs.IPageChangeProvider; -import org.eclipse.jface.dialogs.IPageChangedListener; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.dialogs.PageChangedEvent; -import org.eclipse.jface.wizard.IWizardContainer; -import org.eclipse.jface.wizard.Wizard; -import org.eclipse.jface.wizard.WizardPage; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Combo; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Text; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdminEvent; - -/** Wizard to update users */ -public class UserBatchUpdateWizard extends Wizard { - - private final static CmsLog log = CmsLog.getLog(UserBatchUpdateWizard.class); - private UserAdminWrapper userAdminWrapper; - - // pages - private ChooseCommandWizardPage chooseCommandPage; - private ChooseUsersWizardPage userListPage; - private ValidateAndLaunchWizardPage validatePage; - - // Various implemented commands keys - private final static String CMD_UPDATE_PASSWORD = "resetPassword"; - private final static String CMD_UPDATE_EMAIL = "resetEmail"; - private final static String CMD_GROUP_MEMBERSHIP = "groupMembership"; - - private final Map commands = new HashMap() { - private static final long serialVersionUID = 1L; - { - put("Reset password(s)", CMD_UPDATE_PASSWORD); - put("Reset email(s)", CMD_UPDATE_EMAIL); - // TODO implement role / group management - // put("Add/Remove from group", CMD_GROUP_MEMBERSHIP); - } - }; - - public UserBatchUpdateWizard(UserAdminWrapper userAdminWrapper) { - this.userAdminWrapper = userAdminWrapper; - } - - @Override - public void addPages() { - chooseCommandPage = new ChooseCommandWizardPage(); - addPage(chooseCommandPage); - userListPage = new ChooseUsersWizardPage(); - addPage(userListPage); - validatePage = new ValidateAndLaunchWizardPage(); - addPage(validatePage); - } - - @Override - public boolean performFinish() { - if (!canFinish()) - return false; - WorkTransaction ut = userAdminWrapper.getUserTransaction(); - if (!ut.isNoTransactionStatus() && !MessageDialog.openConfirm(getShell(), "Existing Transaction", - "A user transaction is already existing, " + "are you sure you want to proceed ?")) - return false; - - // We cannot use jobs, user modifications are still meant to be done in - // the UIThread - // UpdateJob job = null; - // if (job != null) - // job.schedule(); - - if (CMD_UPDATE_PASSWORD.equals(chooseCommandPage.getCommand())) { - char[] newValue = chooseCommandPage.getPwdValue(); - if (newValue == null) - throw new CmsException("Password cannot be null or an empty string"); - ResetPassword job = new ResetPassword(userAdminWrapper, userListPage.getSelectedUsers(), newValue); - job.doUpdate(); - } else if (CMD_UPDATE_EMAIL.equals(chooseCommandPage.getCommand())) { - String newValue = chooseCommandPage.getEmailValue(); - if (newValue == null) - throw new CmsException("Password cannot be null or an empty string"); - ResetEmail job = new ResetEmail(userAdminWrapper, userListPage.getSelectedUsers(), newValue); - job.doUpdate(); - } - return true; - } - - public boolean canFinish() { - if (this.getContainer().getCurrentPage() == validatePage) - return true; - return false; - } - - private class ResetPassword { - private char[] newPwd; - private UserAdminWrapper userAdminWrapper; - private List usersToUpdate; - - public ResetPassword(UserAdminWrapper userAdminWrapper, List usersToUpdate, char[] newPwd) { - this.newPwd = newPwd; - this.usersToUpdate = usersToUpdate; - this.userAdminWrapper = userAdminWrapper; - } - - @SuppressWarnings("unchecked") - protected void doUpdate() { - userAdminWrapper.beginTransactionIfNeeded(); - try { - for (User user : usersToUpdate) { - // the char array is emptied after being used. - user.getCredentials().put(null, newPwd.clone()); - } - userAdminWrapper.commitOrNotifyTransactionStateChange(); - } catch (Exception e) { - throw new CmsException("Cannot perform batch update on users", e); - } finally { - WorkTransaction ut = userAdminWrapper.getUserTransaction(); - if (!ut.isNoTransactionStatus()) - ut.rollback(); - } - } - } - - private class ResetEmail { - private String newEmail; - private UserAdminWrapper userAdminWrapper; - private List usersToUpdate; - - public ResetEmail(UserAdminWrapper userAdminWrapper, List usersToUpdate, String newEmail) { - this.newEmail = newEmail; - this.usersToUpdate = usersToUpdate; - this.userAdminWrapper = userAdminWrapper; - } - - @SuppressWarnings("unchecked") - protected void doUpdate() { - userAdminWrapper.beginTransactionIfNeeded(); - try { - for (User user : usersToUpdate) { - // the char array is emptied after being used. - user.getProperties().put(LdapAttrs.mail.name(), newEmail); - } - - userAdminWrapper.commitOrNotifyTransactionStateChange(); - if (!usersToUpdate.isEmpty()) - userAdminWrapper.notifyListeners( - new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, usersToUpdate.get(0))); - } catch (Exception e) { - throw new CmsException("Cannot perform batch update on users", e); - } finally { - WorkTransaction ut = userAdminWrapper.getUserTransaction(); - if (!ut.isNoTransactionStatus()) - ut.rollback(); - } - } - } - - // @SuppressWarnings("unused") - // private class AddToGroup extends UpdateJob { - // private String groupID; - // private Session session; - // - // public AddToGroup(Session session, List nodesToUpdate, - // String groupID) { - // super(session, nodesToUpdate); - // this.session = session; - // this.groupID = groupID; - // } - // - // protected void doUpdate(Node node) { - // log.info("Add/Remove to group actions are not yet implemented"); - // // TODO implement this - // // try { - // // throw new CmsException("Not yet implemented"); - // // } catch (RepositoryException re) { - // // throw new CmsException( - // // "Unable to update boolean value for node " + node, re); - // // } - // } - // } - - // /** - // * Base privileged job that will be run asynchronously to perform the - // batch - // * update - // */ - // private abstract class UpdateJob extends PrivilegedJob { - // - // private final UserAdminWrapper userAdminWrapper; - // private final List usersToUpdate; - // - // protected abstract void doUpdate(User user); - // - // public UpdateJob(UserAdminWrapper userAdminWrapper, - // List usersToUpdate) { - // super("Perform update"); - // this.usersToUpdate = usersToUpdate; - // this.userAdminWrapper = userAdminWrapper; - // } - // - // @Override - // protected IStatus doRun(IProgressMonitor progressMonitor) { - // try { - // JcrMonitor monitor = new EclipseJcrMonitor(progressMonitor); - // int total = usersToUpdate.size(); - // monitor.beginTask("Performing change", total); - // userAdminWrapper.beginTransactionIfNeeded(); - // for (User user : usersToUpdate) { - // doUpdate(user); - // monitor.worked(1); - // } - // userAdminWrapper.getUserTransaction().commit(); - // } catch (Exception e) { - // throw new CmsException( - // "Cannot perform batch update on users", e); - // } finally { - // UserTransaction ut = userAdminWrapper.getUserTransaction(); - // try { - // if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION) - // ut.rollback(); - // } catch (IllegalStateException | SecurityException - // | SystemException e) { - // log.error("Unable to rollback session in 'finally', " - // + "the system might be in a dirty state"); - // e.printStackTrace(); - // } - // } - // return Status.OK_STATUS; - // } - // } - - // PAGES - /** - * Displays a combo box that enables user to choose which action to perform - */ - private class ChooseCommandWizardPage extends WizardPage { - private static final long serialVersionUID = -8069434295293996633L; - private Combo chooseCommandCmb; - private Button trueChk; - private Text valueTxt; - private Text pwdTxt; - private Text pwd2Txt; - - public ChooseCommandWizardPage() { - super("Choose a command to run."); - setTitle("Choose a command to run."); - } - - @Override - public void createControl(Composite parent) { - GridLayout gl = new GridLayout(); - Composite container = new Composite(parent, SWT.NO_FOCUS); - container.setLayout(gl); - - chooseCommandCmb = new Combo(container, SWT.READ_ONLY); - chooseCommandCmb.setLayoutData(EclipseUiUtils.fillWidth()); - String[] values = commands.keySet().toArray(new String[0]); - chooseCommandCmb.setItems(values); - - final Composite bottomPart = new Composite(container, SWT.NO_FOCUS); - bottomPart.setLayoutData(EclipseUiUtils.fillAll()); - bottomPart.setLayout(EclipseUiUtils.noSpaceGridLayout()); - - chooseCommandCmb.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = 1L; - - @Override - public void widgetSelected(SelectionEvent e) { - if (getCommand().equals(CMD_UPDATE_PASSWORD)) - populatePasswordCmp(bottomPart); - else if (getCommand().equals(CMD_UPDATE_EMAIL)) - populateEmailCmp(bottomPart); - else if (getCommand().equals(CMD_GROUP_MEMBERSHIP)) - populateGroupCmp(bottomPart); - else - populateBooleanFlagCmp(bottomPart); - checkPageComplete(); - bottomPart.layout(true, true); - } - }); - setControl(container); - } - - private void populateBooleanFlagCmp(Composite parent) { - EclipseUiUtils.clear(parent); - trueChk = new Button(parent, SWT.CHECK); - trueChk.setText("Do it. (It will to the contrary if unchecked)"); - trueChk.setSelection(true); - trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false)); - } - - private void populatePasswordCmp(Composite parent) { - EclipseUiUtils.clear(parent); - Composite body = new Composite(parent, SWT.NO_FOCUS); - - ModifyListener ml = new ModifyListener() { - private static final long serialVersionUID = -1558726363536729634L; - - @Override - public void modifyText(ModifyEvent event) { - checkPageComplete(); - } - }; - - body.setLayout(new GridLayout(2, false)); - body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - pwdTxt = EclipseUiUtils.createGridLP(body, "New password", ml); - pwd2Txt = EclipseUiUtils.createGridLP(body, "Repeat password", ml); - } - - private void populateEmailCmp(Composite parent) { - EclipseUiUtils.clear(parent); - Composite body = new Composite(parent, SWT.NO_FOCUS); - - ModifyListener ml = new ModifyListener() { - private static final long serialVersionUID = 2147704227294268317L; - - @Override - public void modifyText(ModifyEvent event) { - checkPageComplete(); - } - }; - - body.setLayout(new GridLayout(2, false)); - body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - valueTxt = EclipseUiUtils.createGridLT(body, "New e-mail", ml); - } - - private void checkPageComplete() { - String errorMsg = null; - if (chooseCommandCmb.getSelectionIndex() < 0) - errorMsg = "Please select an action"; - else if (CMD_UPDATE_EMAIL.equals(getCommand())) { - if (!valueTxt.getText().matches(UiAdminUtils.EMAIL_PATTERN)) - errorMsg = "Not a valid e-mail address"; - } else if (CMD_UPDATE_PASSWORD.equals(getCommand())) { - if (EclipseUiUtils.isEmpty(pwdTxt.getText()) || pwdTxt.getText().length() < 4) - errorMsg = "Please enter a password that is at least 4 character long"; - else if (!pwdTxt.getText().equals(pwd2Txt.getText())) - errorMsg = "Passwords are different"; - } - if (EclipseUiUtils.notEmpty(errorMsg)) { - setMessage(errorMsg, WizardPage.ERROR); - setPageComplete(false); - } else { - setMessage("Page complete, you can proceed to user choice", WizardPage.INFORMATION); - setPageComplete(true); - } - - getContainer().updateButtons(); - } - - private void populateGroupCmp(Composite parent) { - EclipseUiUtils.clear(parent); - trueChk = new Button(parent, SWT.CHECK); - trueChk.setText("Add to group. (It will remove user(s) from the " + "corresponding group if unchecked)"); - trueChk.setSelection(true); - trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false)); - } - - protected String getCommand() { - return commands.get(chooseCommandCmb.getItem(chooseCommandCmb.getSelectionIndex())); - } - - protected String getCommandLbl() { - return chooseCommandCmb.getItem(chooseCommandCmb.getSelectionIndex()); - } - - @SuppressWarnings("unused") - protected boolean getBoleanValue() { - // FIXME this is not consistent and will lead to errors. - if ("argeo:enabled".equals(getCommand())) - return trueChk.getSelection(); - else - return !trueChk.getSelection(); - } - - @SuppressWarnings("unused") - protected String getStringValue() { - String value = null; - if (valueTxt != null) { - value = valueTxt.getText(); - if ("".equals(value.trim())) - value = null; - } - return value; - } - - protected char[] getPwdValue() { - // We do not directly reset the password text fields: There is no - // need to over secure this process: setting a pwd to multi users - // at the same time is anyhow a bad practice and should be used only - // in test environment or for temporary access - if (pwdTxt == null || pwdTxt.isDisposed()) - return null; - else - return pwdTxt.getText().toCharArray(); - } - - protected String getEmailValue() { - // We do not directly reset the password text fields: There is no - // need to over secure this process: setting a pwd to multi users - // at the same time is anyhow a bad practice and should be used only - // in test environment or for temporary access - if (valueTxt == null || valueTxt.isDisposed()) - return null; - else - return valueTxt.getText(); - } - } - - /** - * Displays a list of users with a check box to be able to choose some of them - */ - private class ChooseUsersWizardPage extends WizardPage implements IPageChangedListener { - private static final long serialVersionUID = 7651807402211214274L; - private ChooseUserTableViewer userTableCmp; - - public ChooseUsersWizardPage() { - super("Choose Users"); - setTitle("Select users who will be impacted"); - } - - @Override - public void createControl(Composite parent) { - Composite pageCmp = new Composite(parent, SWT.NONE); - pageCmp.setLayout(EclipseUiUtils.noSpaceGridLayout()); - - // Define the displayed columns - List columnDefs = new ArrayList(); - columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name", 150)); - columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150)); - columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200)); - - // Only show technical DN to admin - if (CurrentUser.isInRole(CmsConstants.ROLE_ADMIN)) - columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", 300)); - - userTableCmp = new ChooseUserTableViewer(pageCmp, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); - userTableCmp.setLayoutData(EclipseUiUtils.fillAll()); - userTableCmp.setColumnDefinitions(columnDefs); - userTableCmp.populate(true, true); - userTableCmp.refresh(); - - setControl(pageCmp); - - // Add listener to update message when shown - final IWizardContainer wContainer = this.getContainer(); - if (wContainer instanceof IPageChangeProvider) { - ((IPageChangeProvider) wContainer).addPageChangedListener(this); - } - - } - - @Override - public void pageChanged(PageChangedEvent event) { - if (event.getSelectedPage() == this) { - String msg = "Chosen batch action: " + chooseCommandPage.getCommandLbl(); - ((WizardPage) event.getSelectedPage()).setMessage(msg); - } - } - - protected List getSelectedUsers() { - return userTableCmp.getSelectedUsers(); - } - - private class ChooseUserTableViewer extends LdifUsersTable { - private static final long serialVersionUID = 5080437561015853124L; - private final String[] knownProps = { LdapAttrs.uid.name(), LdapAttrs.DN, LdapAttrs.cn.name(), - LdapAttrs.givenName.name(), LdapAttrs.sn.name(), LdapAttrs.mail.name() }; - - public ChooseUserTableViewer(Composite parent, int style) { - super(parent, style); - } - - @Override - protected List listFilteredElements(String filter) { - Role[] roles; - - try { - StringBuilder builder = new StringBuilder(); - - StringBuilder tmpBuilder = new StringBuilder(); - if (EclipseUiUtils.notEmpty(filter)) - for (String prop : knownProps) { - tmpBuilder.append("("); - tmpBuilder.append(prop); - tmpBuilder.append("=*"); - tmpBuilder.append(filter); - tmpBuilder.append("*)"); - } - if (tmpBuilder.length() > 1) { - builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=") - .append(LdapObjs.inetOrgPerson.name()).append(")(|"); - builder.append(tmpBuilder.toString()); - builder.append("))"); - } else - builder.append("(").append(LdapAttrs.objectClass.name()).append("=") - .append(LdapObjs.inetOrgPerson.name()).append(")"); - roles = userAdminWrapper.getUserAdmin().getRoles(builder.toString()); - } catch (InvalidSyntaxException e) { - throw new CmsException("Unable to get roles with filter: " + filter, e); - } - List users = new ArrayList(); - for (Role role : roles) - // Prevent current logged in user to perform batch on - // himself - if (!UserAdminUtils.isCurrentUser((User) role)) - users.add((User) role); - return users; - } - } - } - - /** Summary of input data before launching the process */ - private class ValidateAndLaunchWizardPage extends WizardPage implements IPageChangedListener { - private static final long serialVersionUID = 7098918351451743853L; - private ChosenUsersTableViewer userTableCmp; - - public ValidateAndLaunchWizardPage() { - super("Validate and launch"); - setTitle("Validate and launch"); - } - - @Override - public void createControl(Composite parent) { - Composite pageCmp = new Composite(parent, SWT.NO_FOCUS); - pageCmp.setLayout(EclipseUiUtils.noSpaceGridLayout()); - - List columnDefs = new ArrayList(); - columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name", 150)); - columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150)); - columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200)); - // Only show technical DN to admin - if (CurrentUser.isInRole(CmsConstants.ROLE_ADMIN)) - columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", 300)); - userTableCmp = new ChosenUsersTableViewer(pageCmp, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); - userTableCmp.setLayoutData(EclipseUiUtils.fillAll()); - userTableCmp.setColumnDefinitions(columnDefs); - userTableCmp.populate(false, false); - userTableCmp.refresh(); - setControl(pageCmp); - // Add listener to update message when shown - final IWizardContainer wContainer = this.getContainer(); - if (wContainer instanceof IPageChangeProvider) { - ((IPageChangeProvider) wContainer).addPageChangedListener(this); - } - } - - @Override - public void pageChanged(PageChangedEvent event) { - if (event.getSelectedPage() == this) { - @SuppressWarnings({ "unchecked", "rawtypes" }) - Object[] values = ((ArrayList) userListPage.getSelectedUsers()) - .toArray(new Object[userListPage.getSelectedUsers().size()]); - userTableCmp.getTableViewer().setInput(values); - String msg = "Following batch action: [" + chooseCommandPage.getCommandLbl() - + "] will be perfomed on the users listed below.\n"; - // + "Are you sure you want to proceed?"; - setMessage(msg); - } - } - - private class ChosenUsersTableViewer extends LdifUsersTable { - private static final long serialVersionUID = 7814764735794270541L; - - public ChosenUsersTableViewer(Composite parent, int style) { - super(parent, style); - } - - @Override - protected List listFilteredElements(String filter) { - return userListPage.getSelectedUsers(); - } - } - } -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UserEditor.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UserEditor.java deleted file mode 100644 index 66f4420..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UserEditor.java +++ /dev/null @@ -1,535 +0,0 @@ -package org.argeo.cms.e4.users; - -import static org.argeo.cms.auth.UserAdminUtils.getProperty; -import static org.argeo.util.naming.LdapAttrs.cn; -import static org.argeo.util.naming.LdapAttrs.givenName; -import static org.argeo.util.naming.LdapAttrs.mail; -import static org.argeo.util.naming.LdapAttrs.sn; -import static org.argeo.util.naming.LdapAttrs.uid; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import javax.inject.Inject; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.auth.UserAdminUtils; -import org.argeo.cms.e4.users.providers.CommonNameLP; -import org.argeo.cms.e4.users.providers.DomainNameLP; -import org.argeo.cms.e4.users.providers.RoleIconLP; -import org.argeo.cms.e4.users.providers.UserFilter; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ui.eclipse.forms.AbstractFormPart; -//import org.argeo.cms.ui.eclipse.forms.FormToolkit; -import org.argeo.cms.ui.eclipse.forms.IManagedForm; -import org.argeo.eclipse.ui.ColumnDefinition; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.parts.LdifUsersTable; -import org.argeo.util.naming.LdapAttrs; -import org.eclipse.e4.ui.workbench.modeling.EPartService; -import org.eclipse.jface.action.Action; -import org.eclipse.jface.action.ToolBarManager; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.dialogs.TrayDialog; -import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.jface.viewers.ViewerDropAdapter; -import org.eclipse.swt.SWT; -import org.eclipse.swt.dnd.DND; -import org.eclipse.swt.dnd.DropTargetEvent; -import org.eclipse.swt.dnd.TextTransfer; -import org.eclipse.swt.dnd.Transfer; -import org.eclipse.swt.dnd.TransferData; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Link; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Text; -import org.eclipse.swt.widgets.ToolBar; -import org.osgi.service.useradmin.Group; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdmin; -import org.osgi.service.useradmin.UserAdminEvent; - -/** Display/edit the properties of a given user */ -public class UserEditor extends AbstractRoleEditor { - // final static String ID = "UserEditor.mainPage"; - - @Inject - private EPartService partService; - - // private final UserEditor editor; - // private UserAdminWrapper userAdminWrapper; - - // Local configuration - // private final int PRE_TITLE_INDENT = 10; - - // public UserMainPage(FormEditor editor, UserAdminWrapper userAdminWrapper) { - // super(editor, ID, "Main"); - // this.editor = (UserEditor) editor; - // this.userAdminWrapper = userAdminWrapper; - // } - - // protected void createFormContent(final IManagedForm mf) { - // ScrolledForm form = mf.getForm(); - // Composite body = form.getBody(); - // GridLayout mainLayout = new GridLayout(); - // // mainLayout.marginRight = 10; - // body.setLayout(mainLayout); - // User user = editor.getDisplayedUser(); - // appendOverviewPart(body, user); - // // Remove to ability to force the password for his own user. The user - // // must then use the change pwd feature - // appendMemberOfPart(body, user); - // } - - @Override - protected void createUi(Composite body) { - // Composite body = new Composite(parent, SWT.BORDER); - GridLayout mainLayout = new GridLayout(); - // mainLayout.marginRight = 10; - body.setLayout(mainLayout); - // body.getParent().setLayout(new GridLayout()); - // body.setLayoutData(CmsUiUtils.fillAll()); - User user = getDisplayedUser(); - appendOverviewPart(body, user); - // Remove to ability to force the password for his own user. The user - // must then use the change pwd feature - appendMemberOfPart(body, user); - } - - /** Creates the general section */ - private void appendOverviewPart(final Composite parent, final User user) { - // FormToolkit tk = getManagedForm().getToolkit(); - - // Section section = tk.createSection(parent, SWT.NO_FOCUS); - // GridData gd = EclipseUiUtils.fillWidth(); - // // gd.verticalAlignment = PRE_TITLE_INDENT; - // section.setLayoutData(gd); - Composite body = new Composite(parent, SWT.NONE); - body.setLayoutData(EclipseUiUtils.fillWidth()); - // section.setClient(body); - // body.setLayout(new GridLayout(6, false)); - body.setLayout(new GridLayout(2, false)); - - Text commonName = createReadOnlyLT(body, "Name", getProperty(user, cn)); - Text distinguishedName = createReadOnlyLT(body, "Login", getProperty(user, uid)); - Text firstName = createLT(body, "First name", getProperty(user, givenName)); - Text lastName = createLT(body, "Last name", getProperty(user, sn)); - Text email = createLT(body, "Email", getProperty(user, mail)); - - Link resetPwdLk = new Link(body, SWT.NONE); - if (!UserAdminUtils.isCurrentUser(user)) { - resetPwdLk.setText("Reset password"); - } - resetPwdLk.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1)); - - // create form part (controller) - AbstractFormPart part = new AbstractFormPart() { - private MainInfoListener listener; - - @Override - public void initialize(IManagedForm form) { - super.initialize(form); - listener = new MainInfoListener(parent.getDisplay(), this); - userAdminWrapper.addListener(listener); - } - - @Override - public void dispose() { - userAdminWrapper.removeListener(listener); - super.dispose(); - } - - @SuppressWarnings("unchecked") - public void commit(boolean onSave) { - // TODO Sanity checks (mail validity...) - user.getProperties().put(LdapAttrs.givenName.name(), firstName.getText()); - user.getProperties().put(LdapAttrs.sn.name(), lastName.getText()); - user.getProperties().put(LdapAttrs.cn.name(), commonName.getText()); - user.getProperties().put(LdapAttrs.mail.name(), email.getText()); - super.commit(onSave); - } - - @Override - public void refresh() { - distinguishedName.setText(UserAdminUtils.getProperty(user, LdapAttrs.uid.name())); - commonName.setText(UserAdminUtils.getProperty(user, LdapAttrs.cn.name())); - firstName.setText(UserAdminUtils.getProperty(user, LdapAttrs.givenName.name())); - lastName.setText(UserAdminUtils.getProperty(user, LdapAttrs.sn.name())); - email.setText(UserAdminUtils.getProperty(user, LdapAttrs.mail.name())); - refreshFormTitle(user); - super.refresh(); - } - }; - - // Improve this: automatically generate CN when first or last name - // changes - ModifyListener cnML = new ModifyListener() { - private static final long serialVersionUID = 4298649222869835486L; - - @Override - public void modifyText(ModifyEvent event) { - String first = firstName.getText(); - String last = lastName.getText(); - String cn = first.trim() + " " + last.trim() + " "; - cn = cn.trim(); - commonName.setText(cn); - // getManagedForm().getForm().setText(cn); - updateEditorTitle(cn); - } - }; - firstName.addModifyListener(cnML); - lastName.addModifyListener(cnML); - - ModifyListener defaultListener = new FormPartML(part); - firstName.addModifyListener(defaultListener); - lastName.addModifyListener(defaultListener); - email.addModifyListener(defaultListener); - - if (!UserAdminUtils.isCurrentUser(user)) - resetPwdLk.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = 5881800534589073787L; - - @Override - public void widgetSelected(SelectionEvent e) { - new ChangePasswordDialog(user, "Reset password").open(); - } - }); - - getManagedForm().addPart(part); - } - - private class ChangePasswordDialog extends TrayDialog { - private static final long serialVersionUID = 2843538207460082349L; - - private User user; - private Text password1; - private Text password2; - private String title; - // private FormToolkit tk; - - public ChangePasswordDialog(User user, String title) { - super(Display.getDefault().getActiveShell()); - // this.tk = tk; - this.user = user; - this.title = title; - } - - protected Control createDialogArea(Composite parent) { - Composite dialogarea = (Composite) super.createDialogArea(parent); - dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - Composite body = new Composite(dialogarea, SWT.NO_FOCUS); - body.setLayoutData(EclipseUiUtils.fillAll()); - GridLayout layout = new GridLayout(2, false); - body.setLayout(layout); - - password1 = createLP(body, "New password", ""); - password2 = createLP(body, "Repeat password", ""); - parent.pack(); - return body; - } - - @SuppressWarnings("unchecked") - @Override - protected void okPressed() { - String msg = null; - - if (password1.getText().equals("")) - msg = "Password cannot be empty"; - else if (password1.getText().equals(password2.getText())) { - char[] newPassword = password1.getText().toCharArray(); - // userAdminWrapper.beginTransactionIfNeeded(); - userAdminWrapper.beginTransactionIfNeeded(); - user.getCredentials().put(null, newPassword); - userAdminWrapper.commitOrNotifyTransactionStateChange(); - super.okPressed(); - } else { - msg = "Passwords are not equals"; - } - - if (EclipseUiUtils.notEmpty(msg)) - MessageDialog.openError(getParentShell(), "Cannot reset pasword", msg); - } - - protected void configureShell(Shell shell) { - super.configureShell(shell); - shell.setText(title); - } - } - - private LdifUsersTable appendMemberOfPart(final Composite parent, User user) { - // Section section = addSection(tk, parent, "Roles"); - // Composite body = (Composite) section.getClient(); - // Composite body= parent; - Composite body = new Composite(parent, SWT.BORDER); - body.setLayout(new GridLayout()); - body.setLayoutData(CmsSwtUtils.fillAll()); - - // boolean isAdmin = CurrentUser.isInRole(NodeConstants.ROLE_ADMIN); - - // Displayed columns - List columnDefs = new ArrayList(); - columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 0, 24)); - columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Name", 150)); - columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 100)); - // Only show technical DN to administrators - // if (isAdmin) - // columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", - // 300)); - - // Create and configure the table - final LdifUsersTable userViewerCmp = new MyUserTableViewer(body, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL, user); - - userViewerCmp.setColumnDefinitions(columnDefs); - // if (isAdmin) - // userViewerCmp.populateWithStaticFilters(false, false); - // else - userViewerCmp.populate(true, false); - GridData gd = EclipseUiUtils.fillAll(); - gd.heightHint = 500; - userViewerCmp.setLayoutData(gd); - - // Controllers - TableViewer userViewer = userViewerCmp.getTableViewer(); - userViewer.addDoubleClickListener(new UserTableDefaultDClickListener(partService)); - int operations = DND.DROP_COPY | DND.DROP_MOVE; - Transfer[] tt = new Transfer[] { TextTransfer.getInstance() }; - GroupDropListener dropL = new GroupDropListener(userAdminWrapper, userViewer, user); - userViewer.addDropSupport(operations, tt, dropL); - - AbstractFormPart part = new AbstractFormPart() { - - private GroupChangeListener listener; - - @Override - public void initialize(IManagedForm form) { - super.initialize(form); - listener = new GroupChangeListener(parent.getDisplay(), this); - userAdminWrapper.addListener(listener); - } - - public void commit(boolean onSave) { - super.commit(onSave); - } - - @Override - public void dispose() { - userAdminWrapper.removeListener(listener); - super.dispose(); - } - - @Override - public void refresh() { - userViewerCmp.refresh(); - super.refresh(); - } - }; - getManagedForm().addPart(part); - // addRemoveAbitily(body, userViewer, user); - // userViewerCmp.refresh(); - String tooltip = "Remove " + UserAdminUtils.getUserLocalId(user.getName()) + " from the below selected groups"; - Action action = new RemoveMembershipAction(userViewer, user, tooltip, SecurityAdminImages.ICON_REMOVE_DESC); - ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT); - ToolBar toolBar = toolBarManager.createControl(body); - toolBar.setLayoutData(CmsSwtUtils.fillWidth()); - toolBarManager.add(action); - toolBarManager.update(true); - return userViewerCmp; - } - - private class MyUserTableViewer extends LdifUsersTable { - private static final long serialVersionUID = 2653790051461237329L; - - private Button showSystemRoleBtn; - - private final User user; - private final UserFilter userFilter; - - public MyUserTableViewer(Composite parent, int style, User user) { - super(parent, style, true); - this.user = user; - userFilter = new UserFilter(); - } - - protected void populateStaticFilters(Composite staticFilterCmp) { - staticFilterCmp.setLayout(new GridLayout()); - showSystemRoleBtn = new Button(staticFilterCmp, SWT.CHECK); - showSystemRoleBtn.setText("Show system roles"); - boolean showSysRole = CurrentUser.isInRole(CmsConstants.ROLE_ADMIN); - showSystemRoleBtn.setSelection(showSysRole); - userFilter.setShowSystemRole(showSysRole); - showSystemRoleBtn.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = -7033424592697691676L; - - @Override - public void widgetSelected(SelectionEvent e) { - userFilter.setShowSystemRole(showSystemRoleBtn.getSelection()); - refresh(); - } - }); - } - - @Override - protected List listFilteredElements(String filter) { - List users = (List) getFlatGroups(null); - List filteredUsers = new ArrayList(); - if (users.contains(user)) - users.remove(user); - userFilter.setSearchText(filter); - for (User user : users) - if (userFilter.select(null, null, user)) - filteredUsers.add(user); - return filteredUsers; - } - } - - // private void addRemoveAbility(Composite parent, TableViewer userViewer, User - // user) { - // // Section section = sectionPart.getSection(); - // ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT); - // ToolBar toolbar = toolBarManager.createControl(parent); - // final Cursor handCursor = new Cursor(Display.getCurrent(), SWT.CURSOR_HAND); - // toolbar.setCursor(handCursor); - // toolbar.addDisposeListener(new DisposeListener() { - // private static final long serialVersionUID = 3882131405820522925L; - // - // public void widgetDisposed(DisposeEvent e) { - // if ((handCursor != null) && (handCursor.isDisposed() == false)) { - // handCursor.dispose(); - // } - // } - // }); - // - // String tooltip = "Remove " + UserAdminUtils.getUserLocalId(user.getName()) + - // " from the below selected groups"; - // Action action = new RemoveMembershipAction(userViewer, user, tooltip, - // SecurityAdminImages.ICON_REMOVE_DESC); - // toolBarManager.add(action); - // toolBarManager.update(true); - // // section.setTextClient(toolbar); - // } - - private class RemoveMembershipAction extends Action { - private static final long serialVersionUID = -1337713097184522588L; - - private final TableViewer userViewer; - private final User user; - - RemoveMembershipAction(TableViewer userViewer, User user, String name, ImageDescriptor img) { - super(name, img); - this.userViewer = userViewer; - this.user = user; - } - - @Override - public void run() { - ISelection selection = userViewer.getSelection(); - if (selection.isEmpty()) - return; - - @SuppressWarnings("unchecked") - Iterator it = ((IStructuredSelection) selection).iterator(); - List groups = new ArrayList(); - while (it.hasNext()) { - Group currGroup = it.next(); - groups.add(currGroup); - } - - userAdminWrapper.beginTransactionIfNeeded(); - for (Group group : groups) { - group.removeMember(user); - } - userAdminWrapper.commitOrNotifyTransactionStateChange(); - for (Group group : groups) { - userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, group)); - } - } - } - - /** - * Defines the table as being a potential target to add group memberships - * (roles) to this user - */ - private class GroupDropListener extends ViewerDropAdapter { - private static final long serialVersionUID = 2893468717831451621L; - - private final UserAdminWrapper myUserAdminWrapper; - private final User myUser; - - public GroupDropListener(UserAdminWrapper userAdminWrapper, Viewer userViewer, User user) { - super(userViewer); - this.myUserAdminWrapper = userAdminWrapper; - this.myUser = user; - } - - @Override - public boolean validateDrop(Object target, int operation, TransferData transferType) { - // Target is always OK in a list only view - // TODO check if not a string - boolean validDrop = true; - return validDrop; - } - - @Override - public void drop(DropTargetEvent event) { - String name = (String) event.data; - UserAdmin myUserAdmin = myUserAdminWrapper.getUserAdmin(); - Role role = myUserAdmin.getRole(name); - // TODO this check should be done before. - if (role.getType() == Role.GROUP) { - // TODO check if the user is already member of this group - - myUserAdminWrapper.beginTransactionIfNeeded(); - Group group = (Group) role; - group.addMember(myUser); - userAdminWrapper.commitOrNotifyTransactionStateChange(); - myUserAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, group)); - } - super.drop(event); - } - - @Override - public boolean performDrop(Object data) { - // userTableViewerCmp.refresh(); - return true; - } - } - - // LOCAL HELPERS - private void refreshFormTitle(User group) { - // getManagedForm().getForm().setText(UserAdminUtils.getProperty(group, - // LdapAttrs.cn.name())); - } - - /** Appends a section with a title */ - // private Section addSection(FormToolkit tk, Composite parent, String title) { - // Section section = tk.createSection(parent, Section.TITLE_BAR); - // GridData gd = EclipseUiUtils.fillWidth(); - // gd.verticalAlignment = PRE_TITLE_INDENT; - // section.setLayoutData(gd); - // section.setText(title); - // // section.getMenu().setVisible(true); - // - // Composite body = tk.createComposite(section, SWT.WRAP); - // body.setLayoutData(EclipseUiUtils.fillAll()); - // section.setClient(body); - // - // return section; - // } - -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UserTableDefaultDClickListener.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UserTableDefaultDClickListener.java deleted file mode 100644 index c6d024e..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UserTableDefaultDClickListener.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.argeo.cms.e4.users; - -import org.argeo.cms.e4.CmsE4Utils; -import org.argeo.util.naming.LdapAttrs; -import org.eclipse.e4.ui.workbench.modeling.EPartService; -import org.eclipse.jface.viewers.DoubleClickEvent; -import org.eclipse.jface.viewers.IDoubleClickListener; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.osgi.service.useradmin.Group; -import org.osgi.service.useradmin.User; - -/** - * Default double click listener for the various user tables, will open the - * clicked item in the editor - */ -public class UserTableDefaultDClickListener implements IDoubleClickListener { - private final EPartService partService; - - public UserTableDefaultDClickListener(EPartService partService) { - this.partService = partService; - } - - public void doubleClick(DoubleClickEvent evt) { - if (evt.getSelection().isEmpty()) - return; - Object obj = ((IStructuredSelection) evt.getSelection()).getFirstElement(); - User user = (User) obj; - - String editorId = getEditorId(user); - CmsE4Utils.openEditor(partService, editorId, LdapAttrs.uid.name(), user.getName()); - } - - protected String getEditorId(User user) { - if (user instanceof Group) - return "org.argeo.cms.e4.partdescriptor.groupEditor"; - else - return "org.argeo.cms.e4.partdescriptor.userEditor"; - } -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UsersView.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UsersView.java deleted file mode 100644 index 720945c..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UsersView.java +++ /dev/null @@ -1,182 +0,0 @@ -package org.argeo.cms.e4.users; - -import java.util.ArrayList; -import java.util.List; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import javax.inject.Inject; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.e4.users.providers.CommonNameLP; -import org.argeo.cms.e4.users.providers.DomainNameLP; -import org.argeo.cms.e4.users.providers.MailLP; -import org.argeo.cms.e4.users.providers.UserDragListener; -import org.argeo.cms.e4.users.providers.UserNameLP; -import org.argeo.cms.swt.CmsException; -import org.argeo.eclipse.ui.ColumnDefinition; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.parts.LdifUsersTable; -import org.argeo.util.naming.LdapAttrs; -import org.argeo.util.naming.LdapObjs; -import org.eclipse.e4.ui.di.Focus; -import org.eclipse.e4.ui.workbench.modeling.EPartService; -import org.eclipse.e4.ui.workbench.modeling.ESelectionService; -import org.eclipse.jface.viewers.ISelectionChangedListener; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.SelectionChangedEvent; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.dnd.DND; -import org.eclipse.swt.dnd.TextTransfer; -import org.eclipse.swt.dnd.Transfer; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdminEvent; -import org.osgi.service.useradmin.UserAdminListener; - -/** List all users with filter - based on Ldif userAdmin */ -public class UsersView { - // private final static Log log = LogFactory.getLog(UsersView.class); - - // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".usersView"; - - @Inject - private UserAdminWrapper userAdminWrapper; - @Inject - private EPartService partService; - - // UI Objects - private LdifUsersTable userTableViewerCmp; - private TableViewer userViewer; - private List columnDefs = new ArrayList(); - - private UserAdminListener listener; - - @PostConstruct - public void createPartControl(Composite parent, ESelectionService selectionService) { - - parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); - // Define the displayed columns - columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name", 150)); - columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150)); - columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200)); - // Only show technical DN to admin - if (CurrentUser.isInRole(CmsConstants.ROLE_ADMIN)) - columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", 300)); - - // Create and configure the table - userTableViewerCmp = new MyUserTableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); - userTableViewerCmp.setLayoutData(EclipseUiUtils.fillAll()); - userTableViewerCmp.setColumnDefinitions(columnDefs); - userTableViewerCmp.populate(true, false); - - // Links - userViewer = userTableViewerCmp.getTableViewer(); - userViewer.addDoubleClickListener(new UserTableDefaultDClickListener(partService)); - userViewer.addSelectionChangedListener(new ISelectionChangedListener() { - - @Override - public void selectionChanged(SelectionChangedEvent event) { - IStructuredSelection selection = (IStructuredSelection) event.getSelection(); - selectionService.setSelection(selection.toList()); - } - }); - // getViewSite().setSelectionProvider(userViewer); - - // Really? - userTableViewerCmp.refresh(); - - // Drag and drop - int operations = DND.DROP_COPY | DND.DROP_MOVE; - Transfer[] tt = new Transfer[] { TextTransfer.getInstance() }; - userViewer.addDragSupport(operations, tt, new UserDragListener(userViewer)); - - // Register a useradmin listener - listener = new MyUiUAListener(parent.getDisplay()); - userAdminWrapper.addListener(listener); - } - - private class MyUiUAListener extends UiUserAdminListener { - public MyUiUAListener(Display display) { - super(display); - } - - @Override - public void roleChangedToUiThread(UserAdminEvent event) { - if (userViewer != null && !userViewer.getTable().isDisposed()) - refresh(); - } - } - - private class MyUserTableViewer extends LdifUsersTable { - private static final long serialVersionUID = 8467999509931900367L; - - private final String[] knownProps = { LdapAttrs.DN, LdapAttrs.uid.name(), LdapAttrs.cn.name(), - LdapAttrs.givenName.name(), LdapAttrs.sn.name(), LdapAttrs.mail.name() }; - - public MyUserTableViewer(Composite parent, int style) { - super(parent, style); - } - - @Override - protected List listFilteredElements(String filter) { - Role[] roles; - - try { - StringBuilder builder = new StringBuilder(); - - StringBuilder tmpBuilder = new StringBuilder(); - if (EclipseUiUtils.notEmpty(filter)) - for (String prop : knownProps) { - tmpBuilder.append("("); - tmpBuilder.append(prop); - tmpBuilder.append("=*"); - tmpBuilder.append(filter); - tmpBuilder.append("*)"); - } - if (tmpBuilder.length() > 1) { - builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=") - .append(LdapObjs.inetOrgPerson.name()).append(")(|"); - builder.append(tmpBuilder.toString()); - builder.append("))"); - } else - builder.append("(").append(LdapAttrs.objectClass.name()).append("=") - .append(LdapObjs.inetOrgPerson.name()).append(")"); - roles = userAdminWrapper.getUserAdmin().getRoles(builder.toString()); - } catch (InvalidSyntaxException e) { - throw new CmsException("Unable to get roles with filter: " + filter, e); - } - List users = new ArrayList(); - for (Role role : roles) - // if (role.getType() == Role.USER && role.getType() != - // Role.GROUP) - users.add((User) role); - return users; - } - } - - public void refresh() { - userTableViewerCmp.refresh(); - } - - // Override generic view methods - @PreDestroy - public void dispose() { - userAdminWrapper.removeListener(listener); - } - - @Focus - public void setFocus() { - userTableViewerCmp.setFocus(); - } - - /* DEPENDENCY INJECTION */ - public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { - this.userAdminWrapper = userAdminWrapper; - } -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/DeleteGroups.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/DeleteGroups.java deleted file mode 100644 index 742bc3f..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/DeleteGroups.java +++ /dev/null @@ -1,95 +0,0 @@ -package org.argeo.cms.e4.users.handlers; - -import java.util.List; - -import javax.inject.Inject; -import javax.inject.Named; - -import org.argeo.cms.auth.UserAdminUtils; -import org.argeo.cms.e4.users.GroupsView; -import org.argeo.cms.e4.users.UserAdminWrapper; -import org.eclipse.e4.core.di.annotations.CanExecute; -import org.eclipse.e4.core.di.annotations.Execute; -import org.eclipse.e4.ui.model.application.ui.basic.MPart; -import org.eclipse.e4.ui.services.IServiceConstants; -import org.eclipse.e4.ui.workbench.modeling.ESelectionService; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.swt.widgets.Display; -import org.osgi.service.useradmin.Group; -import org.osgi.service.useradmin.UserAdmin; -import org.osgi.service.useradmin.UserAdminEvent; - -/** Delete the selected groups */ -public class DeleteGroups { - // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + - // ".deleteGroups"; - - /* DEPENDENCY INJECTION */ - @Inject - private UserAdminWrapper userAdminWrapper; - - @Inject - ESelectionService selectionService; - - @SuppressWarnings("unchecked") - @Execute - public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) { - // ISelection selection = null;// HandlerUtil.getCurrentSelection(event); - // if (selection.isEmpty()) - // return null; - // - // List groups = new ArrayList(); - // Iterator it = ((IStructuredSelection) selection).iterator(); - - List selection = (List) selectionService.getSelection(); - if (selection == null) - return; - - StringBuilder builder = new StringBuilder(); - for (Group group : selection) { - Group currGroup = group; - String groupName = UserAdminUtils.getUserLocalId(currGroup.getName()); - // TODO add checks - builder.append(groupName).append("; "); - // groups.add(currGroup); - } - - if (!MessageDialog.openQuestion(Display.getCurrent().getActiveShell(), "Delete Groups", "Are you sure that you " - + "want to delete these groups?\n" + builder.substring(0, builder.length() - 2))) - return; - - userAdminWrapper.beginTransactionIfNeeded(); - UserAdmin userAdmin = userAdminWrapper.getUserAdmin(); - // IWorkbenchPage iwp = - // HandlerUtil.getActiveWorkbenchWindow(event).getActivePage(); - for (Group group : selection) { - String groupName = group.getName(); - // TODO find a way to close the editor cleanly if opened. Cannot be - // done through the UserAdminListeners, it causes a - // java.util.ConcurrentModificationException because disposing the - // editor unregisters and disposes the listener - // IEditorPart part = iwp.findEditor(new UserEditorInput(groupName)); - // if (part != null) - // iwp.closeEditor(part, false); - userAdmin.removeRole(groupName); - } - userAdminWrapper.commitOrNotifyTransactionStateChange(); - - // Update the view - for (Group group : selection) { - userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_REMOVED, group)); - } - - // return null; - } - - @CanExecute - public boolean canExecute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) { - return part.getObject() instanceof GroupsView && selectionService.getSelection() != null; - } - - /* DEPENDENCY INJECTION */ - // public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { - // this.userAdminWrapper = userAdminWrapper; - // } -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/DeleteUsers.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/DeleteUsers.java deleted file mode 100644 index d1afd22..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/DeleteUsers.java +++ /dev/null @@ -1,88 +0,0 @@ -package org.argeo.cms.e4.users.handlers; - -import java.util.List; - -import javax.inject.Inject; -import javax.inject.Named; - -import org.argeo.cms.auth.UserAdminUtils; -import org.argeo.cms.e4.users.UserAdminWrapper; -import org.argeo.cms.e4.users.UsersView; -import org.eclipse.e4.core.di.annotations.CanExecute; -import org.eclipse.e4.core.di.annotations.Execute; -import org.eclipse.e4.ui.model.application.ui.basic.MPart; -import org.eclipse.e4.ui.services.IServiceConstants; -import org.eclipse.e4.ui.workbench.modeling.ESelectionService; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.swt.widgets.Display; -import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdmin; -import org.osgi.service.useradmin.UserAdminEvent; - -/** Delete the selected users */ -public class DeleteUsers { - // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".deleteUsers"; - - /* DEPENDENCY INJECTION */ - @Inject - private UserAdminWrapper userAdminWrapper; - - @SuppressWarnings("unchecked") - @Execute - public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) { - // ISelection selection = null;// HandlerUtil.getCurrentSelection(event); - // if (selection.isEmpty()) - // return null; - List selection = (List) selectionService.getSelection(); - if (selection == null) - return; - -// Iterator it = ((IStructuredSelection) selection).iterator(); -// List users = new ArrayList(); - StringBuilder builder = new StringBuilder(); - - for(User user:selection) { - User currUser = user; -// User currUser = it.next(); - String userName = UserAdminUtils.getUserLocalId(currUser.getName()); - if (UserAdminUtils.isCurrentUser(currUser)) { - MessageDialog.openError(Display.getCurrent().getActiveShell(), "Deletion forbidden", - "You cannot delete your own user this way."); - return; - } - builder.append(userName).append("; "); -// users.add(currUser); - } - - if (!MessageDialog.openQuestion(Display.getCurrent().getActiveShell(), "Delete Users", - "Are you sure that you want to delete these users?\n" + builder.substring(0, builder.length() - 2))) - return; - - userAdminWrapper.beginTransactionIfNeeded(); - UserAdmin userAdmin = userAdminWrapper.getUserAdmin(); - // IWorkbenchPage iwp = - // HandlerUtil.getActiveWorkbenchWindow(event).getActivePage(); - - for (User user : selection) { - String userName = user.getName(); - // TODO find a way to close the editor cleanly if opened. Cannot be - // done through the UserAdminListeners, it causes a - // java.util.ConcurrentModificationException because disposing the - // editor unregisters and disposes the listener - // IEditorPart part = iwp.findEditor(new UserEditorInput(userName)); - // if (part != null) - // iwp.closeEditor(part, false); - userAdmin.removeRole(userName); - } - userAdminWrapper.commitOrNotifyTransactionStateChange(); - - for (User user : selection) { - userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_REMOVED, user)); - } - } - - @CanExecute - public boolean canExecute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) { - return part.getObject() instanceof UsersView && selectionService.getSelection() != null; - } -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/NewGroup.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/NewGroup.java deleted file mode 100644 index 41e14e0..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/NewGroup.java +++ /dev/null @@ -1,212 +0,0 @@ -package org.argeo.cms.e4.users.handlers; - -import java.util.Dictionary; -import java.util.Map; - -import javax.inject.Inject; - -import org.argeo.cms.e4.users.UserAdminWrapper; -import org.argeo.cms.swt.CmsException; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.dialogs.ErrorFeedback; -import org.argeo.util.directory.DirectoryConf; -import org.argeo.util.naming.LdapAttrs; -import org.eclipse.e4.core.di.annotations.Execute; -import org.eclipse.jface.wizard.Wizard; -import org.eclipse.jface.wizard.WizardDialog; -import org.eclipse.jface.wizard.WizardPage; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.FocusEvent; -import org.eclipse.swt.events.FocusListener; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Combo; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; -import org.osgi.service.useradmin.Group; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.UserAdminEvent; - -/** Create a new group */ -public class NewGroup { - // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".newGroup"; - - /* DEPENDENCY INJECTION */ - @Inject - private UserAdminWrapper userAdminWrapper; - - @Execute - public Object execute() { - NewGroupWizard newGroupWizard = new NewGroupWizard(); - newGroupWizard.setWindowTitle("Group creation"); - WizardDialog dialog = new WizardDialog(Display.getCurrent().getActiveShell(), newGroupWizard); - dialog.open(); - return null; - } - - private class NewGroupWizard extends Wizard { - - // Pages - private MainGroupInfoWizardPage mainGroupInfo; - - // UI fields - private Text dNameTxt, commonNameTxt, descriptionTxt; - private Combo baseDnCmb; - - public NewGroupWizard() { - } - - @Override - public void addPages() { - mainGroupInfo = new MainGroupInfoWizardPage(); - addPage(mainGroupInfo); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Override - public boolean performFinish() { - if (!canFinish()) - return false; - String commonName = commonNameTxt.getText(); - try { - userAdminWrapper.beginTransactionIfNeeded(); - String dn = getDn(commonName); - Group group = (Group) userAdminWrapper.getUserAdmin().createRole(dn, Role.GROUP); - Dictionary props = group.getProperties(); - String descStr = descriptionTxt.getText(); - if (EclipseUiUtils.notEmpty(descStr)) - props.put(LdapAttrs.description.name(), descStr); - userAdminWrapper.commitOrNotifyTransactionStateChange(); - userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CREATED, group)); - return true; - } catch (Exception e) { - ErrorFeedback.show("Cannot create new group " + commonName, e); - return false; - } - } - - private class MainGroupInfoWizardPage extends WizardPage implements FocusListener { - private static final long serialVersionUID = -3150193365151601807L; - - public MainGroupInfoWizardPage() { - super("Main"); - setTitle("General information"); - setMessage("Please choose a domain, provide a common name " + "and a free description"); - } - - @Override - public void createControl(Composite parent) { - Composite bodyCmp = new Composite(parent, SWT.NONE); - setControl(bodyCmp); - bodyCmp.setLayout(new GridLayout(2, false)); - - dNameTxt = EclipseUiUtils.createGridLT(bodyCmp, "Distinguished name"); - dNameTxt.setEnabled(false); - - baseDnCmb = createGridLC(bodyCmp, "Base DN"); - // Initialise before adding the listener to avoid NPE - initialiseDnCmb(baseDnCmb); - baseDnCmb.addFocusListener(this); - - commonNameTxt = EclipseUiUtils.createGridLT(bodyCmp, "Common name"); - commonNameTxt.addFocusListener(this); - - Label descLbl = new Label(bodyCmp, SWT.LEAD); - descLbl.setText("Description"); - descLbl.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false)); - descriptionTxt = new Text(bodyCmp, SWT.LEAD | SWT.MULTI | SWT.WRAP | SWT.BORDER); - descriptionTxt.setLayoutData(EclipseUiUtils.fillAll()); - descriptionTxt.addFocusListener(this); - - // Initialize buttons - setPageComplete(false); - getContainer().updateButtons(); - } - - @Override - public void focusLost(FocusEvent event) { - String name = commonNameTxt.getText(); - if (EclipseUiUtils.isEmpty(name)) - dNameTxt.setText(""); - else - dNameTxt.setText(getDn(name)); - - String message = checkComplete(); - if (message != null) { - setMessage(message, WizardPage.ERROR); - setPageComplete(false); - } else { - setMessage("Complete", WizardPage.INFORMATION); - setPageComplete(true); - } - getContainer().updateButtons(); - } - - @Override - public void focusGained(FocusEvent event) { - } - - /** @return the error message or null if complete */ - protected String checkComplete() { - String name = commonNameTxt.getText(); - - if (name.trim().equals("")) - return "Common name must not be empty"; - Role role = userAdminWrapper.getUserAdmin().getRole(getDn(name)); - if (role != null) - return "Group " + name + " already exists"; - return null; - } - - @Override - public void setVisible(boolean visible) { - super.setVisible(visible); - if (visible) - if (baseDnCmb.getSelectionIndex() == -1) - baseDnCmb.setFocus(); - else - commonNameTxt.setFocus(); - } - } - - private Map getDns() { - return userAdminWrapper.getKnownBaseDns(true); - } - - private String getDn(String cn) { - Map dns = getDns(); - String bdn = baseDnCmb.getText(); - if (EclipseUiUtils.notEmpty(bdn)) { - Dictionary props = DirectoryConf.uriAsProperties(dns.get(bdn)); - String dn = LdapAttrs.cn.name() + "=" + cn + "," + DirectoryConf.groupBase.getValue(props) + "," + bdn; - return dn; - } - return null; - } - - private void initialiseDnCmb(Combo combo) { - Map dns = userAdminWrapper.getKnownBaseDns(true); - if (dns.isEmpty()) - throw new CmsException("No writable base dn found. Cannot create group"); - combo.setItems(dns.keySet().toArray(new String[0])); - if (dns.size() == 1) - combo.select(0); - } - } - - private Combo createGridLC(Composite parent, String label) { - Label lbl = new Label(parent, SWT.LEAD); - lbl.setText(label); - lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); - Combo combo = new Combo(parent, SWT.LEAD | SWT.BORDER | SWT.READ_ONLY); - combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); - return combo; - } - - /* DEPENDENCY INJECTION */ - public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { - this.userAdminWrapper = userAdminWrapper; - } -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/NewUser.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/NewUser.java deleted file mode 100644 index 40a4460..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/NewUser.java +++ /dev/null @@ -1,287 +0,0 @@ -package org.argeo.cms.e4.users.handlers; - -import java.util.Dictionary; -import java.util.List; -import java.util.Map; - -import javax.inject.Inject; -import javax.naming.InvalidNameException; -import javax.naming.ldap.LdapName; -import javax.naming.ldap.Rdn; - -import org.argeo.cms.auth.UserAdminUtils; -import org.argeo.cms.e4.users.UiAdminUtils; -import org.argeo.cms.e4.users.UserAdminWrapper; -import org.argeo.cms.swt.CmsException; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.dialogs.ErrorFeedback; -import org.argeo.util.directory.DirectoryConf; -import org.argeo.util.naming.LdapAttrs; -import org.eclipse.e4.core.di.annotations.Execute; -import org.eclipse.jface.wizard.Wizard; -import org.eclipse.jface.wizard.WizardDialog; -import org.eclipse.jface.wizard.WizardPage; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Combo; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdminEvent; - -/** Open a wizard that enables creation of a new user. */ -public class NewUser { - // private final static Log log = LogFactory.getLog(NewUser.class); - // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".newUser"; - - /* DEPENDENCY INJECTION */ - @Inject - private UserAdminWrapper userAdminWrapper; - - @Execute - public Object execute() { - NewUserWizard newUserWizard = new NewUserWizard(); - newUserWizard.setWindowTitle("User creation"); - WizardDialog dialog = new WizardDialog(Display.getCurrent().getActiveShell(), newUserWizard); - dialog.open(); - return null; - } - - private class NewUserWizard extends Wizard { - - // pages - private MainUserInfoWizardPage mainUserInfo; - - // End user fields - private Text dNameTxt, usernameTxt, firstNameTxt, lastNameTxt, primaryMailTxt, pwd1Txt, pwd2Txt; - private Combo baseDnCmb; - - public NewUserWizard() { - - } - - @Override - public void addPages() { - mainUserInfo = new MainUserInfoWizardPage(); - addPage(mainUserInfo); - String message = "Default wizard that also eases user creation tests:\n " - + "Mail and last name are automatically " - + "generated form the uid. Password are defauted to 'demo'."; - mainUserInfo.setMessage(message, WizardPage.WARNING); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Override - public boolean performFinish() { - if (!canFinish()) - return false; - String username = mainUserInfo.getUsername(); - userAdminWrapper.beginTransactionIfNeeded(); - try { - User user = (User) userAdminWrapper.getUserAdmin().createRole(getDn(username), Role.USER); - - Dictionary props = user.getProperties(); - - String lastNameStr = lastNameTxt.getText(); - if (EclipseUiUtils.notEmpty(lastNameStr)) - props.put(LdapAttrs.sn.name(), lastNameStr); - - String firstNameStr = firstNameTxt.getText(); - if (EclipseUiUtils.notEmpty(firstNameStr)) - props.put(LdapAttrs.givenName.name(), firstNameStr); - - String cn = UserAdminUtils.buildDefaultCn(firstNameStr, lastNameStr); - if (EclipseUiUtils.notEmpty(cn)) - props.put(LdapAttrs.cn.name(), cn); - - String mailStr = primaryMailTxt.getText(); - if (EclipseUiUtils.notEmpty(mailStr)) - props.put(LdapAttrs.mail.name(), mailStr); - - char[] password = mainUserInfo.getPassword(); - user.getCredentials().put(null, password); - userAdminWrapper.commitOrNotifyTransactionStateChange(); - userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CREATED, user)); - return true; - } catch (Exception e) { - ErrorFeedback.show("Cannot create new user " + username, e); - return false; - } - } - - private class MainUserInfoWizardPage extends WizardPage implements ModifyListener { - private static final long serialVersionUID = -3150193365151601807L; - - public MainUserInfoWizardPage() { - super("Main"); - setTitle("Required Information"); - } - - @Override - public void createControl(Composite parent) { - Composite composite = new Composite(parent, SWT.NONE); - composite.setLayout(new GridLayout(2, false)); - dNameTxt = EclipseUiUtils.createGridLT(composite, "Distinguished name", this); - dNameTxt.setEnabled(false); - - baseDnCmb = createGridLC(composite, "Base DN"); - initialiseDnCmb(baseDnCmb); - baseDnCmb.addModifyListener(this); - baseDnCmb.addModifyListener(new ModifyListener() { - private static final long serialVersionUID = -1435351236582736843L; - - @Override - public void modifyText(ModifyEvent event) { - String name = usernameTxt.getText(); - dNameTxt.setText(getDn(name)); - } - }); - - usernameTxt = EclipseUiUtils.createGridLT(composite, "Local ID", this); - usernameTxt.addModifyListener(new ModifyListener() { - private static final long serialVersionUID = -1435351236582736843L; - - @Override - public void modifyText(ModifyEvent event) { - String name = usernameTxt.getText(); - if (name.trim().equals("")) { - dNameTxt.setText(""); - lastNameTxt.setText(""); - primaryMailTxt.setText(""); - pwd1Txt.setText(""); - pwd2Txt.setText(""); - } else { - dNameTxt.setText(getDn(name)); - lastNameTxt.setText(name.toUpperCase()); - primaryMailTxt.setText(getMail(name)); - pwd1Txt.setText("demo"); - pwd2Txt.setText("demo"); - } - } - }); - - primaryMailTxt = EclipseUiUtils.createGridLT(composite, "Email", this); - firstNameTxt = EclipseUiUtils.createGridLT(composite, "First name", this); - lastNameTxt = EclipseUiUtils.createGridLT(composite, "Last name", this); - pwd1Txt = EclipseUiUtils.createGridLP(composite, "Password", this); - pwd2Txt = EclipseUiUtils.createGridLP(composite, "Repeat password", this); - setControl(composite); - - // Initialize buttons - setPageComplete(false); - getContainer().updateButtons(); - } - - @Override - public void modifyText(ModifyEvent event) { - String message = checkComplete(); - if (message != null) { - setMessage(message, WizardPage.ERROR); - setPageComplete(false); - } else { - setMessage("Complete", WizardPage.INFORMATION); - setPageComplete(true); - } - getContainer().updateButtons(); - } - - /** @return error message or null if complete */ - protected String checkComplete() { - String name = usernameTxt.getText(); - - if (name.trim().equals("")) - return "User name must not be empty"; - Role role = userAdminWrapper.getUserAdmin().getRole(getDn(name)); - if (role != null) - return "User " + name + " already exists"; - if (!primaryMailTxt.getText().matches(UiAdminUtils.EMAIL_PATTERN)) - return "Not a valid email address"; - if (lastNameTxt.getText().trim().equals("")) - return "Specify a last name"; - if (pwd1Txt.getText().trim().equals("")) - return "Specify a password"; - if (pwd2Txt.getText().trim().equals("")) - return "Repeat the password"; - if (!pwd2Txt.getText().equals(pwd1Txt.getText())) - return "Passwords are different"; - return null; - } - - @Override - public void setVisible(boolean visible) { - super.setVisible(visible); - if (visible) - if (baseDnCmb.getSelectionIndex() == -1) - baseDnCmb.setFocus(); - else - usernameTxt.setFocus(); - } - - public String getUsername() { - return usernameTxt.getText(); - } - - public char[] getPassword() { - return pwd1Txt.getTextChars(); - } - - } - - private Map getDns() { - return userAdminWrapper.getKnownBaseDns(true); - } - - private String getDn(String uid) { - Map dns = getDns(); - String bdn = baseDnCmb.getText(); - if (EclipseUiUtils.notEmpty(bdn)) { - Dictionary props = DirectoryConf.uriAsProperties(dns.get(bdn)); - String dn = LdapAttrs.uid.name() + "=" + uid + "," + DirectoryConf.userBase.getValue(props) + "," + bdn; - return dn; - } - return null; - } - - private void initialiseDnCmb(Combo combo) { - Map dns = userAdminWrapper.getKnownBaseDns(true); - if (dns.isEmpty()) - throw new CmsException("No writable base dn found. Cannot create user"); - combo.setItems(dns.keySet().toArray(new String[0])); - if (dns.size() == 1) - combo.select(0); - } - - private String getMail(String username) { - if (baseDnCmb.getSelectionIndex() == -1) - return null; - String baseDn = baseDnCmb.getText(); - try { - LdapName name = new LdapName(baseDn); - List rdns = name.getRdns(); - return username + "@" + (String) rdns.get(1).getValue() + '.' + (String) rdns.get(0).getValue(); - } catch (InvalidNameException e) { - throw new CmsException("Unable to generate mail for " + username + " with base dn " + baseDn, e); - } - } - } - - private Combo createGridLC(Composite parent, String label) { - Label lbl = new Label(parent, SWT.LEAD); - lbl.setText(label); - lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); - Combo combo = new Combo(parent, SWT.LEAD | SWT.BORDER | SWT.READ_ONLY); - combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); - return combo; - } - - /* DEPENDENCY INJECTION */ - public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { - this.userAdminWrapper = userAdminWrapper; - } -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/package-info.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/package-info.java deleted file mode 100644 index cf3db1d..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Users management handlers. */ -package org.argeo.cms.e4.users.handlers; \ No newline at end of file diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/package-info.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/package-info.java deleted file mode 100644 index c6f14b0..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Users management perspective. */ -package org.argeo.cms.e4.users; \ No newline at end of file diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/CommonNameLP.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/CommonNameLP.java deleted file mode 100644 index 2d8db67..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/CommonNameLP.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.argeo.cms.e4.users.providers; - -import org.argeo.cms.auth.UserAdminUtils; -import org.argeo.util.naming.LdapAttrs; -import org.osgi.service.useradmin.User; - -/** Simply declare a label provider that returns the common name of a user */ -public class CommonNameLP extends UserAdminAbstractLP { - private static final long serialVersionUID = 5256703081044911941L; - - @Override - public String getText(User user) { - return UserAdminUtils.getProperty(user, LdapAttrs.cn.name()); - } - - @Override - public String getToolTipText(Object element) { - return UserAdminUtils.getProperty((User) element, LdapAttrs.DN); - } - -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/DomainNameLP.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/DomainNameLP.java deleted file mode 100644 index e23729d..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/DomainNameLP.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.argeo.cms.e4.users.providers; - -import org.argeo.cms.auth.UserAdminUtils; -import org.osgi.service.useradmin.User; - -/** The human friendly domain name for the corresponding user. */ -public class DomainNameLP extends UserAdminAbstractLP { - private static final long serialVersionUID = 5256703081044911941L; - - @Override - public String getText(User user) { - return UserAdminUtils.getDomainName(user); - } -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/MailLP.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/MailLP.java deleted file mode 100644 index 52d3b85..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/MailLP.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.argeo.cms.e4.users.providers; - -import org.argeo.cms.auth.UserAdminUtils; -import org.argeo.util.naming.LdapAttrs; -import org.osgi.service.useradmin.User; - -/** Simply declare a label provider that returns the Primary Mail of a user */ -public class MailLP extends UserAdminAbstractLP { - private static final long serialVersionUID = 8329764452141982707L; - - @Override - public String getText(User user) { - return UserAdminUtils.getProperty(user, LdapAttrs.mail.name()); - } -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/RoleIconLP.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/RoleIconLP.java deleted file mode 100644 index 8e12eed..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/RoleIconLP.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.argeo.cms.e4.users.providers; - -import org.argeo.api.cms.CmsContext; -import org.argeo.api.cms.CmsConstants; -import org.argeo.cms.auth.UserAdminUtils; -import org.argeo.cms.e4.users.SecurityAdminImages; -import org.argeo.util.naming.LdapAttrs; -import org.eclipse.swt.graphics.Image; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; - -/** Provide a bundle specific image depending on the current user type */ -public class RoleIconLP extends UserAdminAbstractLP { - private static final long serialVersionUID = 6550449442061090388L; - - @Override - public String getText(User user) { - return ""; - } - - @Override - public Image getImage(Object element) { - User user = (User) element; - String dn = user.getName(); - if (dn.endsWith(CmsConstants.SYSTEM_ROLES_BASEDN)) - return SecurityAdminImages.ICON_ROLE; - else if (user.getType() == Role.GROUP) { - String businessCategory = UserAdminUtils.getProperty(user, LdapAttrs.businessCategory); - if (businessCategory != null && businessCategory.equals(CmsContext.WORKGROUP)) - return SecurityAdminImages.ICON_WORKGROUP; - return SecurityAdminImages.ICON_GROUP; - } else - return SecurityAdminImages.ICON_USER; - } -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/UserAdminAbstractLP.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/UserAdminAbstractLP.java deleted file mode 100644 index 29873db..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/UserAdminAbstractLP.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.argeo.cms.e4.users.providers; - -import javax.naming.InvalidNameException; -import javax.naming.ldap.LdapName; - -import org.argeo.cms.auth.UserAdminUtils; -import org.argeo.cms.swt.CmsException; -import org.eclipse.jface.resource.JFaceResources; -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Font; -import org.eclipse.swt.widgets.Display; -import org.osgi.service.useradmin.User; - -/** - * Utility class that add font modifications to a column label provider - * depending on the given user properties - */ -public abstract class UserAdminAbstractLP extends ColumnLabelProvider { - private static final long serialVersionUID = 137336765024922368L; - - // private Font italic; - private Font bold; - - @Override - public Font getFont(Object element) { - // Self as bold - try { - LdapName selfUserName = UserAdminUtils.getCurrentUserLdapName(); - String userName = ((User) element).getName(); - LdapName userLdapName = new LdapName(userName); - if (userLdapName.equals(selfUserName)) { - if (bold == null) - bold = JFaceResources.getFontRegistry() - .defaultFontDescriptor().setStyle(SWT.BOLD) - .createFont(Display.getCurrent()); - return bold; - } - } catch (InvalidNameException e) { - throw new CmsException("cannot parse dn for " + element, e); - } - - // Disabled as Italic - // Node userProfile = (Node) elem; - // if (!userProfile.getProperty(ARGEO_ENABLED).getBoolean()) - // return italic; - - return null; - // return super.getFont(element); - } - - @Override - public String getText(Object element) { - User user = (User) element; - return getText(user); - } - - public void setDisplay(Display display) { - // italic = JFaceResources.getFontRegistry().defaultFontDescriptor() - // .setStyle(SWT.ITALIC).createFont(display); - bold = JFaceResources.getFontRegistry().defaultFontDescriptor() - .setStyle(SWT.BOLD).createFont(Display.getCurrent()); - } - - public abstract String getText(User user); -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/UserDragListener.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/UserDragListener.java deleted file mode 100644 index 56a2624..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/UserDragListener.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.argeo.cms.e4.users.providers; - -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.swt.dnd.DragSourceEvent; -import org.eclipse.swt.dnd.DragSourceListener; -import org.osgi.service.useradmin.User; - -/** Default drag listener to modify group and users via the UI */ -public class UserDragListener implements DragSourceListener { - private static final long serialVersionUID = -2074337775033781454L; - private final Viewer viewer; - - public UserDragListener(Viewer viewer) { - this.viewer = viewer; - } - - public void dragStart(DragSourceEvent event) { - // TODO implement finer checks - IStructuredSelection selection = (IStructuredSelection) viewer - .getSelection(); - if (selection.isEmpty() || selection.size() > 1) - event.doit = false; - else - event.doit = true; - } - - public void dragSetData(DragSourceEvent event) { - // TODO Support multiple selection - Object obj = ((IStructuredSelection) viewer.getSelection()) - .getFirstElement(); - if (obj != null) { - User user = (User) obj; - event.data = user.getName(); - } - } - - public void dragFinished(DragSourceEvent event) { - } -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/UserFilter.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/UserFilter.java deleted file mode 100644 index 7a7bfbf..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/UserFilter.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.argeo.cms.e4.users.providers; - -import static org.argeo.eclipse.ui.EclipseUiUtils.notEmpty; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.cms.auth.UserAdminUtils; -import org.argeo.util.naming.LdapAttrs; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.jface.viewers.ViewerFilter; -import org.osgi.service.useradmin.User; - -/** - * Filter user list using JFace mechanism on the client (yet on the server) side - * rather than having the UserAdmin to process the search - */ -public class UserFilter extends ViewerFilter { - private static final long serialVersionUID = 5082509381672880568L; - - private String searchString; - private boolean showSystemRole = true; - - private final String[] knownProps = { LdapAttrs.DN, LdapAttrs.cn.name(), LdapAttrs.givenName.name(), - LdapAttrs.sn.name(), LdapAttrs.uid.name(), LdapAttrs.description.name(), LdapAttrs.mail.name() }; - - public void setSearchText(String s) { - // ensure that the value can be used for matching - if (notEmpty(s)) - searchString = ".*" + s.toLowerCase() + ".*"; - else - searchString = ".*"; - } - - public void setShowSystemRole(boolean showSystemRole) { - this.showSystemRole = showSystemRole; - } - - @Override - public boolean select(Viewer viewer, Object parentElement, Object element) { - User user = (User) element; - if (!showSystemRole && user.getName().matches(".*(" + CmsConstants.SYSTEM_ROLES_BASEDN + ")")) - // UserAdminUtils.getProperty(user, LdifName.dn.name()) - // .toLowerCase().endsWith(AuthConstants.ROLES_BASEDN)) - return false; - - if (searchString == null || searchString.length() == 0) - return true; - - if (user.getName().matches(searchString)) - return true; - - for (String key : knownProps) { - String currVal = UserAdminUtils.getProperty(user, key); - if (notEmpty(currVal) && currVal.toLowerCase().matches(searchString)) - return true; - } - return false; - } -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/UserNameLP.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/UserNameLP.java deleted file mode 100644 index 3cd00eb..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/UserNameLP.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.argeo.cms.e4.users.providers; - -import org.osgi.service.useradmin.User; - -/** Simply declare a label provider that returns the username of a user */ -public class UserNameLP extends UserAdminAbstractLP { - private static final long serialVersionUID = 6550449442061090388L; - - @Override - public String getText(User user) { - return user.getName(); - } -} diff --git a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/package-info.java b/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/package-info.java deleted file mode 100644 index 33bef8d..0000000 --- a/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Users management content providers. */ -package org.argeo.cms.e4.users.providers; \ No newline at end of file diff --git a/org.argeo.cms.jcr.ui/.classpath b/org.argeo.cms.jcr.ui/.classpath deleted file mode 100644 index 81fe078..0000000 --- a/org.argeo.cms.jcr.ui/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/org.argeo.cms.jcr.ui/.project b/org.argeo.cms.jcr.ui/.project deleted file mode 100644 index 645296b..0000000 --- a/org.argeo.cms.jcr.ui/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - org.argeo.cms.jcr.ui - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/org.argeo.cms.jcr.ui/META-INF/.gitignore b/org.argeo.cms.jcr.ui/META-INF/.gitignore deleted file mode 100644 index 4854a41..0000000 --- a/org.argeo.cms.jcr.ui/META-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/MANIFEST.MF diff --git a/org.argeo.cms.jcr.ui/bnd.bnd b/org.argeo.cms.jcr.ui/bnd.bnd deleted file mode 100644 index c3c609c..0000000 --- a/org.argeo.cms.jcr.ui/bnd.bnd +++ /dev/null @@ -1,23 +0,0 @@ -Bundle-Activator: org.argeo.cms.ui.internal.Activator -Bundle-ActivationPolicy: lazy - -Import-Package: org.eclipse.swt,\ -org.eclipse.jface.window,\ -org.eclipse.core.commands,\ -javax.jcr.security,\ -org.eclipse.rap.fileupload;version="[2.1,4)",\ -org.eclipse.rap.rwt;version="[2.1,4)",\ -org.eclipse.rap.rwt.application;version="[2.1,4)",\ -org.eclipse.rap.rwt.client;version="[2.1,4)",\ -org.eclipse.rap.rwt.client.service;version="[2.1,4)",\ -org.eclipse.rap.rwt.service;version="[2.1,4)",\ -org.eclipse.rap.rwt.widgets;version="[2.1,4)",\ -org.osgi.*;version=0.0.0,\ -javax.servlet.*;version="[3,5)",\ -* - -## TODO: in order to enable single sourcing, we have introduced dummy RAP packages -# in the RCP specific bundle org.argeo.eclipse.ui.rap. -# this was working with RAP 2.x but since we upgrade Rap to 3.1.x, -# there is a version range issue cause org.argeo.eclipse.ui.rap bundle is still in 2.x -# We enable large RAP version range as a workaround that must be fixed \ No newline at end of file diff --git a/org.argeo.cms.jcr.ui/build.properties b/org.argeo.cms.jcr.ui/build.properties deleted file mode 100644 index c6baffa..0000000 --- a/org.argeo.cms.jcr.ui/build.properties +++ /dev/null @@ -1,5 +0,0 @@ -source.. = src/ -output.. = bin/ -bin.includes = META-INF/,\ - .,\ - icons/ diff --git a/org.argeo.cms.jcr.ui/icons/loading.gif b/org.argeo.cms.jcr.ui/icons/loading.gif deleted file mode 100644 index 3288d10..0000000 Binary files a/org.argeo.cms.jcr.ui/icons/loading.gif and /dev/null differ diff --git a/org.argeo.cms.jcr.ui/icons/noPic-goldenRatio-640px.png b/org.argeo.cms.jcr.ui/icons/noPic-goldenRatio-640px.png deleted file mode 100644 index 0396506..0000000 Binary files a/org.argeo.cms.jcr.ui/icons/noPic-goldenRatio-640px.png and /dev/null differ diff --git a/org.argeo.cms.jcr.ui/icons/noPic-square-640px.png b/org.argeo.cms.jcr.ui/icons/noPic-square-640px.png deleted file mode 100644 index 8e3abb5..0000000 Binary files a/org.argeo.cms.jcr.ui/icons/noPic-square-640px.png and /dev/null differ diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/AbstractOsgiComposite.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/AbstractOsgiComposite.java deleted file mode 100644 index 4fd1d68..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/AbstractOsgiComposite.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.argeo.cms.e4.maintenance; - -import java.util.Collection; - -import org.argeo.api.cms.CmsLog; -import org.argeo.cms.swt.CmsSwtUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Composite; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.framework.ServiceReference; - -abstract class AbstractOsgiComposite extends Composite { - private static final long serialVersionUID = -4097415973477517137L; - protected final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext(); - protected final CmsLog log = CmsLog.getLog(getClass()); - - public AbstractOsgiComposite(Composite parent, int style) { - super(parent, style); - parent.setLayout(CmsSwtUtils.noSpaceGridLayout()); - setLayout(CmsSwtUtils.noSpaceGridLayout()); - setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); - initUi(style); - } - - protected abstract void initUi(int style); - - protected T getService(Class clazz) { - return bc.getService(bc.getServiceReference(clazz)); - } - - protected Collection> getServiceReferences(Class clazz, String filter) { - try { - return bc.getServiceReferences(clazz, filter); - } catch (InvalidSyntaxException e) { - throw new IllegalArgumentException("Filter " + filter + " is invalid", e); - } - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/Browse.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/Browse.java deleted file mode 100644 index a536da0..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/Browse.java +++ /dev/null @@ -1,576 +0,0 @@ -package org.argeo.cms.e4.maintenance; - -import static org.eclipse.swt.SWT.RIGHT; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.LinkedHashMap; - -import javax.jcr.ItemNotFoundException; -import javax.jcr.Node; -import javax.jcr.NodeIterator; -import javax.jcr.Property; -import javax.jcr.PropertyIterator; -import javax.jcr.PropertyType; -import javax.jcr.RepositoryException; -import javax.jcr.Value; - -import org.argeo.api.cms.ux.Cms2DSize; -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.cms.ui.util.CmsLink; -import org.argeo.cms.ui.widgets.EditableImage; -import org.argeo.cms.ui.widgets.Img; -import org.argeo.jcr.JcrUtils; -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.jface.viewers.ILazyContentProvider; -import org.eclipse.jface.viewers.ISelectionChangedListener; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.SelectionChangedEvent; -import org.eclipse.jface.viewers.StructuredSelection; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.jface.viewers.TableViewerColumn; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.ScrolledComposite; -import org.eclipse.swt.events.ControlAdapter; -import org.eclipse.swt.events.ControlEvent; -import org.eclipse.swt.events.KeyEvent; -import org.eclipse.swt.events.KeyListener; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Table; -import org.eclipse.swt.widgets.TableColumn; -import org.eclipse.swt.widgets.Text; - -public class Browse implements CmsUiProvider { - - // Some local constants to experiment. should be cleaned - private final static String BROWSE_PREFIX = "browse#"; - private final static int THUMBNAIL_WIDTH = 400; - private final static int COLUMN_WIDTH = 160; - private DateFormat timeFormatter = new SimpleDateFormat("dd-MM-yyyy', 'HH:mm"); - - // keep a cache of the opened nodes - // Key is the path - private LinkedHashMap browserCols = new LinkedHashMap(); - private Composite nodeDisplayParent; - private Composite colViewer; - private ScrolledComposite scrolledCmp; - private Text parentPathTxt; - private Text filterTxt; - private Node currEdited; - - private String initialPath; - - @Override - public Control createUi(Composite parent, Node context) throws RepositoryException { - if (context == null) - // return null; - throw new CmsException("Context cannot be null"); - GridLayout layout = CmsSwtUtils.noSpaceGridLayout(); - layout.numColumns = 2; - parent.setLayout(layout); - - // Left - Composite leftCmp = new Composite(parent, SWT.NO_FOCUS); - leftCmp.setLayoutData(CmsSwtUtils.fillAll()); - createBrowserPart(leftCmp, context); - - // Right - nodeDisplayParent = new Composite(parent, SWT.NO_FOCUS | SWT.BORDER); - GridData gd = new GridData(SWT.RIGHT, SWT.FILL, false, true); - gd.widthHint = THUMBNAIL_WIDTH; - nodeDisplayParent.setLayoutData(gd); - createNodeView(nodeDisplayParent, context); - - // INIT - setEdited(context); - initialPath = context.getPath(); - - // Workaround we don't yet manage the delete to display parent of the - // initial context node - - return null; - } - - private void createBrowserPart(Composite parent, Node context) throws RepositoryException { - GridLayout layout = CmsSwtUtils.noSpaceGridLayout(); - parent.setLayout(layout); - Composite filterCmp = new Composite(parent, SWT.NO_FOCUS); - filterCmp.setLayoutData(CmsSwtUtils.fillWidth()); - - // top filter - addFilterPanel(filterCmp); - - // scrolled composite - scrolledCmp = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.BORDER | SWT.NO_FOCUS); - scrolledCmp.setLayoutData(CmsSwtUtils.fillAll()); - scrolledCmp.setExpandVertical(true); - scrolledCmp.setExpandHorizontal(true); - scrolledCmp.setShowFocusedControl(true); - - colViewer = new Composite(scrolledCmp, SWT.NO_FOCUS); - scrolledCmp.setContent(colViewer); - scrolledCmp.addControlListener(new ControlAdapter() { - private static final long serialVersionUID = 6589392045145698201L; - - @Override - public void controlResized(ControlEvent e) { - Rectangle r = scrolledCmp.getClientArea(); - scrolledCmp.setMinSize(colViewer.computeSize(SWT.DEFAULT, r.height)); - } - }); - initExplorer(colViewer, context); - } - - private Control initExplorer(Composite parent, Node context) throws RepositoryException { - parent.setLayout(CmsSwtUtils.noSpaceGridLayout()); - createBrowserColumn(parent, context); - return null; - } - - private Control createBrowserColumn(Composite parent, Node context) throws RepositoryException { - // TODO style is not correctly managed. - FilterEntitiesVirtualTable table = new FilterEntitiesVirtualTable(parent, SWT.BORDER | SWT.NO_FOCUS, context); - // CmsUiUtils.style(table, ArgeoOrgStyle.browserColumn.style()); - table.filterList("*"); - table.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, false, true)); - browserCols.put(context.getPath(), table); - return null; - } - - public void addFilterPanel(Composite parent) { - - parent.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(2, false))); - - // Text Area for the filter - parentPathTxt = new Text(parent, SWT.NO_FOCUS); - parentPathTxt.setEditable(false); - filterTxt = new Text(parent, SWT.SEARCH | SWT.ICON_CANCEL); - filterTxt.setMessage("Filter current list"); - filterTxt.setLayoutData(CmsSwtUtils.fillWidth()); - filterTxt.addModifyListener(new ModifyListener() { - private static final long serialVersionUID = 7709303319740056286L; - - public void modifyText(ModifyEvent event) { - modifyFilter(false); - } - }); - - filterTxt.addKeyListener(new KeyListener() { - private static final long serialVersionUID = -4523394262771183968L; - - @Override - public void keyReleased(KeyEvent e) { - } - - @Override - public void keyPressed(KeyEvent e) { - boolean shiftPressed = (e.stateMask & SWT.SHIFT) != 0; - // boolean altPressed = (e.stateMask & SWT.ALT) != 0; - FilterEntitiesVirtualTable currTable = null; - if (currEdited != null) { - FilterEntitiesVirtualTable table = browserCols.get(getPath(currEdited)); - if (table != null && !table.isDisposed()) - currTable = table; - } - - try { - if (e.keyCode == SWT.ARROW_DOWN) - currTable.setFocus(); - else if (e.keyCode == SWT.BS) { - if (filterTxt.getText().equals("") - && !(getPath(currEdited).equals("/") || getPath(currEdited).equals(initialPath))) { - setEdited(currEdited.getParent()); - e.doit = false; - filterTxt.setFocus(); - } - } else if (e.keyCode == SWT.TAB && !shiftPressed) { - if (currEdited.getNodes(filterTxt.getText() + "*").getSize() == 1) { - setEdited(currEdited.getNodes(filterTxt.getText() + "*").nextNode()); - } - filterTxt.setFocus(); - e.doit = false; - } - } catch (RepositoryException e1) { - throw new CmsException("Unexpected error in key management for " + currEdited + "with filter " - + filterTxt.getText(), e1); - } - - } - }); - } - - private void setEdited(Node node) { - try { - currEdited = node; - CmsSwtUtils.clear(nodeDisplayParent); - createNodeView(nodeDisplayParent, currEdited); - nodeDisplayParent.layout(); - refreshFilters(node); - refreshBrowser(node); - } catch (RepositoryException re) { - throw new CmsException("Unable to update browser for " + node, re); - } - } - - private void refreshFilters(Node node) throws RepositoryException { - String currNodePath = node.getPath(); - parentPathTxt.setText(currNodePath); - filterTxt.setText(""); - filterTxt.getParent().layout(); - } - - private void refreshBrowser(Node node) throws RepositoryException { - - // Retrieve - String currNodePath = node.getPath(); - String currParPath = ""; - if (!"/".equals(currNodePath)) - currParPath = JcrUtils.parentPath(currNodePath); - if ("".equals(currParPath)) - currParPath = "/"; - - Object[][] colMatrix = new Object[browserCols.size()][2]; - - int i = 0, j = -1, k = -1; - for (String path : browserCols.keySet()) { - colMatrix[i][0] = path; - colMatrix[i][1] = browserCols.get(path); - if (j >= 0 && k < 0 && !currNodePath.equals("/")) { - boolean leaveOpened = path.startsWith(currNodePath); - - // workaround for same name siblings - // fix me weird side effect when we go left or click on anb - // already selected, unfocused node - if (leaveOpened && (path.lastIndexOf("/") == 0 && currNodePath.lastIndexOf("/") == 0 - || JcrUtils.parentPath(path).equals(JcrUtils.parentPath(currNodePath)))) - leaveOpened = JcrUtils.lastPathElement(path).equals(JcrUtils.lastPathElement(currNodePath)); - - if (!leaveOpened) - k = i; - } - if (currParPath.equals(path)) - j = i; - i++; - } - - if (j >= 0 && k >= 0) - // remove useless cols - for (int l = i - 1; l >= k; l--) { - browserCols.remove(colMatrix[l][0]); - ((FilterEntitiesVirtualTable) colMatrix[l][1]).dispose(); - } - - // Remove disposed columns - // TODO investigate and fix the mechanism that leave them there after - // disposal - if (browserCols.containsKey(currNodePath)) { - FilterEntitiesVirtualTable currCol = browserCols.get(currNodePath); - if (currCol.isDisposed()) - browserCols.remove(currNodePath); - } - - if (!browserCols.containsKey(currNodePath)) - createBrowserColumn(colViewer, node); - - colViewer.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(browserCols.size(), false))); - // colViewer.pack(); - colViewer.layout(); - // also resize the scrolled composite - scrolledCmp.layout(); - scrolledCmp.getShowFocusedControl(); - // colViewer.getParent().layout(); - // if (JcrUtils.parentPath(currNodePath).equals(currBrowserKey)) { - // } else { - // } - } - - private void modifyFilter(boolean fromOutside) { - if (!fromOutside) - if (currEdited != null) { - String filter = filterTxt.getText() + "*"; - FilterEntitiesVirtualTable table = browserCols.get(getPath(currEdited)); - if (table != null && !table.isDisposed()) - table.filterList(filter); - } - - } - - private String getPath(Node node) { - try { - return node.getPath(); - } catch (RepositoryException e) { - throw new CmsException("Unable to get path for node " + node, e); - } - } - - private Cms2DSize imageWidth = new Cms2DSize(250, 0); - - /** - * Recreates the content of the box that displays information about the current - * selected node. - */ - private Control createNodeView(Composite parent, Node context) throws RepositoryException { - - parent.setLayout(new GridLayout(2, false)); - - if (isImg(context)) { - EditableImage image = new Img(parent, RIGHT, context, imageWidth); - image.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false, 2, 1)); - } - - // Name and primary type - Label contextL = new Label(parent, SWT.NONE); - CmsSwtUtils.markup(contextL); - contextL.setText("" + context.getName() + ""); - new Label(parent, SWT.NONE).setText(context.getPrimaryNodeType().getName()); - - // Children - for (NodeIterator nIt = context.getNodes(); nIt.hasNext();) { - Node child = nIt.nextNode(); - new CmsLink(child.getName(), BROWSE_PREFIX + child.getPath()).createUi(parent, context); - new Label(parent, SWT.NONE).setText(child.getPrimaryNodeType().getName()); - } - - // Properties - for (PropertyIterator pIt = context.getProperties(); pIt.hasNext();) { - Property property = pIt.nextProperty(); - Label label = new Label(parent, SWT.NONE); - label.setText(property.getName()); - label.setToolTipText(JcrUtils.getPropertyDefinitionAsString(property)); - new Label(parent, SWT.NONE).setText(getPropAsString(property)); - } - - return null; - } - - private boolean isImg(Node node) throws RepositoryException { - // TODO support images - return false; -// return node.hasNode(JCR_CONTENT) && node.isNodeType(CmsTypes.CMS_IMAGE); - } - - private String getPropAsString(Property property) throws RepositoryException { - String result = ""; - if (property.isMultiple()) { - result = getMultiAsString(property, ", "); - } else { - Value value = property.getValue(); - if (value.getType() == PropertyType.BINARY) - result = ""; - else if (value.getType() == PropertyType.DATE) - result = timeFormatter.format(value.getDate().getTime()); - else - result = value.getString(); - } - return result; - } - - private String getMultiAsString(Property property, String separator) throws RepositoryException { - if (separator == null) - separator = "; "; - Value[] values = property.getValues(); - StringBuilder builder = new StringBuilder(); - for (Value val : values) { - String currStr = val.getString(); - if (!"".equals(currStr.trim())) - builder.append(currStr).append(separator); - } - if (builder.lastIndexOf(separator) >= 0) - return builder.substring(0, builder.length() - separator.length()); - else - return builder.toString(); - } - - /** Almost canonical implementation of a table that display entities */ - private class FilterEntitiesVirtualTable extends Composite { - private static final long serialVersionUID = 8798147431706283824L; - - // Context - private Node context; - - // UI Objects - private TableViewer entityViewer; - - // enable management of multiple columns - Node getNode() { - return context; - } - - @Override - public boolean setFocus() { - if (entityViewer.getTable().isDisposed()) - return false; - if (entityViewer.getSelection().isEmpty()) { - Object first = entityViewer.getElementAt(0); - if (first != null) { - entityViewer.setSelection(new StructuredSelection(first), true); - } - } - return entityViewer.getTable().setFocus(); - } - - void filterList(String filter) { - try { - NodeIterator nit = context.getNodes(filter); - refreshFilteredList(nit); - } catch (RepositoryException e) { - throw new CmsException("Unable to filter " + getNode() + " children with filter " + filter, e); - } - - } - - public FilterEntitiesVirtualTable(Composite parent, int style, Node context) { - super(parent, SWT.NO_FOCUS); - this.context = context; - populate(); - } - - protected void populate() { - Composite parent = this; - GridLayout layout = CmsSwtUtils.noSpaceGridLayout(); - - this.setLayout(layout); - createTableViewer(parent); - } - - private void createTableViewer(final Composite parent) { - // the list - // We must limit the size of the table otherwise the full list is - // loaded - // before the layout happens - Composite listCmp = new Composite(parent, SWT.NO_FOCUS); - GridData gd = new GridData(SWT.LEFT, SWT.FILL, false, true); - gd.widthHint = COLUMN_WIDTH; - listCmp.setLayoutData(gd); - listCmp.setLayout(CmsSwtUtils.noSpaceGridLayout()); - - entityViewer = new TableViewer(listCmp, SWT.VIRTUAL | SWT.SINGLE); - Table table = entityViewer.getTable(); - - table.setLayoutData(CmsSwtUtils.fillAll()); - table.setLinesVisible(true); - table.setHeaderVisible(false); - CmsSwtUtils.markup(table); - - CmsSwtUtils.style(table, MaintenanceStyles.BROWSER_COLUMN); - - // first column - TableViewerColumn column = new TableViewerColumn(entityViewer, SWT.NONE); - TableColumn tcol = column.getColumn(); - tcol.setWidth(COLUMN_WIDTH); - tcol.setResizable(true); - column.setLabelProvider(new SimpleNameLP()); - - entityViewer.setContentProvider(new MyLazyCP(entityViewer)); - entityViewer.addSelectionChangedListener(new ISelectionChangedListener() { - - @Override - public void selectionChanged(SelectionChangedEvent event) { - IStructuredSelection selection = (IStructuredSelection) entityViewer.getSelection(); - if (selection.isEmpty()) - return; - else - setEdited((Node) selection.getFirstElement()); - - } - }); - - table.addKeyListener(new KeyListener() { - private static final long serialVersionUID = -330694313896036230L; - - @Override - public void keyReleased(KeyEvent e) { - } - - @Override - public void keyPressed(KeyEvent e) { - - IStructuredSelection selection = (IStructuredSelection) entityViewer.getSelection(); - Node selected = null; - if (!selection.isEmpty()) - selected = ((Node) selection.getFirstElement()); - try { - if (e.keyCode == SWT.ARROW_RIGHT) { - if (selected != null) { - setEdited(selected); - browserCols.get(selected.getPath()).setFocus(); - } - } else if (e.keyCode == SWT.ARROW_LEFT) { - try { - selected = getNode().getParent(); - String newPath = selected.getPath(); // getNode().getParent() - setEdited(selected); - if (browserCols.containsKey(newPath)) - browserCols.get(newPath).setFocus(); - } catch (ItemNotFoundException ie) { - // root silent - } - } - } catch (RepositoryException ie) { - throw new CmsException("Error while managing arrow " + "events in the browser for " + selected, - ie); - } - } - }); - } - - private class MyLazyCP implements ILazyContentProvider { - private static final long serialVersionUID = 1L; - private TableViewer viewer; - private Object[] elements; - - public MyLazyCP(TableViewer viewer) { - this.viewer = viewer; - } - - public void dispose() { - } - - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - // IMPORTANT: don't forget this: an exception will be thrown if - // a selected object is not part of the results anymore. - viewer.setSelection(null); - this.elements = (Object[]) newInput; - } - - public void updateElement(int index) { - viewer.replace(elements[index], index); - } - } - - protected void refreshFilteredList(NodeIterator children) { - Object[] rows = JcrUtils.nodeIteratorToList(children).toArray(); - entityViewer.setInput(rows); - entityViewer.setItemCount(rows.length); - entityViewer.refresh(); - } - - public class SimpleNameLP extends ColumnLabelProvider { - private static final long serialVersionUID = 2465059387875338553L; - - @Override - public String getText(Object element) { - if (element instanceof Node) { - Node curr = ((Node) element); - try { - return curr.getName(); - } catch (RepositoryException e) { - throw new CmsException("Unable to get name for" + curr); - } - } - return super.getText(element); - } - } - } -} \ No newline at end of file diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/ConnectivityDeploymentUi.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/ConnectivityDeploymentUi.java deleted file mode 100644 index 97f3e67..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/ConnectivityDeploymentUi.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.argeo.cms.e4.maintenance; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; -import org.osgi.framework.ServiceReference; -import org.osgi.service.http.HttpService; -import org.osgi.service.useradmin.UserAdmin; - -class ConnectivityDeploymentUi extends AbstractOsgiComposite { - private static final long serialVersionUID = 590221539553514693L; - - public ConnectivityDeploymentUi(Composite parent, int style) { - super(parent, style); - } - - @Override - protected void initUi(int style) { - StringBuffer text = new StringBuffer(); - text.append("Provided Servers
"); - - ServiceReference userAdminRef = bc.getServiceReference(HttpService.class); - if (userAdminRef != null) { - // FIXME use constants - Object httpPort = userAdminRef.getProperty("http.port"); - Object httpsPort = userAdminRef.getProperty("https.port"); - if (httpPort != null) - text.append("http ").append(httpPort).append("
"); - if (httpsPort != null) - text.append("https ").append(httpsPort).append("
"); - - } - - text.append("
"); - text.append("Referenced Servers
"); - - Label label = new Label(this, SWT.NONE); - label.setData(new GridData(SWT.FILL, SWT.FILL, false, false)); - CmsSwtUtils.markup(label); - label.setText(text.toString()); - } - - protected boolean isDeployed() { - return bc.getServiceReference(UserAdmin.class) != null; - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/DataDeploymentUi.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/DataDeploymentUi.java deleted file mode 100644 index ef95bde..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/DataDeploymentUi.java +++ /dev/null @@ -1,139 +0,0 @@ -package org.argeo.cms.e4.maintenance; - -import java.io.File; -import java.io.IOException; -import java.nio.file.FileStore; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Collection; - -import org.apache.jackrabbit.core.RepositoryContext; -import org.apache.jackrabbit.core.config.RepositoryConfig; -import org.argeo.api.cms.CmsConstants; -import org.argeo.cms.swt.CmsSwtUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; -import org.osgi.framework.ServiceReference; - -class DataDeploymentUi extends AbstractOsgiComposite { - private static final long serialVersionUID = 590221539553514693L; - - public DataDeploymentUi(Composite parent, int style) { - super(parent, style); - } - - @Override - protected void initUi(int style) { - if (isDeployed()) { - initCurrentUi(this); - } else { - initNewUi(this); - } - } - - private void initNewUi(Composite parent) { -// try { -// ConfigurationAdmin confAdmin = bc.getService(bc.getServiceReference(ConfigurationAdmin.class)); -// Configuration[] confs = confAdmin.listConfigurations( -// "(" + ConfigurationAdmin.SERVICE_FACTORYPID + "=" + NodeConstants.NODE_REPOS_FACTORY_PID + ")"); -// if (confs == null || confs.length == 0) { -// Group buttonGroup = new Group(parent, SWT.NONE); -// buttonGroup.setText("Repository Type"); -// buttonGroup.setLayout(new GridLayout(2, true)); -// buttonGroup.setLayoutData(new GridData(GridData.FILL_VERTICAL)); -// -// SelectionListener selectionListener = new SelectionAdapter() { -// private static final long serialVersionUID = 6247064348421088092L; -// -// public void widgetSelected(SelectionEvent event) { -// Button radio = (Button) event.widget; -// if (!radio.getSelection()) -// return; -// log.debug(event); -// JackrabbitType nodeType = (JackrabbitType) radio.getData(); -// if (log.isDebugEnabled()) -// log.debug(" selected = " + nodeType.name()); -// }; -// }; -// -// for (JackrabbitType nodeType : JackrabbitType.values()) { -// Button radio = new Button(buttonGroup, SWT.RADIO); -// radio.setText(nodeType.name()); -// radio.setData(nodeType); -// if (nodeType.equals(JackrabbitType.localfs)) -// radio.setSelection(true); -// radio.addSelectionListener(selectionListener); -// } -// -// } else if (confs.length == 1) { -// -// } else { -// throw new CmsException("Multiple repos not yet supported"); -// } -// } catch (Exception e) { -// throw new CmsException("Cannot initialize UI", e); -// } - - } - - private void initCurrentUi(Composite parent) { - parent.setLayout(new GridLayout()); - Collection> contexts = getServiceReferences(RepositoryContext.class, - "(" + CmsConstants.CN + "=*)"); - StringBuffer text = new StringBuffer(); - text.append("Jackrabbit Repositories
"); - for (ServiceReference sr : contexts) { - RepositoryContext repositoryContext = bc.getService(sr); - String alias = sr.getProperty(CmsConstants.CN).toString(); - String rootNodeId = repositoryContext.getRootNodeId().toString(); - RepositoryConfig repositoryConfig = repositoryContext.getRepositoryConfig(); - Path repoHomePath = new File(repositoryConfig.getHomeDir()).toPath().toAbsolutePath(); - // TODO check data store - - text.append("" + alias + "
"); - text.append("rootNodeId: " + rootNodeId + "
"); - try { - FileStore fileStore = Files.getFileStore(repoHomePath); - text.append("partition: " + fileStore.toString() + "
"); - text.append( - percentUsed(fileStore) + " used (" + humanReadable(fileStore.getUsableSpace()) + " free)
"); - } catch (IOException e) { - log.error("Cannot check fileStore for " + repoHomePath, e); - } - } - Label label = new Label(parent, SWT.NONE); - label.setData(new GridData(SWT.FILL, SWT.FILL, false, false)); - CmsSwtUtils.markup(label); - label.setText("" + text.toString() + ""); - } - - private String humanReadable(long bytes) { - long mb = bytes / (1024 * 1024); - return mb >= 2048 ? Long.toString(mb / 1024) + " GB" : Long.toString(mb) + " MB"; - } - - private String percentUsed(FileStore fs) throws IOException { - long used = fs.getTotalSpace() - fs.getUnallocatedSpace(); - long percent = used * 100 / fs.getTotalSpace(); - if (log.isTraceEnabled()) { - // output identical to `df -B 1`) - log.trace(fs.getTotalSpace() + "," + used + "," + fs.getUsableSpace()); - } - String span; - if (percent < 80) - span = ""; - else if (percent < 95) - span = ""; - else - span = ""; - return span + percent + "%"; - } - - protected boolean isDeployed() { - return bc.getServiceReference(RepositoryContext.class) != null; - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/DeploymentEntryPoint.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/DeploymentEntryPoint.java deleted file mode 100644 index 0a28dc5..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/DeploymentEntryPoint.java +++ /dev/null @@ -1,95 +0,0 @@ -package org.argeo.cms.e4.maintenance; - -import java.util.GregorianCalendar; -import java.util.TimeZone; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.api.cms.CmsContext; -import org.argeo.api.cms.CmsDeployment; -import org.argeo.api.cms.CmsState; -import org.argeo.cms.swt.CmsSwtUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.FillLayout; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Group; -import org.eclipse.swt.widgets.Label; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.ServiceReference; - -class DeploymentEntryPoint { - private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext(); - - protected void createContents(Composite parent) { - // FIXME manage authentication if needed - // if (!CurrentUser.roles().contains(AuthConstants.ROLE_ADMIN)) - // return; - - // parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - if (isDesktop()) { - parent.setLayout(new GridLayout(2, true)); - } else { - // TODO add scrolling - parent.setLayout(new GridLayout(1, true)); - } - - initHighLevelSummary(parent); - - Group securityGroup = createHighLevelGroup(parent, "Security"); - securityGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); - new SecurityDeploymentUi(securityGroup, SWT.NONE); - - Group dataGroup = createHighLevelGroup(parent, "Data"); - dataGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); - new DataDeploymentUi(dataGroup, SWT.NONE); - - Group logGroup = createHighLevelGroup(parent, "Notifications"); - logGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true)); - new LogDeploymentUi(logGroup, SWT.NONE); - - Group connectivityGroup = createHighLevelGroup(parent, "Connectivity"); - new ConnectivityDeploymentUi(connectivityGroup, SWT.NONE); - connectivityGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true)); - - } - - private void initHighLevelSummary(Composite parent) { - Composite composite = new Composite(parent, SWT.NONE); - GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false); - if (isDesktop()) - gridData.horizontalSpan = 3; - composite.setLayoutData(gridData); - composite.setLayout(new FillLayout()); - - ServiceReference nodeStateRef = bc.getServiceReference(CmsState.class); - if (nodeStateRef == null) - throw new IllegalStateException("No CMS state available"); - CmsState nodeState = bc.getService(nodeStateRef); - ServiceReference nodeDeploymentRef = bc.getServiceReference(CmsContext.class); - Label label = new Label(composite, SWT.WRAP); - CmsSwtUtils.markup(label); - if (nodeDeploymentRef == null) { - label.setText("Not yet deployed on, please configure below."); - } else { - Object stateUuid = nodeStateRef.getProperty(CmsConstants.CN); - CmsContext nodeDeployment = bc.getService(nodeDeploymentRef); - GregorianCalendar calendar = new GregorianCalendar(); - calendar.setTimeInMillis(nodeDeployment.getAvailableSince()); - calendar.setTimeZone(TimeZone.getDefault()); - label.setText("Deployment state " + stateUuid + ", available since " + calendar.getTime() + ""); - } - } - - private static Group createHighLevelGroup(Composite parent, String text) { - Group group = new Group(parent, SWT.NONE); - group.setText(text); - CmsSwtUtils.markup(group); - return group; - } - - private boolean isDesktop() { - return true; - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/LogDeploymentUi.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/LogDeploymentUi.java deleted file mode 100644 index fa5d3da..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/LogDeploymentUi.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.argeo.cms.e4.maintenance; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Enumeration; -import java.util.GregorianCalendar; -import java.util.TimeZone; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Text; -import org.osgi.service.log.LogEntry; -import org.osgi.service.log.LogListener; -import org.osgi.service.log.LogReaderService; - -class LogDeploymentUi extends AbstractOsgiComposite implements LogListener { - private static final long serialVersionUID = 590221539553514693L; - - private DateFormat dateFormat = new SimpleDateFormat("MMdd HH:mm"); - - private Display display; - private Text logDisplay; - - public LogDeploymentUi(Composite parent, int style) { - super(parent, style); - } - - @Override - protected void initUi(int style) { - LogReaderService logReader = getService(LogReaderService.class); - // FIXME use server push - // logReader.addLogListener(this); - this.display = getDisplay(); - this.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - logDisplay = new Text(this, SWT.WRAP | SWT.MULTI | SWT.READ_ONLY); - logDisplay.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - CmsSwtUtils.markup(logDisplay); - Enumeration logEntries = (Enumeration) logReader.getLog(); - while (logEntries.hasMoreElements()) - logDisplay.append(printEntry(logEntries.nextElement())); - } - - private String printEntry(LogEntry entry) { - StringBuilder sb = new StringBuilder(); - GregorianCalendar calendar = new GregorianCalendar(TimeZone.getDefault()); - calendar.setTimeInMillis(entry.getTime()); - sb.append(dateFormat.format(calendar.getTime())).append(' '); - sb.append(entry.getMessage()); - sb.append('\n'); - return sb.toString(); - } - - @Override - public void logged(LogEntry entry) { - if (display.isDisposed()) - return; - display.asyncExec(() -> { - if (logDisplay.isDisposed()) - return; - logDisplay.append(printEntry(entry)); - }); - display.wake(); - } - - // @Override - // public void dispose() { - // super.dispose(); - // getService(LogReaderService.class).removeLogListener(this); - // } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/MaintenanceStyles.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/MaintenanceStyles.java deleted file mode 100644 index df1be51..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/MaintenanceStyles.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.argeo.cms.e4.maintenance; - -/** Specific styles used by the various maintenance pages . */ -public interface MaintenanceStyles { - // General - public final static String PREFIX = "maintenance_"; - - // Browser - public final static String BROWSER_COLUMN = "browser_column"; - } diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/NonAdminPage.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/NonAdminPage.java deleted file mode 100644 index cb38ce8..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/NonAdminPage.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.argeo.cms.e4.maintenance; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ui.CmsUiProvider; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; - -public class NonAdminPage implements CmsUiProvider{ - - @Override - public Control createUi(Composite parent, Node context) - throws RepositoryException { - Composite body = new Composite(parent, SWT.NO_FOCUS); - body.setLayoutData(CmsSwtUtils.fillAll()); - body.setLayout(new GridLayout()); - Label label = new Label(body, SWT.NONE); - label.setText("You should be an admin to perform maintenance operations. " - + "Are you sure you are logged in?"); - label.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true)); - return null; - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/SecurityDeploymentUi.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/SecurityDeploymentUi.java deleted file mode 100644 index 3492c54..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/SecurityDeploymentUi.java +++ /dev/null @@ -1,85 +0,0 @@ -package org.argeo.cms.e4.maintenance; - -import java.net.URI; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.framework.ServiceReference; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.UserAdmin; - -class SecurityDeploymentUi extends AbstractOsgiComposite { - private static final long serialVersionUID = 590221539553514693L; - - public SecurityDeploymentUi(Composite parent, int style) { - super(parent, style); - } - - @Override - protected void initUi(int style) { - if (isDeployed()) { - initCurrentUi(this); - } else { - initNewUi(this); - } - } - - private void initNewUi(Composite parent) { - new Label(parent, SWT.NONE).setText("Security is not configured"); - } - - private void initCurrentUi(Composite parent) { - ServiceReference userAdminRef = bc.getServiceReference(UserAdmin.class); - UserAdmin userAdmin = bc.getService(userAdminRef); - StringBuffer text = new StringBuffer(); - text.append("Domains
"); - domains: for (String key : userAdminRef.getPropertyKeys()) { - if (!key.startsWith("/")) - continue domains; - URI uri; - try { - uri = new URI(key); - } catch (Exception e) { - // ignore non URI keys - continue domains; - } - - String rootDn = uri.getPath().substring(1, uri.getPath().length()); - // FIXME make reading query options more robust, using utils - boolean readOnly = uri.getQuery().equals("readOnly=true"); - if (readOnly) - text.append(""); - else - text.append(""); - - text.append(rootDn); - text.append("
"); - try { - Role[] roles = userAdmin.getRoles("(dn=*," + rootDn + ")"); - long userCount = 0; - long groupCount = 0; - for (Role role : roles) { - if (role.getType() == Role.USER) - userCount++; - else - groupCount++; - } - text.append(" " + userCount + " users, " + groupCount +" groups.
"); - } catch (InvalidSyntaxException e) { - log.error("Invalid syntax", e); - } - } - Label label = new Label(parent, SWT.NONE); - label.setData(new GridData(SWT.FILL, SWT.FILL, false, false)); - CmsSwtUtils.markup(label); - label.setText(text.toString()); - } - - protected boolean isDeployed() { - return bc.getServiceReference(UserAdmin.class) != null; - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/package-info.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/package-info.java deleted file mode 100644 index e4d2ad4..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Maintenance perspective. */ -package org.argeo.cms.e4.maintenance; \ No newline at end of file diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/CmsUiConstants.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/CmsUiConstants.java deleted file mode 100644 index fd1dda5..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/CmsUiConstants.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.argeo.cms.ui; - -/** Commons constants */ -@Deprecated -public interface CmsUiConstants { - // DATAKEYS -// public final static String STYLE = EclipseUiConstants.CSS_CLASS; -// public final static String MARKUP = EclipseUiConstants.MARKUP_SUPPORT; - @Deprecated - /* RWT.CUSTOM_ITEM_HEIGHT */ - public final static String ITEM_HEIGHT = "org.eclipse.rap.rwt.customItemHeight"; - - // EVENT DETAILS - @Deprecated - /* RWT.HYPERLINK */ - public final static int HYPERLINK = 1 << 26; - - // STANDARD RESOURCES - public final static String LOADING_IMAGE = "icons/loading.gif"; - - // MISCEALLENEOUS - String DATE_TIME_FORMAT = "dd/MM/yyyy, HH:mm"; -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/CmsUiProvider.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/CmsUiProvider.java deleted file mode 100644 index 5f2377b..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/CmsUiProvider.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.argeo.cms.ui; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.api.acr.Content; -import org.argeo.cms.jcr.acr.JcrContent; -import org.argeo.cms.swt.acr.SwtUiProvider; -import org.argeo.jcr.JcrException; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -/** Stateless factory building an SWT user interface given a JCR context. */ -public interface CmsUiProvider extends SwtUiProvider { - /** - * Initialises a user interface. - * - * @param parent the parent composite - * @param context a context node (holding the JCR underlying session), or null - */ - default Control createUi(Composite parent, Node context) throws RepositoryException { - // does nothing by default - return null; - } - - default Control createUiPart(Composite parent, Node context) { - try { - return createUi(parent, context); - } catch (RepositoryException e) { - throw new JcrException("Cannot create UI for context " + context, e); - } - } - - @Override - default Control createUiPart(Composite parent, Content context) { - if (context == null) - return createUiPart(parent, (Node) null); - if (context instanceof JcrContent) { - Node node = ((JcrContent) context).getJcrNode(); - return createUiPart(parent, node); - } else { -// CmsLog.getLog(CmsUiProvider.class) -// .warn("In " + getClass() + ", content " + context + " is not compatible with JCR."); -// return createUiPart(parent, (Node) null); - - throw new IllegalArgumentException( - "In " + getClass() + ", content " + context + " is not compatible with JCR"); - } - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/LifeCycleUiProvider.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/LifeCycleUiProvider.java deleted file mode 100644 index 5d77c15..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/LifeCycleUiProvider.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.argeo.cms.ui; - -import javax.jcr.RepositoryException; -import javax.jcr.Session; - -/** CmsUiProvider notified of initialisation with a system session. */ -public interface LifeCycleUiProvider extends CmsUiProvider { - public void init(Session adminSession) throws RepositoryException; - - public void destroy(); -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/AbstractFormPart.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/AbstractFormPart.java deleted file mode 100644 index 4ce4688..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/AbstractFormPart.java +++ /dev/null @@ -1,108 +0,0 @@ -package org.argeo.cms.ui.eclipse.forms; -/** - * AbstractFormPart implements IFormPart interface and can be used as a - * convenient base class for concrete form parts. If a method contains - * code that must be called, look for instructions to call 'super' - * when overriding. - * - * @see org.eclipse.ui.forms.widgets.Section - * @since 1.0 - */ -public abstract class AbstractFormPart implements IFormPart { - private IManagedForm managedForm; - private boolean dirty = false; - private boolean stale = true; - /** - * @see org.eclipse.ui.forms.IFormPart#initialize(org.eclipse.ui.forms.IManagedForm) - */ - public void initialize(IManagedForm form) { - this.managedForm = form; - } - /** - * Returns the form that manages this part. - * - * @return the managed form - */ - public IManagedForm getManagedForm() { - return managedForm; - } - /** - * Disposes the part. Subclasses should override to release any system - * resources. - */ - public void dispose() { - } - /** - * Commits the part. Subclasses should call 'super' when overriding. - * - * @param onSave - * true if the request to commit has arrived as a - * result of the 'save' action. - */ - public void commit(boolean onSave) { - dirty = false; - } - /** - * Sets the overall form input. Subclases may elect to override the method - * and adjust according to the form input. - * - * @param input - * the form input object - * @return false - */ - public boolean setFormInput(Object input) { - return false; - } - /** - * Instructs the part to grab keyboard focus. - */ - public void setFocus() { - } - /** - * Refreshes the section after becoming stale (falling behind data in the - * model). Subclasses must call 'super' when overriding this method. - */ - public void refresh() { - stale = false; - // since we have refreshed, any changes we had in the - // part are gone and we are not dirty - dirty = false; - } - /** - * Marks the part dirty. Subclasses should call this method as a result of - * user interaction with the widgets in the section. - */ - public void markDirty() { - dirty = true; - managedForm.dirtyStateChanged(); - } - /** - * Tests whether the part is dirty i.e. its widgets have state that is - * newer than the data in the model. - * - * @return true if the part is dirty, false - * otherwise. - */ - public boolean isDirty() { - return dirty; - } - /** - * Tests whether the part is stale i.e. its widgets have state that is - * older than the data in the model. - * - * @return true if the part is stale, false - * otherwise. - */ - public boolean isStale() { - return stale; - } - /** - * Marks the part stale. Subclasses should call this method as a result of - * model notification that indicates that the content of the section is no - * longer in sync with the model. - */ - public void markStale() { - stale = true; - managedForm.staleStateChanged(); - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormColors.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormColors.java deleted file mode 100644 index 32b031b..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormColors.java +++ /dev/null @@ -1,730 +0,0 @@ -package org.argeo.cms.ui.eclipse.forms; - -import java.util.HashMap; -import java.util.Map; - -import org.eclipse.jface.resource.JFaceResources; -import org.eclipse.jface.resource.LocalResourceManager; -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Color; -import org.eclipse.swt.graphics.RGB; -//import org.eclipse.swt.internal.graphics.Graphics; -import org.eclipse.swt.widgets.Display; - -/** - * Manages colors that will be applied to forms and form widgets. The colors are - * chosen to make the widgets look correct in the editor area. If a different - * set of colors is needed, subclass this class and override 'initialize' and/or - * 'initializeColors'. - * - * @since 1.0 - */ -public class FormColors { - /** - * Key for the form title foreground color. - * - * @deprecated use IFormColors.TITLE. - */ - public static final String TITLE = IFormColors.TITLE; - - /** - * Key for the tree/table border color. - * - * @deprecated use IFormColors.BORDER - */ - public static final String BORDER = IFormColors.BORDER; - - /** - * Key for the section separator color. - * - * @deprecated use IFormColors.SEPARATOR. - */ - public static final String SEPARATOR = IFormColors.SEPARATOR; - - /** - * Key for the section title bar background. - * - * @deprecated use IFormColors.TB_BG - */ - public static final String TB_BG = IFormColors.TB_BG; - - /** - * Key for the section title bar foreground. - * - * @deprecated use IFormColors.TB_FG - */ - public static final String TB_FG = IFormColors.TB_FG; - - /** - * Key for the section title bar gradient. - * - * @deprecated use IFormColors.TB_GBG - */ - public static final String TB_GBG = IFormColors.TB_GBG; - - /** - * Key for the section title bar border. - * - * @deprecated use IFormColors.TB_BORDER. - */ - public static final String TB_BORDER = IFormColors.TB_BORDER; - - /** - * Key for the section toggle color. Since 3.1, this color is used for all - * section styles. - * - * @deprecated use IFormColors.TB_TOGGLE. - */ - public static final String TB_TOGGLE = IFormColors.TB_TOGGLE; - - /** - * Key for the section toggle hover color. - * - * @deprecated use IFormColors.TB_TOGGLE_HOVER. - */ - public static final String TB_TOGGLE_HOVER = IFormColors.TB_TOGGLE_HOVER; - - protected Map colorRegistry = new HashMap(10); - - private LocalResourceManager resources; - - protected Color background; - - protected Color foreground; - - private boolean shared; - - protected Display display; - - protected Color border; - - /** - * Creates form colors using the provided display. - * - * @param display - * the display to use - */ - public FormColors(Display display) { - this.display = display; - initialize(); - } - - /** - * Returns the display used to create colors. - * - * @return the display - */ - public Display getDisplay() { - return display; - } - - /** - * Initializes the colors. Subclasses can override this method to change the - * way colors are created. Alternatively, only the color table can be - * modified by overriding initializeColorTable(). - * - * @see #initializeColorTable - */ - protected void initialize() { - background = display.getSystemColor(SWT.COLOR_LIST_BACKGROUND); - foreground = display.getSystemColor(SWT.COLOR_LIST_FOREGROUND); - initializeColorTable(); - updateBorderColor(); - } - - /** - * Allocates colors for the following keys: BORDER, SEPARATOR and - * TITLE. Subclasses can override to allocate these colors differently. - */ - protected void initializeColorTable() { - createTitleColor(); - createColor(IFormColors.SEPARATOR, getColor(IFormColors.TITLE).getRGB()); - RGB black = getSystemColor(SWT.COLOR_BLACK); - RGB borderRGB = getSystemColor(SWT.COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT); - createColor(IFormColors.BORDER, blend(borderRGB, black, 80)); - } - - /** - * Allocates colors for the section tool bar (all the keys that start with - * TB). Since these colors are only needed when TITLE_BAR style is used with - * the Section widget, they are not needed all the time and are allocated on - * demand. Consequently, this method will do nothing if the colors have been - * already initialized. Call this method prior to using colors with the TB - * keys to ensure they are available. - */ - public void initializeSectionToolBarColors() { - if (colorRegistry.containsKey(IFormColors.TB_BG)) - return; - createTitleBarGradientColors(); - createTitleBarOutlineColors(); - createTwistieColors(); - } - - /** - * Allocates additional colors for the form header, namely background - * gradients, bottom separator keylines and DND highlights. Since these - * colors are only needed for clients that want to use these particular - * style of header rendering, they are not needed all the time and are - * allocated on demand. Consequently, this method will do nothing if the - * colors have been already initialized. Call this method prior to using - * color keys with the H_ prefix to ensure they are available. - */ - protected void initializeFormHeaderColors() { - if (colorRegistry.containsKey(IFormColors.H_BOTTOM_KEYLINE2)) - return; - createFormHeaderColors(); - } - - /** - * Returns the RGB value of the system color represented by the code - * argument, as defined in SWT class. - * - * @param code - * the system color constant as defined in SWT - * class. - * @return the RGB value of the system color - */ - public RGB getSystemColor(int code) { - return getDisplay().getSystemColor(code).getRGB(); - } - - /** - * Creates the color for the specified key using the provided RGB object. - * The color object will be returned and also put into the registry. When - * the class is disposed, the color will be disposed with it. - * - * @param key - * the unique color key - * @param rgb - * the RGB object - * @return the allocated color object - */ - public Color createColor(String key, RGB rgb) { - // RAP [rh] changes due to missing Color constructor -// Color c = getResourceManager().createColor(rgb); -// Color prevC = (Color) colorRegistry.get(key); -// if (prevC != null && !prevC.isDisposed()) -// getResourceManager().destroyColor(prevC.getRGB()); -// Color c = Graphics.getColor(rgb); - Color c = new Color(display, rgb); - colorRegistry.put(key, c); - return c; - } - - /** - * Creates a color that can be used for areas of the form that is inactive. - * These areas can contain images, links, controls and other content but are - * considered auxilliary to the main content area. - * - *

- * The color should not be disposed because it is managed by this class. - * - * @return the inactive form color - */ - public Color getInactiveBackground() { - String key = "__ncbg__"; //$NON-NLS-1$ - Color color = getColor(key); - if (color == null) { - RGB sel = getSystemColor(SWT.COLOR_LIST_SELECTION); - // a blend of 95% white and 5% list selection system color - RGB ncbg = blend(sel, getSystemColor(SWT.COLOR_WHITE), 5); - color = createColor(key, ncbg); - } - return color; - } - - /** - * Creates the color for the specified key using the provided RGB values. - * The color object will be returned and also put into the registry. If - * there is already another color object under the same key in the registry, - * the existing object will be disposed. When the class is disposed, the - * color will be disposed with it. - * - * @param key - * the unique color key - * @param r - * red value - * @param g - * green value - * @param b - * blue value - * @return the allocated color object - */ - public Color createColor(String key, int r, int g, int b) { - return createColor(key, new RGB(r,g,b)); - } - - /** - * Computes the border color relative to the background. Allocated border - * color is designed to work well with white. Otherwise, stanard widget - * background color will be used. - */ - protected void updateBorderColor() { - if (isWhiteBackground()) - border = getColor(IFormColors.BORDER); - else { - border = display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND); - Color bg = getImpliedBackground(); - if (border.getRed() == bg.getRed() - && border.getGreen() == bg.getGreen() - && border.getBlue() == bg.getBlue()) - border = display.getSystemColor(SWT.COLOR_WIDGET_DARK_SHADOW); - } - } - - /** - * Sets the background color. All the toolkits that use this class will - * share the same background. - * - * @param bg - * background color - */ - public void setBackground(Color bg) { - this.background = bg; - updateBorderColor(); - updateFormHeaderColors(); - } - - /** - * Sets the foreground color. All the toolkits that use this class will - * share the same foreground. - * - * @param fg - * foreground color - */ - public void setForeground(Color fg) { - this.foreground = fg; - } - - /** - * Returns the current background color. - * - * @return the background color - */ - public Color getBackground() { - return background; - } - - /** - * Returns the current foreground color. - * - * @return the foreground color - */ - public Color getForeground() { - return foreground; - } - - /** - * Returns the computed border color. Border color depends on the background - * and is recomputed whenever the background changes. - * - * @return the current border color - */ - public Color getBorderColor() { - return border; - } - - /** - * Tests if the background is white. White background has RGB value - * 255,255,255. - * - * @return true if background is white, false - * otherwise. - */ - public boolean isWhiteBackground() { - Color bg = getImpliedBackground(); - return bg.getRed() == 255 && bg.getGreen() == 255 - && bg.getBlue() == 255; - } - - /** - * Returns the color object for the provided key or null if - * not in the registry. - * - * @param key - * the color key - * @return color object if found, or null if not. - */ - public Color getColor(String key) { - if (key.startsWith(IFormColors.TB_PREFIX)) - initializeSectionToolBarColors(); - else if (key.startsWith(IFormColors.H_PREFIX)) - initializeFormHeaderColors(); - return (Color) colorRegistry.get(key); - } - - /** - * Disposes all the colors in the registry. - */ - public void dispose() { - if (resources != null) - resources.dispose(); - resources = null; - colorRegistry = null; - } - - /** - * Marks the colors shared. This prevents toolkits that share this object - * from disposing it. - */ - public void markShared() { - this.shared = true; - } - - /** - * Tests if the colors are shared. - * - * @return true if shared, false otherwise. - */ - public boolean isShared() { - return shared; - } - - /** - * Blends c1 and c2 based in the provided ratio. - * - * @param c1 - * first color - * @param c2 - * second color - * @param ratio - * percentage of the first color in the blend (0-100) - * @return the RGB value of the blended color - */ - public static RGB blend(RGB c1, RGB c2, int ratio) { - int r = blend(c1.red, c2.red, ratio); - int g = blend(c1.green, c2.green, ratio); - int b = blend(c1.blue, c2.blue, ratio); - return new RGB(r, g, b); - } - - /** - * Tests the source RGB for range. - * - * @param rgb - * the tested RGB - * @param from - * range start (excluding the value itself) - * @param to - * range end (excluding the value itself) - * @return true if at least one of the primary colors in the - * source RGB are within the provided range, false - * otherwise. - */ - public static boolean testAnyPrimaryColor(RGB rgb, int from, int to) { - if (testPrimaryColor(rgb.red, from, to)) - return true; - if (testPrimaryColor(rgb.green, from, to)) - return true; - if (testPrimaryColor(rgb.blue, from, to)) - return true; - return false; - } - - /** - * Tests the source RGB for range. - * - * @param rgb - * the tested RGB - * @param from - * range start (excluding the value itself) - * @param to - * tange end (excluding the value itself) - * @return true if at least two of the primary colors in the - * source RGB are within the provided range, false - * otherwise. - */ - public static boolean testTwoPrimaryColors(RGB rgb, int from, int to) { - int total = 0; - if (testPrimaryColor(rgb.red, from, to)) - total++; - if (testPrimaryColor(rgb.green, from, to)) - total++; - if (testPrimaryColor(rgb.blue, from, to)) - total++; - return total >= 2; - } - - /** - * Blends two primary color components based on the provided ratio. - * - * @param v1 - * first component - * @param v2 - * second component - * @param ratio - * percentage of the first component in the blend - * @return - */ - private static int blend(int v1, int v2, int ratio) { - int b = (ratio * v1 + (100 - ratio) * v2) / 100; - return Math.min(255, b); - } - - private Color getImpliedBackground() { - if (getBackground() != null) - return getBackground(); - return getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND); - } - - private static boolean testPrimaryColor(int value, int from, int to) { - return value > from && value < to; - } - - private void createTitleColor() { - /* - * RGB rgb = getSystemColor(SWT.COLOR_LIST_SELECTION); // test too light - * if (testTwoPrimaryColors(rgb, 120, 151)) rgb = blend(rgb, BLACK, 80); - * else if (testTwoPrimaryColors(rgb, 150, 256)) rgb = blend(rgb, BLACK, - * 50); createColor(TITLE, rgb); - */ - RGB bg = getImpliedBackground().getRGB(); - RGB listSelection = getSystemColor(SWT.COLOR_LIST_SELECTION); - RGB listForeground = getSystemColor(SWT.COLOR_LIST_FOREGROUND); - RGB rgb = listSelection; - - // Group 1 - // Rule: If at least 2 of the LIST_SELECTION RGB values are equal to or - // between 0 and 120, then use 100% LIST_SELECTION as it is (no - // additions) - // Examples: XP Default, Win Classic Standard, Win High Con White, Win - // Classic Marine - if (testTwoPrimaryColors(listSelection, -1, 121)) - rgb = listSelection; - // Group 2 - // When LIST_BACKGROUND = white (255, 255, 255) or not black, text - // colour = LIST_SELECTION @ 100% Opacity + 50% LIST_FOREGROUND over - // LIST_BACKGROUND - // Rule: If at least 2 of the LIST_SELECTION RGB values are equal to or - // between 121 and 255, then add 50% LIST_FOREGROUND to LIST_SELECTION - // foreground colour - // Examples: Win Vista, XP Silver, XP Olive , Win Classic Plum, OSX - // Aqua, OSX Graphite, Linux GTK - else if (testTwoPrimaryColors(listSelection, 120, 256) - || (bg.red == 0 && bg.green == 0 && bg.blue == 0)) - rgb = blend(listSelection, listForeground, 50); - // Group 3 - // When LIST_BACKGROUND = black (0, 0, 0), text colour = LIST_SELECTION - // @ 100% Opacity + 50% LIST_FOREGROUND over LIST_BACKGROUND - // Rule: If LIST_BACKGROUND = 0, 0, 0, then add 50% LIST_FOREGROUND to - // LIST_SELECTION foreground colour - // Examples: Win High Con Black, Win High Con #1, Win High Con #2 - // (covered in the second part of the OR clause above) - createColor(IFormColors.TITLE, rgb); - } - - private void createTwistieColors() { - RGB rgb = getColor(IFormColors.TITLE).getRGB(); - RGB white = getSystemColor(SWT.COLOR_WHITE); - createColor(TB_TOGGLE, rgb); - rgb = blend(rgb, white, 60); - createColor(TB_TOGGLE_HOVER, rgb); - } - - private void createTitleBarGradientColors() { - RGB tbBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND); - RGB bg = getImpliedBackground().getRGB(); - - // Group 1 - // Rule: If at least 2 of the RGB values are equal to or between 180 and - // 255, then apply specified opacity for Group 1 - // Examples: Vista, XP Silver, Wn High Con #2 - // Gradient Bottom = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND - // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND - if (testTwoPrimaryColors(tbBg, 179, 256)) - tbBg = blend(tbBg, bg, 30); - - // Group 2 - // Rule: If at least 2 of the RGB values are equal to or between 121 and - // 179, then apply specified opacity for Group 2 - // Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black - // Gradient Bottom = TITLE_BACKGROUND @ 20% Opacity over LIST_BACKGROUND - // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND - else if (testTwoPrimaryColors(tbBg, 120, 180)) - tbBg = blend(tbBg, bg, 20); - - // Group 3 - // Rule: Everything else - // Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX - // Aqua, Wn High Con White, Wn High Con #1 - // Gradient Bottom = TITLE_BACKGROUND @ 10% Opacity over LIST_BACKGROUND - // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND - else { - tbBg = blend(tbBg, bg, 10); - } - - createColor(IFormColors.TB_BG, tbBg); - - // for backward compatibility - createColor(TB_GBG, tbBg); - } - - private void createTitleBarOutlineColors() { - // title bar outline - border color - RGB tbBorder = getSystemColor(SWT.COLOR_TITLE_BACKGROUND); - RGB bg = getImpliedBackground().getRGB(); - // Group 1 - // Rule: If at least 2 of the RGB values are equal to or between 180 and - // 255, then apply specified opacity for Group 1 - // Examples: Vista, XP Silver, Wn High Con #2 - // Keyline = TITLE_BACKGROUND @ 70% Opacity over LIST_BACKGROUND - if (testTwoPrimaryColors(tbBorder, 179, 256)) - tbBorder = blend(tbBorder, bg, 70); - - // Group 2 - // Rule: If at least 2 of the RGB values are equal to or between 121 and - // 179, then apply specified opacity for Group 2 - // Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black - - // Keyline = TITLE_BACKGROUND @ 50% Opacity over LIST_BACKGROUND - else if (testTwoPrimaryColors(tbBorder, 120, 180)) - tbBorder = blend(tbBorder, bg, 50); - - // Group 3 - // Rule: Everything else - // Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX - // Aqua, Wn High Con White, Wn High Con #1 - - // Keyline = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND - else { - tbBorder = blend(tbBorder, bg, 30); - } - createColor(FormColors.TB_BORDER, tbBorder); - } - - private void updateFormHeaderColors() { - if (colorRegistry.containsKey(IFormColors.H_GRADIENT_END)) { - disposeIfFound(IFormColors.H_GRADIENT_END); - disposeIfFound(IFormColors.H_GRADIENT_START); - disposeIfFound(IFormColors.H_BOTTOM_KEYLINE1); - disposeIfFound(IFormColors.H_BOTTOM_KEYLINE2); - disposeIfFound(IFormColors.H_HOVER_LIGHT); - disposeIfFound(IFormColors.H_HOVER_FULL); - initializeFormHeaderColors(); - } - } - - private void disposeIfFound(String key) { - Color color = getColor(key); - if (color != null) { - colorRegistry.remove(key); - // RAP [rh] changes due to missing Color#dispose() -// color.dispose(); - } - } - - private void createFormHeaderColors() { - createFormHeaderGradientColors(); - createFormHeaderKeylineColors(); - createFormHeaderDNDColors(); - } - - private void createFormHeaderGradientColors() { - RGB titleBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND); - Color bgColor = getImpliedBackground(); - RGB bg = bgColor.getRGB(); - RGB bottom, top; - // Group 1 - // Rule: If at least 2 of the RGB values are equal to or between 180 and - // 255, then apply specified opacity for Group 1 - // Examples: Vista, XP Silver, Wn High Con #2 - // Gradient Bottom = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND - // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND - if (testTwoPrimaryColors(titleBg, 179, 256)) { - bottom = blend(titleBg, bg, 30); - top = bg; - } - - // Group 2 - // Rule: If at least 2 of the RGB values are equal to or between 121 and - // 179, then apply specified opacity for Group 2 - // Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black - // Gradient Bottom = TITLE_BACKGROUND @ 20% Opacity over LIST_BACKGROUND - // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND - else if (testTwoPrimaryColors(titleBg, 120, 180)) { - bottom = blend(titleBg, bg, 20); - top = bg; - } - - // Group 3 - // Rule: If at least 2 of the RGB values are equal to or between 0 and - // 120, then apply specified opacity for Group 3 - // Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX - // Aqua, Wn High Con White, Wn High Con #1 - // Gradient Bottom = TITLE_BACKGROUND @ 10% Opacity over LIST_BACKGROUND - // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND - else { - bottom = blend(titleBg, bg, 10); - top = bg; - } - createColor(IFormColors.H_GRADIENT_END, top); - createColor(IFormColors.H_GRADIENT_START, bottom); - } - - private void createFormHeaderKeylineColors() { - RGB titleBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND); - Color bgColor = getImpliedBackground(); - RGB bg = bgColor.getRGB(); - RGB keyline2; - // H_BOTTOM_KEYLINE1 - createColor(IFormColors.H_BOTTOM_KEYLINE1, new RGB(255, 255, 255)); - - // H_BOTTOM_KEYLINE2 - // Group 1 - // Rule: If at least 2 of the RGB values are equal to or between 180 and - // 255, then apply specified opacity for Group 1 - // Examples: Vista, XP Silver, Wn High Con #2 - // Keyline = TITLE_BACKGROUND @ 70% Opacity over LIST_BACKGROUND - if (testTwoPrimaryColors(titleBg, 179, 256)) - keyline2 = blend(titleBg, bg, 70); - - // Group 2 - // Rule: If at least 2 of the RGB values are equal to or between 121 and - // 179, then apply specified opacity for Group 2 - // Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black - // Keyline = TITLE_BACKGROUND @ 50% Opacity over LIST_BACKGROUND - else if (testTwoPrimaryColors(titleBg, 120, 180)) - keyline2 = blend(titleBg, bg, 50); - - // Group 3 - // Rule: If at least 2 of the RGB values are equal to or between 0 and - // 120, then apply specified opacity for Group 3 - // Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX - // Aqua, Wn High Con White, Wn High Con #1 - - // Keyline = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND - else - keyline2 = blend(titleBg, bg, 30); - // H_BOTTOM_KEYLINE2 - createColor(IFormColors.H_BOTTOM_KEYLINE2, keyline2); - } - - private void createFormHeaderDNDColors() { - RGB titleBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND_GRADIENT); - Color bgColor = getImpliedBackground(); - RGB bg = bgColor.getRGB(); - RGB light, full; - // ALL Themes - // - // Light Highlight - // When *near* the 'hot' area - // Rule: If near the title in the 'hot' area, show background highlight - // TITLE_BACKGROUND_GRADIENT @ 40% - light = blend(titleBg, bg, 40); - // Full Highlight - // When *on* the title area (regions 1 and 2) - // Rule: If near the title in the 'hot' area, show background highlight - // TITLE_BACKGROUND_GRADIENT @ 60% - full = blend(titleBg, bg, 60); - // H_DND_LIGHT - // H_DND_FULL - createColor(IFormColors.H_HOVER_LIGHT, light); - createColor(IFormColors.H_HOVER_FULL, full); - } - - private LocalResourceManager getResourceManager() { - if (resources == null) - resources = new LocalResourceManager(JFaceResources.getResources()); - return resources; - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormFonts.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormFonts.java deleted file mode 100644 index 9e931ba..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormFonts.java +++ /dev/null @@ -1,122 +0,0 @@ -package org.argeo.cms.ui.eclipse.forms; - -import java.util.HashMap; - -import org.eclipse.jface.resource.DeviceResourceException; -import org.eclipse.jface.resource.FontDescriptor; -import org.eclipse.jface.resource.JFaceResources; -import org.eclipse.jface.resource.LocalResourceManager; -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Device; -import org.eclipse.swt.graphics.Font; -import org.eclipse.swt.graphics.FontData; -//import org.eclipse.swt.internal.graphics.Graphics; -import org.eclipse.swt.widgets.Display; - -public class FormFonts { - private static FormFonts instance; - - public static FormFonts getInstance() { - if (instance == null) - instance = new FormFonts(); - return instance; - } - - private LocalResourceManager resources; - private HashMap descriptors; - - private FormFonts() { - } - - private class BoldFontDescriptor extends FontDescriptor { - private FontData[] fFontData; - - BoldFontDescriptor(Font font) { - // RAP [if] Changes due to different way of creating fonts - // fFontData = font.getFontData(); - // for (int i = 0; i < fFontData.length; i++) { - // fFontData[i].setStyle(fFontData[i].getStyle() | SWT.BOLD); - // } - FontData fontData = font.getFontData()[0]; - // Font boldFont = Graphics.getFont( fontData.getName(), - // fontData.getHeight(), - // fontData.getStyle() | SWT.BOLD ); - Font boldFont = new Font(Display.getCurrent(), fontData.getName(), fontData.getHeight(), - fontData.getStyle() | SWT.BOLD); - fFontData = boldFont.getFontData(); - } - - public boolean equals(Object obj) { - if (obj instanceof BoldFontDescriptor) { - BoldFontDescriptor desc = (BoldFontDescriptor) obj; - if (desc.fFontData.length != fFontData.length) - return false; - for (int i = 0; i < fFontData.length; i++) - if (!fFontData[i].equals(desc.fFontData[i])) - return false; - return true; - } - return false; - } - - public int hashCode() { - int hash = 0; - for (int i = 0; i < fFontData.length; i++) - hash = hash * 7 + fFontData[i].hashCode(); - return hash; - } - - public Font createFont(Device device) throws DeviceResourceException { - // RAP [if] Changes due to different way of creating fonts - return new Font(device, fFontData[0]); - // return Graphics.getFont( fFontData[ 0 ] ); - } - - public void destroyFont(Font previouslyCreatedFont) { - // RAP [if] unnecessary - // previouslyCreatedFont.dispose(); - } - } - - public Font getBoldFont(Display display, Font font) { - checkHashMaps(); - BoldFontDescriptor desc = new BoldFontDescriptor(font); - Font result = getResourceManager().createFont(desc); - descriptors.put(result, desc); - return result; - } - - public boolean markFinished(Font boldFont) { - checkHashMaps(); - BoldFontDescriptor desc = (BoldFontDescriptor) descriptors.get(boldFont); - if (desc != null) { - getResourceManager().destroyFont(desc); - if (getResourceManager().find(desc) == null) { - descriptors.remove(boldFont); - validateHashMaps(); - } - return true; - - } - // if the image was not found, dispose of it for the caller - // RAP [if] unnecessary - // boldFont.dispose(); - return false; - } - - private LocalResourceManager getResourceManager() { - if (resources == null) - resources = new LocalResourceManager(JFaceResources.getResources()); - return resources; - } - - private void checkHashMaps() { - if (descriptors == null) - descriptors = new HashMap(); - } - - private void validateHashMaps() { - if (descriptors.size() == 0) - descriptors = null; - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormToolkit.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormToolkit.java deleted file mode 100644 index 9927104..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormToolkit.java +++ /dev/null @@ -1,913 +0,0 @@ -package org.argeo.cms.ui.eclipse.forms; - -import org.eclipse.jface.resource.JFaceResources; -import org.eclipse.jface.window.Window; -import org.eclipse.swt.SWT; -//import org.eclipse.swt.custom.CCombo; -import org.eclipse.swt.custom.ScrolledComposite; -import org.eclipse.swt.events.FocusAdapter; -import org.eclipse.swt.events.FocusEvent; -import org.eclipse.swt.events.KeyAdapter; -import org.eclipse.swt.events.KeyEvent; -import org.eclipse.swt.events.MouseAdapter; -import org.eclipse.swt.events.MouseEvent; -// RAP [rh] Paint events missing -//import org.eclipse.swt.events.PaintEvent; -//import org.eclipse.swt.events.PaintListener; -import org.eclipse.swt.graphics.Color; -import org.eclipse.swt.graphics.Font; -//RAP [rh] GC missing -//import org.eclipse.swt.graphics.GC; -import org.eclipse.swt.graphics.Point; -//import org.eclipse.swt.graphics.RGB; -//import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -//import org.eclipse.swt.widgets.Event; -import org.eclipse.swt.widgets.Label; -//import org.eclipse.swt.widgets.Listener; -import org.eclipse.swt.widgets.Table; -import org.eclipse.swt.widgets.Text; -import org.eclipse.swt.widgets.Tree; -import org.eclipse.swt.widgets.Widget; -//import org.eclipse.ui.forms.FormColors; -//import org.eclipse.ui.forms.HyperlinkGroup; -//import org.eclipse.ui.forms.IFormColors; -//import org.eclipse.ui.internal.forms.widgets.FormFonts; -//import org.eclipse.ui.internal.forms.widgets.FormUtil; - -/** - * The toolkit is responsible for creating SWT controls adapted to work in - * Eclipse forms. In addition to changing their presentation properties (fonts, - * colors etc.), various listeners are attached to make them behave correctly in - * the form context. - *

- * In addition to being the control factory, the toolkit is also responsible for - * painting flat borders for select controls, managing hyperlink groups and - * control colors. - *

- * The toolkit creates some of the most common controls used to populate Eclipse - * forms. Controls that must be created using their constructors, - * adapt() method is available to change its properties in the - * same way as with the supported toolkit controls. - *

- * Typically, one toolkit object is created per workbench part (for example, an - * editor or a form wizard). The toolkit is disposed when the part is disposed. - * To conserve resources, it is possible to create one color object for the - * entire plug-in and share it between several toolkits. The plug-in is - * responsible for disposing the colors (disposing the toolkit that uses shared - * color object will not dispose the colors). - *

- * FormToolkit is normally instantiated, but can also be subclassed if some of - * the methods needs to be modified. In those cases, super must - * be called to preserve normal behaviour. - * - * @since 1.0 - */ -public class FormToolkit { - public static final String KEY_DRAW_BORDER = "FormWidgetFactory.drawBorder"; //$NON-NLS-1$ - - public static final String TREE_BORDER = "treeBorder"; //$NON-NLS-1$ - - public static final String TEXT_BORDER = "textBorder"; //$NON-NLS-1$ - - private int borderStyle = SWT.NULL; - - private FormColors colors; - - private int orientation = Window.getDefaultOrientation(); - - // private KeyListener deleteListener; - // RAP [rh] Paint events missing -// private BorderPainter borderPainter; - - private BoldFontHolder boldFontHolder; - -// private HyperlinkGroup hyperlinkGroup; - - private boolean isDisposed = false; - - /* default */ - VisibilityHandler visibilityHandler; - - /* default */ - KeyboardHandler keyboardHandler; - - // RAP [rh] Paint events missing -// private class BorderPainter implements PaintListener { -// public void paintControl(PaintEvent event) { -// Composite composite = (Composite) event.widget; -// Control[] children = composite.getChildren(); -// for (int i = 0; i < children.length; i++) { -// Control c = children[i]; -// boolean inactiveBorder = false; -// boolean textBorder = false; -// if (!c.isVisible()) -// continue; -// /* -// * if (c.getEnabled() == false && !(c instanceof CCombo)) -// * continue; -// */ -// if (c instanceof Hyperlink) -// continue; -// Object flag = c.getData(KEY_DRAW_BORDER); -// if (flag != null) { -// if (flag.equals(Boolean.FALSE)) -// continue; -// if (flag.equals(TREE_BORDER)) -// inactiveBorder = true; -// else if (flag.equals(TEXT_BORDER)) -// textBorder = true; -// } -// if (getBorderStyle() == SWT.BORDER) { -// if (!inactiveBorder && !textBorder) { -// continue; -// } -// if (c instanceof Text || c instanceof Table -// || c instanceof Tree) -// continue; -// } -// if (!inactiveBorder -// && (c instanceof Text || c instanceof CCombo || textBorder)) { -// Rectangle b = c.getBounds(); -// GC gc = event.gc; -// gc.setForeground(c.getBackground()); -// gc.drawRectangle(b.x - 1, b.y - 1, b.width + 1, -// b.height + 1); -// // gc.setForeground(getBorderStyle() == SWT.BORDER ? colors -// // .getBorderColor() : colors.getForeground()); -// gc.setForeground(colors.getBorderColor()); -// if (c instanceof CCombo) -// gc.drawRectangle(b.x - 1, b.y - 1, b.width + 1, -// b.height + 1); -// else -// gc.drawRectangle(b.x - 1, b.y - 2, b.width + 1, -// b.height + 3); -// } else if (inactiveBorder || c instanceof Table -// || c instanceof Tree) { -// Rectangle b = c.getBounds(); -// GC gc = event.gc; -// gc.setForeground(colors.getBorderColor()); -// gc.drawRectangle(b.x - 1, b.y - 1, b.width + 1, -// b.height + 1); -// } -// } -// } -// } - - private static class VisibilityHandler extends FocusAdapter { - public void focusGained(FocusEvent e) { - Widget w = e.widget; - if (w instanceof Control) { - FormUtil.ensureVisible((Control) w); - } - } - } - - private static class KeyboardHandler extends KeyAdapter { - public void keyPressed(KeyEvent e) { - Widget w = e.widget; - if (w instanceof Control) { - if (e.doit) - FormUtil.processKey(e.keyCode, (Control) w); - } - } - } - - private class BoldFontHolder { - private Font normalFont; - - private Font boldFont; - - public BoldFontHolder() { - } - - public Font getBoldFont(Font font) { - createBoldFont(font); - return boldFont; - } - - private void createBoldFont(Font font) { - if (normalFont == null || !normalFont.equals(font)) { - normalFont = font; - dispose(); - } - if (boldFont == null) { - boldFont = FormFonts.getInstance().getBoldFont(colors.getDisplay(), - normalFont); - } - } - - public void dispose() { - if (boldFont != null) { - FormFonts.getInstance().markFinished(boldFont); - boldFont = null; - } - } - } - - /** - * Creates a toolkit that is self-sufficient (will manage its own colors). - *

- * Clients that call this method must call {@link #dispose()} when they - * are finished using the toolkit. - * - */ - public FormToolkit(Display display) { - this(new FormColors(display)); - } - - /** - * Creates a toolkit that will use the provided (shared) colors. The toolkit - * will dispose the colors if and only if they are not marked as - * shared via the markShared() method. - *

- * Clients that call this method must call {@link #dispose()} when they - * are finished using the toolkit. - * - * @param colors - * the shared colors - */ - public FormToolkit(FormColors colors) { - this.colors = colors; - initialize(); - } - - /** - * Creates a button as a part of the form. - * - * @param parent - * the button parent - * @param text - * an optional text for the button (can be null) - * @param style - * the button style (for example, SWT.PUSH) - * @return the button widget - */ - public Button createButton(Composite parent, String text, int style) { - Button button = new Button(parent, style | SWT.FLAT | orientation); - if (text != null) - button.setText(text); - adapt(button, true, true); - return button; - } - - /** - * Creates the composite as a part of the form. - * - * @param parent - * the composite parent - * @return the composite widget - */ - public Composite createComposite(Composite parent) { - return createComposite(parent, SWT.NULL); - } - - /** - * Creates the composite as part of the form using the provided style. - * - * @param parent - * the composite parent - * @param style - * the composite style - * @return the composite widget - */ - public Composite createComposite(Composite parent, int style) { -// Composite composite = new LayoutComposite(parent, style | orientation); - Composite composite = new Composite(parent, style | orientation); - adapt(composite); - return composite; - } - - /** - * Creats the composite that can server as a separator between various parts - * of a form. Separator height should be controlled by setting the height - * hint on the layout data for the composite. - * - * @param parent - * the separator parent - * @return the separator widget - */ -// RAP [rh] createCompositeSeparator: currently no useful implementation possible, delete? - public Composite createCompositeSeparator(Composite parent) { - final Composite composite = new Composite(parent, orientation); -// RAP [rh] GC and paint events missing -// composite.addListener(SWT.Paint, new Listener() { -// public void handleEvent(Event e) { -// if (composite.isDisposed()) -// return; -// Rectangle bounds = composite.getBounds(); -// GC gc = e.gc; -// gc.setForeground(colors.getColor(IFormColors.SEPARATOR)); -// if (colors.getBackground() != null) -// gc.setBackground(colors.getBackground()); -// gc.fillGradientRectangle(0, 0, bounds.width, bounds.height, -// false); -// } -// }); -// if (parent instanceof Section) -// ((Section) parent).setSeparatorControl(composite); - return composite; - } - - /** - * Creates a label as a part of the form. - * - * @param parent - * the label parent - * @param text - * the label text - * @return the label widget - */ - public Label createLabel(Composite parent, String text) { - return createLabel(parent, text, SWT.NONE); - } - - /** - * Creates a label as a part of the form. - * - * @param parent - * the label parent - * @param text - * the label text - * @param style - * the label style - * @return the label widget - */ - public Label createLabel(Composite parent, String text, int style) { - Label label = new Label(parent, style | orientation); - if (text != null) - label.setText(text); - adapt(label, false, false); - return label; - } - - /** - * Creates a hyperlink as a part of the form. The hyperlink will be added to - * the hyperlink group that belongs to this toolkit. - * - * @param parent - * the hyperlink parent - * @param text - * the text of the hyperlink - * @param style - * the hyperlink style - * @return the hyperlink widget - */ -// public Hyperlink createHyperlink(Composite parent, String text, int style) { -// Hyperlink hyperlink = new Hyperlink(parent, style | orientation); -// if (text != null) -// hyperlink.setText(text); -// hyperlink.addFocusListener(visibilityHandler); -// hyperlink.addKeyListener(keyboardHandler); -// hyperlinkGroup.add(hyperlink); -// return hyperlink; -// } - - /** - * Creates an image hyperlink as a part of the form. The hyperlink will be - * added to the hyperlink group that belongs to this toolkit. - * - * @param parent - * the hyperlink parent - * @param style - * the hyperlink style - * @return the image hyperlink widget - */ -// public ImageHyperlink createImageHyperlink(Composite parent, int style) { -// ImageHyperlink hyperlink = new ImageHyperlink(parent, style -// | orientation); -// hyperlink.addFocusListener(visibilityHandler); -// hyperlink.addKeyListener(keyboardHandler); -// hyperlinkGroup.add(hyperlink); -// return hyperlink; -// } - - /** - * Creates a rich text as a part of the form. - * - * @param parent - * the rich text parent - * @param trackFocus - * if true, the toolkit will monitor focus - * transfers to ensure that the hyperlink in focus is visible in - * the form. - * @return the rich text widget - * @since 1.2 - */ -// public FormText createFormText(Composite parent, boolean trackFocus) { -// FormText engine = new FormText(parent, SWT.WRAP | orientation); -// engine.marginWidth = 1; -// engine.marginHeight = 0; -// engine.setHyperlinkSettings(getHyperlinkGroup()); -// adapt(engine, trackFocus, true); -// engine.setMenu(parent.getMenu()); -// return engine; -// } - - /** - * Adapts a control to be used in a form that is associated with this - * toolkit. This involves adjusting colors and optionally adding handlers to - * ensure focus tracking and keyboard management. - * - * @param control - * a control to adapt - * @param trackFocus - * if true, form will be scrolled horizontally - * and/or vertically if needed to ensure that the control is - * visible when it gains focus. Set it to false if - * the control is not capable of gaining focus. - * @param trackKeyboard - * if true, the control that is capable of - * gaining focus will be tracked for certain keys that are - * important to the underlying form (for example, PageUp, - * PageDown, ScrollUp, ScrollDown etc.). Set it to - * false if the control is not capable of gaining - * focus or these particular key event are already used by the - * control. - */ - public void adapt(Control control, boolean trackFocus, boolean trackKeyboard) { - control.setBackground(colors.getBackground()); - control.setForeground(colors.getForeground()); -// if (control instanceof ExpandableComposite) { -// ExpandableComposite ec = (ExpandableComposite) control; -// if (ec.toggle != null) { -// if (trackFocus) -// ec.toggle.addFocusListener(visibilityHandler); -// if (trackKeyboard) -// ec.toggle.addKeyListener(keyboardHandler); -// } -// if (ec.textLabel != null) { -// if (trackFocus) -// ec.textLabel.addFocusListener(visibilityHandler); -// if (trackKeyboard) -// ec.textLabel.addKeyListener(keyboardHandler); -// } -// return; -// } - if (trackFocus) - control.addFocusListener(visibilityHandler); - if (trackKeyboard) - control.addKeyListener(keyboardHandler); - } - - /** - * Adapts a composite to be used in a form associated with this toolkit. - * - * @param composite - * the composite to adapt - */ - public void adapt(Composite composite) { - composite.setBackground(colors.getBackground()); - composite.addMouseListener(new MouseAdapter() { - public void mouseDown(MouseEvent e) { - ((Control) e.widget).setFocus(); - } - }); - if (composite.getParent() != null) - composite.setMenu(composite.getParent().getMenu()); - } - - /** - * A helper method that ensures the provided control is visible when - * ScrolledComposite is somewhere in the parent chain. If scroll bars are - * visible and the control is clipped, the client of the scrolled composite - * will be scrolled to reveal the control. - * - * @param c - * the control to reveal - */ - public static void ensureVisible(Control c) { - FormUtil.ensureVisible(c); - } - - /** - * Creates a section as a part of the form. - * - * @param parent - * the section parent - * @param sectionStyle - * the section style - * @return the section widget - */ -// public Section createSection(Composite parent, int sectionStyle) { -// Section section = new Section(parent, orientation, sectionStyle); -// section.setMenu(parent.getMenu()); -// adapt(section, true, true); -// if (section.toggle != null) { -// section.toggle.setHoverDecorationColor(colors -// .getColor(IFormColors.TB_TOGGLE_HOVER)); -// section.toggle.setDecorationColor(colors -// .getColor(IFormColors.TB_TOGGLE)); -// } -// section.setFont(boldFontHolder.getBoldFont(parent.getFont())); -// if ((sectionStyle & Section.TITLE_BAR) != 0 -// || (sectionStyle & Section.SHORT_TITLE_BAR) != 0) { -// colors.initializeSectionToolBarColors(); -// section.setTitleBarBackground(colors.getColor(IFormColors.TB_BG)); -// section.setTitleBarBorderColor(colors -// .getColor(IFormColors.TB_BORDER)); -// } -// // call setTitleBarForeground regardless as it also sets the label color -// section.setTitleBarForeground(colors -// .getColor(IFormColors.TB_TOGGLE)); -// return section; -// } - - /** - * Creates an expandable composite as a part of the form. - * - * @param parent - * the expandable composite parent - * @param expansionStyle - * the expandable composite style - * @return the expandable composite widget - */ -// public ExpandableComposite createExpandableComposite(Composite parent, -// int expansionStyle) { -// ExpandableComposite ec = new ExpandableComposite(parent, orientation, -// expansionStyle); -// ec.setMenu(parent.getMenu()); -// adapt(ec, true, true); -// ec.setFont(boldFontHolder.getBoldFont(ec.getFont())); -// return ec; -// } - - /** - * Creates a separator label as a part of the form. - * - * @param parent - * the separator parent - * @param style - * the separator style - * @return the separator label - */ - public Label createSeparator(Composite parent, int style) { - Label label = new Label(parent, SWT.SEPARATOR | style | orientation); - label.setBackground(colors.getBackground()); - label.setForeground(colors.getBorderColor()); - return label; - } - - /** - * Creates a table as a part of the form. - * - * @param parent - * the table parent - * @param style - * the table style - * @return the table widget - */ - public Table createTable(Composite parent, int style) { - Table table = new Table(parent, style | borderStyle | orientation); - adapt(table, false, false); - // hookDeleteListener(table); - return table; - } - - /** - * Creates a text as a part of the form. - * - * @param parent - * the text parent - * @param value - * the text initial value - * @return the text widget - */ - public Text createText(Composite parent, String value) { - return createText(parent, value, SWT.SINGLE); - } - - /** - * Creates a text as a part of the form. - * - * @param parent - * the text parent - * @param value - * the text initial value - * @param style - * the text style - * @return the text widget - */ - public Text createText(Composite parent, String value, int style) { - Text text = new Text(parent, borderStyle | style | orientation); - if (value != null) - text.setText(value); - text.setForeground(colors.getForeground()); - text.setBackground(colors.getBackground()); - text.addFocusListener(visibilityHandler); - return text; - } - - /** - * Creates a tree widget as a part of the form. - * - * @param parent - * the tree parent - * @param style - * the tree style - * @return the tree widget - */ - public Tree createTree(Composite parent, int style) { - Tree tree = new Tree(parent, borderStyle | style | orientation); - adapt(tree, false, false); - // hookDeleteListener(tree); - return tree; - } - - /** - * Creates a scrolled form widget in the provided parent. If you do not - * require scrolling because there is already a scrolled composite up the - * parent chain, use 'createForm' instead. - * - * @param parent - * the scrolled form parent - * @return the form that can scroll itself - * @see #createForm - */ - public ScrolledComposite createScrolledForm(Composite parent) { - ScrolledComposite form = new ScrolledComposite(parent, SWT.V_SCROLL - | SWT.H_SCROLL | orientation); - form.setExpandHorizontal(true); - form.setExpandVertical(true); - form.setBackground(colors.getBackground()); - form.setForeground(colors.getColor(IFormColors.TITLE)); - form.setFont(JFaceResources.getHeaderFont()); - return form; - } - - /** - * Creates a form widget in the provided parent. Note that this widget does - * not scroll its content, so make sure there is a scrolled composite up the - * parent chain. If you require scrolling, use 'createScrolledForm' instead. - * - * @param parent - * the form parent - * @return the form that does not scroll - * @see #createScrolledForm - */ -// public Form createForm(Composite parent) { -// Form formContent = new Form(parent, orientation); -// formContent.setBackground(colors.getBackground()); -// formContent.setForeground(colors.getColor(IFormColors.TITLE)); -// formContent.setFont(JFaceResources.getHeaderFont()); -// return formContent; -// } - - /** - * Takes advantage of the gradients and other capabilities to decorate the - * form heading using colors computed based on the current skin and - * operating system. - * - * @param form - * the form to decorate - */ - -// public void decorateFormHeading(Form form) { -// Color top = colors.getColor(IFormColors.H_GRADIENT_END); -// Color bot = colors.getColor(IFormColors.H_GRADIENT_START); -// form.setTextBackground(new Color[] { top, bot }, new int[] { 100 }, -// true); -// form.setHeadColor(IFormColors.H_BOTTOM_KEYLINE1, colors -// .getColor(IFormColors.H_BOTTOM_KEYLINE1)); -// form.setHeadColor(IFormColors.H_BOTTOM_KEYLINE2, colors -// .getColor(IFormColors.H_BOTTOM_KEYLINE2)); -// form.setHeadColor(IFormColors.H_HOVER_LIGHT, colors -// .getColor(IFormColors.H_HOVER_LIGHT)); -// form.setHeadColor(IFormColors.H_HOVER_FULL, colors -// .getColor(IFormColors.H_HOVER_FULL)); -// form.setHeadColor(IFormColors.TB_TOGGLE, colors -// .getColor(IFormColors.TB_TOGGLE)); -// form.setHeadColor(IFormColors.TB_TOGGLE_HOVER, colors -// .getColor(IFormColors.TB_TOGGLE_HOVER)); -// form.setSeparatorVisible(true); -// } - - /** - * Creates a scrolled page book widget as a part of the form. - * - * @param parent - * the page book parent - * @param style - * the text style - * @return the scrolled page book widget - */ -// public ScrolledPageBook createPageBook(Composite parent, int style) { -// ScrolledPageBook book = new ScrolledPageBook(parent, style -// | orientation); -// adapt(book, true, true); -// book.setMenu(parent.getMenu()); -// return book; -// } - - /** - * Disposes the toolkit. - */ - public void dispose() { - if (isDisposed) { - return; - } - isDisposed = true; - if (colors.isShared() == false) { - colors.dispose(); - colors = null; - } - boldFontHolder.dispose(); - } - - /** - * Returns the hyperlink group that manages hyperlinks for this toolkit. - * - * @return the hyperlink group - */ -// public HyperlinkGroup getHyperlinkGroup() { -// return hyperlinkGroup; -// } - - /** - * Sets the background color for the entire toolkit. The method delegates - * the call to the FormColors object and also updates the hyperlink group so - * that hyperlinks and other objects are in sync. - * - * @param bg - * the new background color - */ - public void setBackground(Color bg) { -// hyperlinkGroup.setBackground(bg); - colors.setBackground(bg); - } - - /** - * Refreshes the hyperlink colors by loading from JFace settings. - */ -// public void refreshHyperlinkColors() { -// hyperlinkGroup.initializeDefaultForegrounds(colors.getDisplay()); -// } - -// RAP [rh] paintBordersFor not useful as no GC to actually paint borders -// /** -// * Paints flat borders for widgets created by this toolkit within the -// * provided parent. Borders will not be painted if the global border style -// * is SWT.BORDER (i.e. if native borders are used). Call this method during -// * creation of a form composite to get the borders of its children painted. -// * Care should be taken when selection layout margins. At least one pixel -// * pargin width and height must be chosen to allow the toolkit to paint the -// * border on the parent around the widgets. -// *

-// * Borders are painted for some controls that are selected by the toolkit by -// * default. If a control needs a border but is not on its list, it is -// * possible to force border in the following way: -// * -// *

-//	 *
-//	 *
-//	 *
-//	 *             widget.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TREE_BORDER);
-//	 *
-//	 *             or
-//	 *
-//	 *             widget.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER);
-//	 *
-//	 *
-//	 *
-//	 * 
-// * -// * @param parent -// * the parent that owns the children for which the border needs -// * to be painted. -// */ -// public void paintBordersFor(Composite parent) { -// // if (borderStyle == SWT.BORDER) -// // return; -// if (borderPainter == null) -// borderPainter = new BorderPainter(); -// parent.addPaintListener(borderPainter); -// } - - /** - * Returns the colors used by this toolkit. - * - * @return the color object - */ - public FormColors getColors() { - return colors; - } - - /** - * Returns the border style used for various widgets created by this - * toolkit. The intent of the toolkit is to create controls with styles that - * yield a 'flat' appearance. On systems where the native borders are - * already flat, we set the style to SWT.BORDER and don't paint the borders - * ourselves. Otherwise, the style is set to SWT.NULL, and borders are - * painted by the toolkit. - * - * @return the global border style - */ - public int getBorderStyle() { - return borderStyle; - } - - /** - * Returns the margin required around the children whose border is being - * painted by the toolkit using {@link #paintBordersFor(Composite)}. Since - * the border is painted around the controls on the parent, a number of - * pixels needs to be reserved for this border. For windowing systems where - * the native border is used, this margin is 0. - * - * @return the margin in the parent when children have their border painted - */ - public int getBorderMargin() { - return getBorderStyle() == SWT.BORDER ? 0 : 2; - } - - /** - * Sets the border style to be used when creating widgets. The toolkit - * chooses the correct style based on the platform but this value can be - * changed using this method. - * - * @param style - * SWT.BORDER or SWT.NULL - * @see #getBorderStyle - */ - public void setBorderStyle(int style) { - this.borderStyle = style; - } - - /** - * A utility method that ensures that the control is visible in the scrolled - * composite. The prerequisite for this method is that the control has a - * class that extends ScrolledComposite somewhere in the parent chain. If - * the control is partially or fully clipped, the composite is scrolled to - * set by setting the origin to the control origin. - * - * @param c - * the control to make visible - * @param verticalOnly - * if true, the scrolled composite will be - * scrolled only vertically if needed. Otherwise, the scrolled - * composite origin will be set to the control origin. - */ - public static void setControlVisible(Control c, boolean verticalOnly) { - ScrolledComposite scomp = FormUtil.getScrolledComposite(c); - if (scomp == null) - return; - Point location = FormUtil.getControlLocation(scomp, c); - scomp.setOrigin(location); - } - - private void initialize() { - initializeBorderStyle(); -// hyperlinkGroup = new HyperlinkGroup(colors.getDisplay()); -// hyperlinkGroup.setBackground(colors.getBackground()); - visibilityHandler = new VisibilityHandler(); - keyboardHandler = new KeyboardHandler(); - boldFontHolder = new BoldFontHolder(); - } - -// RAP [rh] revise detection of border style: can't ask OS here - private void initializeBorderStyle() { -// String osname = System.getProperty("os.name"); //$NON-NLS-1$ -// String osversion = System.getProperty("os.version"); //$NON-NLS-1$ -// if (osname.startsWith("Windows") && "5.1".compareTo(osversion) <= 0) { //$NON-NLS-1$ //$NON-NLS-2$ -// // Skinned widgets used on newer Windows (e.g. XP (5.1), Vista -// // (6.0)) -// // Check for Windows Classic. If not used, set the style to BORDER -// RGB rgb = colors.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND); -// if (rgb.red != 212 || rgb.green != 208 || rgb.blue != 200) -// borderStyle = SWT.BORDER; -// } else if (osname.startsWith("Mac")) //$NON-NLS-1$ -// borderStyle = SWT.BORDER; - - borderStyle = SWT.BORDER; - } - - /** - * Returns the orientation that all the widgets created by this toolkit will - * inherit, if set. Can be SWT.NULL, - * SWT.LEFT_TO_RIGHT and SWT.RIGHT_TO_LEFT. - * - * @return orientation style for this toolkit, or SWT.NULL if - * not set. The default orientation is inherited from the Window - * default orientation. - * @see org.eclipse.jface.window.Window#getDefaultOrientation() - */ - - public int getOrientation() { - return orientation; - } - - /** - * Sets the orientation that all the widgets created by this toolkit will - * inherit. Can be SWT.NULL, SWT.LEFT_TO_RIGHT - * and SWT.RIGHT_TO_LEFT. - * - * @param orientation - * style for this toolkit. - */ - - public void setOrientation(int orientation) { - this.orientation = orientation; - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormUtil.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormUtil.java deleted file mode 100644 index 76e3f11..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormUtil.java +++ /dev/null @@ -1,522 +0,0 @@ -package org.argeo.cms.ui.eclipse.forms; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.ScrolledComposite; -import org.eclipse.swt.events.MouseEvent; -//import org.eclipse.swt.graphics.Device; -import org.eclipse.swt.graphics.FontMetrics; -import org.eclipse.swt.graphics.GC; -//import org.eclipse.swt.graphics.Image; -//import org.eclipse.swt.graphics.ImageData; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Combo; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Layout; -//import org.eclipse.swt.widgets.ScrollBar; -import org.eclipse.swt.widgets.Text; -//import org.eclipse.ui.forms.widgets.ColumnLayout; -//import org.eclipse.ui.forms.widgets.Form; -//import org.eclipse.ui.forms.widgets.FormText; -//import org.eclipse.ui.forms.widgets.FormToolkit; -//import org.eclipse.ui.forms.widgets.ILayoutExtension; -// -//import com.ibm.icu.text.BreakIterator; - -public class FormUtil { - public static final String PLUGIN_ID = "org.eclipse.ui.forms"; //$NON-NLS-1$ - - static final int H_SCROLL_INCREMENT = 5; - - static final int V_SCROLL_INCREMENT = 64; - - public static final String DEBUG = PLUGIN_ID + "/debug"; //$NON-NLS-1$ - - public static final String DEBUG_TEXT = DEBUG + "/text"; //$NON-NLS-1$ - public static final String DEBUG_TEXTSIZE = DEBUG + "/textsize"; //$NON-NLS-1$ - - public static final String DEBUG_FOCUS = DEBUG + "/focus"; //$NON-NLS-1$ - - public static final String FOCUS_SCROLLING = "focusScrolling"; //$NON-NLS-1$ - - public static final String IGNORE_BODY = "__ignore_body__"; //$NON-NLS-1$ - - public static Text createText(Composite parent, String label, - FormToolkit factory) { - return createText(parent, label, factory, 1); - } - - public static Text createText(Composite parent, String label, - FormToolkit factory, int span) { - factory.createLabel(parent, label); - Text text = factory.createText(parent, ""); //$NON-NLS-1$ - int hfill = span == 1 ? GridData.FILL_HORIZONTAL - : GridData.HORIZONTAL_ALIGN_FILL; - GridData gd = new GridData(hfill | GridData.VERTICAL_ALIGN_CENTER); - gd.horizontalSpan = span; - text.setLayoutData(gd); - return text; - } - - public static Text createText(Composite parent, String label, - FormToolkit factory, int span, int style) { - Label l = factory.createLabel(parent, label); - if ((style & SWT.MULTI) != 0) { - GridData gd = new GridData(GridData.VERTICAL_ALIGN_BEGINNING); - l.setLayoutData(gd); - } - Text text = factory.createText(parent, "", style); //$NON-NLS-1$ - int hfill = span == 1 ? GridData.FILL_HORIZONTAL - : GridData.HORIZONTAL_ALIGN_FILL; - GridData gd = new GridData(hfill | GridData.VERTICAL_ALIGN_CENTER); - gd.horizontalSpan = span; - text.setLayoutData(gd); - return text; - } - - public static Text createText(Composite parent, FormToolkit factory, - int span) { - Text text = factory.createText(parent, ""); //$NON-NLS-1$ - int hfill = span == 1 ? GridData.FILL_HORIZONTAL - : GridData.HORIZONTAL_ALIGN_FILL; - GridData gd = new GridData(hfill | GridData.VERTICAL_ALIGN_CENTER); - gd.horizontalSpan = span; - text.setLayoutData(gd); - return text; - } - - public static int computeMinimumWidth(GC gc, String text) { -// BreakIterator wb = BreakIterator.getWordInstance(); -// wb.setText(text); -// int last = 0; -// -// int width = 0; -// -// for (int loc = wb.first(); loc != BreakIterator.DONE; loc = wb.next()) { -// String word = text.substring(last, loc); -// Point extent = gc.textExtent(word); -// width = Math.max(width, extent.x); -// last = loc; -// } -// String lastWord = text.substring(last); -// Point extent = gc.textExtent(lastWord); -// width = Math.max(width, extent.x); -// return width; - return 0; - } - - public static Point computeWrapSize(GC gc, String text, int wHint) { -// BreakIterator wb = BreakIterator.getWordInstance(); -// wb.setText(text); - FontMetrics fm = gc.getFontMetrics(); - int lineHeight = fm.getHeight(); - - int saved = 0; - int last = 0; - int height = lineHeight; - int maxWidth = 0; -// for (int loc = wb.first(); loc != BreakIterator.DONE; loc = wb.next()) { -// String word = text.substring(saved, loc); -// Point extent = gc.textExtent(word); -// if (extent.x > wHint) { -// // overflow -// saved = last; -// height += extent.y; -// // switch to current word so maxWidth will accommodate very long single words -// word = text.substring(last, loc); -// extent = gc.textExtent(word); -// } -// maxWidth = Math.max(maxWidth, extent.x); -// last = loc; -// } - /* - * Correct the height attribute in case it was calculated wrong due to wHint being less than maxWidth. - * The recursive call proved to be the only thing that worked in all cases. Some attempts can be made - * to estimate the height, but the algorithm needs to be run again to be sure. - */ - if (maxWidth > wHint) - return computeWrapSize(gc, text, maxWidth); - return new Point(maxWidth, height); - } - -// RAP [rh] paintWrapText unnecessary -// public static void paintWrapText(GC gc, String text, Rectangle bounds) { -// paintWrapText(gc, text, bounds, false); -// } - -// RAP [rh] paintWrapText unnecessary -// public static void paintWrapText(GC gc, String text, Rectangle bounds, -// boolean underline) { -// BreakIterator wb = BreakIterator.getWordInstance(); -// wb.setText(text); -// FontMetrics fm = gc.getFontMetrics(); -// int lineHeight = fm.getHeight(); -// int descent = fm.getDescent(); -// -// int saved = 0; -// int last = 0; -// int y = bounds.y; -// int width = bounds.width; -// -// for (int loc = wb.first(); loc != BreakIterator.DONE; loc = wb.next()) { -// String line = text.substring(saved, loc); -// Point extent = gc.textExtent(line); -// -// if (extent.x > width) { -// // overflow -// String prevLine = text.substring(saved, last); -// gc.drawText(prevLine, bounds.x, y, true); -// if (underline) { -// Point prevExtent = gc.textExtent(prevLine); -// int lineY = y + lineHeight - descent + 1; -// gc -// .drawLine(bounds.x, lineY, bounds.x + prevExtent.x, -// lineY); -// } -// -// saved = last; -// y += lineHeight; -// } -// last = loc; -// } -// // paint the last line -// String lastLine = text.substring(saved, last); -// gc.drawText(lastLine, bounds.x, y, true); -// if (underline) { -// int lineY = y + lineHeight - descent + 1; -// Point lastExtent = gc.textExtent(lastLine); -// gc.drawLine(bounds.x, lineY, bounds.x + lastExtent.x, lineY); -// } -// } - - public static ScrolledComposite getScrolledComposite(Control c) { - Composite parent = c.getParent(); - - while (parent != null) { - if (parent instanceof ScrolledComposite) { - return (ScrolledComposite) parent; - } - parent = parent.getParent(); - } - return null; - } - - public static void ensureVisible(Control c) { - ScrolledComposite scomp = getScrolledComposite(c); - if (scomp != null) { - Object data = scomp.getData(FOCUS_SCROLLING); - if (data == null || !data.equals(Boolean.FALSE)) - FormUtil.ensureVisible(scomp, c); - } - } - - public static void ensureVisible(ScrolledComposite scomp, Control control) { - // if the control is a FormText we do not need to scroll since it will - // ensure visibility of its segments as necessary -// if (control instanceof FormText) -// return; - Point controlSize = control.getSize(); - Point controlOrigin = getControlLocation(scomp, control); - ensureVisible(scomp, controlOrigin, controlSize); - } - - public static void ensureVisible(ScrolledComposite scomp, - Point controlOrigin, Point controlSize) { - Rectangle area = scomp.getClientArea(); - Point scompOrigin = scomp.getOrigin(); - - int x = scompOrigin.x; - int y = scompOrigin.y; - - // horizontal right, but only if the control is smaller - // than the client area - if (controlSize.x < area.width - && (controlOrigin.x + controlSize.x > scompOrigin.x - + area.width)) { - x = controlOrigin.x + controlSize.x - area.width; - } - // horizontal left - make sure the left edge of - // the control is showing - if (controlOrigin.x < x) { - if (controlSize.x < area.width) - x = controlOrigin.x + controlSize.x - area.width; - else - x = controlOrigin.x; - } - // vertical bottom - if (controlSize.y < area.height - && (controlOrigin.y + controlSize.y > scompOrigin.y - + area.height)) { - y = controlOrigin.y + controlSize.y - area.height; - } - // vertical top - make sure the top of - // the control is showing - if (controlOrigin.y < y) { - if (controlSize.y < area.height) - y = controlOrigin.y + controlSize.y - area.height; - else - y = controlOrigin.y; - } - - if (scompOrigin.x != x || scompOrigin.y != y) { - // scroll to reveal - scomp.setOrigin(x, y); - } - } - - public static void ensureVisible(ScrolledComposite scomp, Control control, - MouseEvent e) { - Point controlOrigin = getControlLocation(scomp, control); - int rX = controlOrigin.x + e.x; - int rY = controlOrigin.y + e.y; - Rectangle area = scomp.getClientArea(); - Point scompOrigin = scomp.getOrigin(); - - int x = scompOrigin.x; - int y = scompOrigin.y; - // System.out.println("Ensure: area="+area+", origin="+scompOrigin+", - // cloc="+controlOrigin+", csize="+controlSize+", x="+x+", y="+y); - - // horizontal right - if (rX > scompOrigin.x + area.width) { - x = rX - area.width; - } - // horizontal left - else if (rX < x) { - x = rX; - } - // vertical bottom - if (rY > scompOrigin.y + area.height) { - y = rY - area.height; - } - // vertical top - else if (rY < y) { - y = rY; - } - - if (scompOrigin.x != x || scompOrigin.y != y) { - // scroll to reveal - scomp.setOrigin(x, y); - } - } - - public static Point getControlLocation(ScrolledComposite scomp, - Control control) { - int x = 0; - int y = 0; - Control content = scomp.getContent(); - Control currentControl = control; - for (;;) { - if (currentControl == content) - break; - Point location = currentControl.getLocation(); - // if (location.x > 0) - // x += location.x; - // if (location.y > 0) - // y += location.y; - x += location.x; - y += location.y; - currentControl = currentControl.getParent(); - } - return new Point(x, y); - } - - static void scrollVertical(ScrolledComposite scomp, boolean up) { - scroll(scomp, 0, up ? -V_SCROLL_INCREMENT : V_SCROLL_INCREMENT); - } - - static void scrollHorizontal(ScrolledComposite scomp, boolean left) { - scroll(scomp, left ? -H_SCROLL_INCREMENT : H_SCROLL_INCREMENT, 0); - } - - static void scrollPage(ScrolledComposite scomp, boolean up) { - Rectangle clientArea = scomp.getClientArea(); - int increment = up ? -clientArea.height : clientArea.height; - scroll(scomp, 0, increment); - } - - static void scroll(ScrolledComposite scomp, int xoffset, int yoffset) { - Point origin = scomp.getOrigin(); - Point contentSize = scomp.getContent().getSize(); - int xorigin = origin.x + xoffset; - int yorigin = origin.y + yoffset; - xorigin = Math.max(xorigin, 0); - xorigin = Math.min(xorigin, contentSize.x - 1); - yorigin = Math.max(yorigin, 0); - yorigin = Math.min(yorigin, contentSize.y - 1); - scomp.setOrigin(xorigin, yorigin); - } - -// RAP [rh] FormUtil#updatePageIncrement: empty implementation - public static void updatePageIncrement(ScrolledComposite scomp) { -// ScrollBar vbar = scomp.getVerticalBar(); -// if (vbar != null) { -// Rectangle clientArea = scomp.getClientArea(); -// int increment = clientArea.height - 5; -// vbar.setPageIncrement(increment); -// } -// ScrollBar hbar = scomp.getHorizontalBar(); -// if (hbar != null) { -// Rectangle clientArea = scomp.getClientArea(); -// int increment = clientArea.width - 5; -// hbar.setPageIncrement(increment); -// } - } - - public static void processKey(int keyCode, Control c) { - if (c.isDisposed()) { - return; - } - ScrolledComposite scomp = FormUtil.getScrolledComposite(c); - if (scomp != null) { - if (c instanceof Combo) - return; - switch (keyCode) { - case SWT.ARROW_DOWN: - if (scomp.getData("novarrows") == null) //$NON-NLS-1$ - FormUtil.scrollVertical(scomp, false); - break; - case SWT.ARROW_UP: - if (scomp.getData("novarrows") == null) //$NON-NLS-1$ - FormUtil.scrollVertical(scomp, true); - break; - case SWT.ARROW_LEFT: - FormUtil.scrollHorizontal(scomp, true); - break; - case SWT.ARROW_RIGHT: - FormUtil.scrollHorizontal(scomp, false); - break; - case SWT.PAGE_UP: - FormUtil.scrollPage(scomp, true); - break; - case SWT.PAGE_DOWN: - FormUtil.scrollPage(scomp, false); - break; - } - } - } - - public static boolean isWrapControl(Control c) { - if ((c.getStyle() & SWT.WRAP) != 0) - return true; - if (c instanceof Composite) { - return false; -// return ((Composite) c).getLayout() instanceof ILayoutExtension; - } - return false; - } - - public static int getWidthHint(int wHint, Control c) { - boolean wrap = isWrapControl(c); - return wrap ? wHint : SWT.DEFAULT; - } - - public static int getHeightHint(int hHint, Control c) { - if (c instanceof Composite) { - Layout layout = ((Composite) c).getLayout(); -// if (layout instanceof ColumnLayout) -// return hHint; - } - return SWT.DEFAULT; - } - - public static int computeMinimumWidth(Control c, boolean changed) { - if (c instanceof Composite) { - Layout layout = ((Composite) c).getLayout(); -// if (layout instanceof ILayoutExtension) -// return ((ILayoutExtension) layout).computeMinimumWidth( -// (Composite) c, changed); - } - return c.computeSize(FormUtil.getWidthHint(5, c), SWT.DEFAULT, changed).x; - } - - public static int computeMaximumWidth(Control c, boolean changed) { - if (c instanceof Composite) { - Layout layout = ((Composite) c).getLayout(); -// if (layout instanceof ILayoutExtension) -// return ((ILayoutExtension) layout).computeMaximumWidth( -// (Composite) c, changed); - } - return c.computeSize(SWT.DEFAULT, SWT.DEFAULT, changed).x; - } - -// public static Form getForm(Control c) { -// Composite parent = c.getParent(); -// while (parent != null) { -// if (parent instanceof Form) { -// return (Form) parent; -// } -// parent = parent.getParent(); -// } -// return null; -// } - -// RAP [rh] FormUtil#createAlphaMashImage unnecessary -// public static Image createAlphaMashImage(Device device, Image srcImage) { -// Rectangle bounds = srcImage.getBounds(); -// int alpha = 0; -// int calpha = 0; -// ImageData data = srcImage.getImageData(); -// // Create a new image with alpha values alternating -// // between fully transparent (0) and fully opaque (255). -// // This image will show the background through the -// // transparent pixels. -// for (int i = 0; i < bounds.height; i++) { -// // scan line -// alpha = calpha; -// for (int j = 0; j < bounds.width; j++) { -// // column -// data.setAlpha(j, i, alpha); -// alpha = alpha == 255 ? 0 : 255; -// } -// calpha = calpha == 255 ? 0 : 255; -// } -// return new Image(device, data); -// } - - public static boolean mnemonicMatch(String text, char key) { - char mnemonic = findMnemonic(text); - if (mnemonic == '\0') - return false; - return Character.toUpperCase(key) == Character.toUpperCase(mnemonic); - } - - private static char findMnemonic(String string) { - int index = 0; - int length = string.length(); - do { - while (index < length && string.charAt(index) != '&') - index++; - if (++index >= length) - return '\0'; - if (string.charAt(index) != '&') - return string.charAt(index); - index++; - } while (index < length); - return '\0'; - } - - public static void setFocusScrollingEnabled(Control c, boolean enabled) { - ScrolledComposite scomp = null; - - if (c instanceof ScrolledComposite) - scomp = (ScrolledComposite)c; - else - scomp = getScrolledComposite(c); - if (scomp!=null) - scomp.setData(FormUtil.FOCUS_SCROLLING, enabled?null:Boolean.FALSE); - } - - // RAP [rh] FormUtil#setAntialias unnecessary -// public static void setAntialias(GC gc, int style) { -// if (!gc.getAdvanced()) { -// gc.setAdvanced(true); -// if (!gc.getAdvanced()) -// return; -// } -// gc.setAntialias(style); -// } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IFormColors.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IFormColors.java deleted file mode 100644 index cf0e5d3..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IFormColors.java +++ /dev/null @@ -1,102 +0,0 @@ -package org.argeo.cms.ui.eclipse.forms; - -/** - * A place to hold all the color constants used in the forms package. - * - * @since 1.0 - */ - -public interface IFormColors { - /** - * A prefix for all the keys. - */ - String PREFIX = "org.eclipse.ui.forms."; //$NON-NLS-1$ - /** - * Key for the form title foreground color. - */ - String TITLE = PREFIX + "TITLE"; //$NON-NLS-1$ - - /** - * A prefix for the header color constants. - */ - String H_PREFIX = PREFIX + "H_"; //$NON-NLS-1$ - /* - * A prefix for the section title bar color constants. - */ - String TB_PREFIX = PREFIX + "TB_"; //$NON-NLS-1$ - /** - * Key for the form header background gradient ending color. - */ - String H_GRADIENT_END = H_PREFIX + "GRADIENT_END"; //$NON-NLS-1$ - - /** - * Key for the form header background gradient starting color. - * - */ - String H_GRADIENT_START = H_PREFIX + "GRADIENT_START"; //$NON-NLS-1$ - /** - * Key for the form header bottom keyline 1 color. - * - */ - String H_BOTTOM_KEYLINE1 = H_PREFIX + "BOTTOM_KEYLINE1"; //$NON-NLS-1$ - /** - * Key for the form header bottom keyline 2 color. - * - */ - String H_BOTTOM_KEYLINE2 = H_PREFIX + "BOTTOM_KEYLINE2"; //$NON-NLS-1$ - /** - * Key for the form header light hover color. - * - */ - String H_HOVER_LIGHT = H_PREFIX + "H_HOVER_LIGHT"; //$NON-NLS-1$ - /** - * Key for the form header full hover color. - * - */ - String H_HOVER_FULL = H_PREFIX + "H_HOVER_FULL"; //$NON-NLS-1$ - - /** - * Key for the tree/table border color. - */ - String BORDER = PREFIX + "BORDER"; //$NON-NLS-1$ - - /** - * Key for the section separator color. - */ - String SEPARATOR = PREFIX + "SEPARATOR"; //$NON-NLS-1$ - - /** - * Key for the section title bar background. - */ - String TB_BG = TB_PREFIX + "BG"; //$NON-NLS-1$ - - /** - * Key for the section title bar foreground. - */ - String TB_FG = TB_PREFIX + "FG"; //$NON-NLS-1$ - - /** - * Key for the section title bar gradient. - * @deprecated Since 3.3, this color is not used any more. The - * tool bar gradient is created starting from {@link #TB_BG} to - * the section background color. - */ - String TB_GBG = TB_BG; - - /** - * Key for the section title bar border. - */ - String TB_BORDER = TB_PREFIX + "BORDER"; //$NON-NLS-1$ - - /** - * Key for the section toggle color. Since 3.1, this color is used for all - * section styles. - */ - String TB_TOGGLE = TB_PREFIX + "TOGGLE"; //$NON-NLS-1$ - - /** - * Key for the section toggle hover color. - * - */ - String TB_TOGGLE_HOVER = TB_PREFIX + "TOGGLE_HOVER"; //$NON-NLS-1$ -} \ No newline at end of file diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IFormPart.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IFormPart.java deleted file mode 100644 index 954cc03..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IFormPart.java +++ /dev/null @@ -1,108 +0,0 @@ -package org.argeo.cms.ui.eclipse.forms; - -/** - * Classes that implement this interface can be added to the managed form and - * take part in the form life cycle. The part is initialized with the form and - * will be asked to accept focus. The part can receive form input and can elect - * to do something according to it (for example, select an object that matches - * the input). - *

- * The form part has two 'out of sync' states in respect to the model(s) that - * feed the form: dirty and stale. When a part is dirty, it - * means that the user interacted with it and now its widgets contain state that - * is newer than the model. In order to sync up with the model, 'commit' needs - * to be called. In contrast, the model can change 'under' the form (as a result - * of some actions outside the form), resulting in data in the model being - * 'newer' than the content presented in the form. A 'stale' form part is - * brought in sync with the model by calling 'refresh'. The part is responsible - * for notifying the form when one of these states change in the part. The form - * reserves the right to handle this notification in the most appropriate way - * for the situation (for example, if the form is in a page of the multi-page - * editor, it may do nothing for stale parts if the page is currently not - * showing). - *

- * When the form is disposed, each registered part is disposed as well. Parts - * are responsible for releasing any system resources they created and for - * removing themselves as listeners from all event providers. - * - * @see IManagedForm - * @since 1.0 - * - */ -public interface IFormPart { - /** - * Initializes the part. - * - * @param form - * the managed form that manages the part - */ - void initialize(IManagedForm form); - - /** - * Disposes the part allowing it to release allocated resources. - */ - void dispose(); - - /** - * Returns true if the part has been modified with respect to the data - * loaded from the model. - * - * @return true if the part has been modified with respect to the data - * loaded from the model - */ - boolean isDirty(); - - /** - * If part is displaying information loaded from a model, this method - * instructs it to commit the new (modified) data back into the model. - * - * @param onSave - * indicates if commit is called during 'save' operation or for - * some other reason (for example, if form is contained in a - * wizard or a multi-page editor and the user is about to leave - * the page). - */ - void commit(boolean onSave); - - /** - * Notifies the part that an object has been set as overall form's input. - * The part can elect to react by revealing or selecting the object, or do - * nothing if not applicable. - * - * @return true if the part has selected and revealed the - * input object, false otherwise. - */ - boolean setFormInput(Object input); - - /** - * Instructs form part to transfer focus to the widget that should has focus - * in that part. The method can do nothing (if it has no widgets capable of - * accepting focus). - */ - void setFocus(); - - /** - * Tests whether the form part is stale and needs refreshing. Parts can - * receive notification from models that will make their content stale, but - * may need to delay refreshing to improve performance (for example, there - * is no need to immediately refresh a part on a form that is current on a - * hidden page). - *

- * It is important to differentiate 'stale' and 'dirty' states. Part is - * 'dirty' if user interacted with its editable widgets and changed the - * values. In contrast, part is 'stale' when the data it presents in the - * widgets has been changed in the model without direct user interaction. - * - * @return true if the part needs refreshing, - * false otherwise. - */ - boolean isStale(); - - /** - * Refreshes the part completely from the information freshly obtained from - * the model. The method will not be called if the part is not stale. - * Otherwise, the part is responsible for clearing the 'stale' flag after - * refreshing itself. - */ - void refresh(); -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IManagedForm.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IManagedForm.java deleted file mode 100644 index 490d3a3..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IManagedForm.java +++ /dev/null @@ -1,175 +0,0 @@ -package org.argeo.cms.ui.eclipse.forms; - -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.swt.custom.ScrolledComposite; -//import org.eclipse.ui.forms.widgets.FormToolkit; -//import org.eclipse.ui.forms.widgets.ScrolledForm; - -/** - * Managed form wraps a form widget and adds life cycle methods for form parts. - * A form part is a portion of the form that participates in form life cycle - * events. - *

- * There is no 1/1 mapping between widgets and form parts. A widget like Section - * can be a part by itself, but a number of widgets can gather around one form - * part. - *

- * This interface should not be extended or implemented. New form instances - * should be created using ManagedForm. - * - * @see ManagedForm - * @since 1.0 - * @noimplement This interface is not intended to be implemented by clients. - * @noextend This interface is not intended to be extended by clients. - */ -public interface IManagedForm { - /** - * Initializes the form by looping through the managed parts and - * initializing them. Has no effect if already called once. - */ - public void initialize(); - - /** - * Returns the toolkit used by this form. - * - * @return the toolkit - */ - public FormToolkit getToolkit(); - - /** - * Returns the form widget managed by this form. - * - * @return the form widget - */ - public ScrolledComposite getForm(); - - /** - * Reflows the form as a result of the layout change. - * - * @param changed - * if true, discard cached layout information - */ - public void reflow(boolean changed); - - /** - * A part can use this method to notify other parts that implement - * IPartSelectionListener about selection changes. - * - * @param part - * the part that broadcasts the selection - * @param selection - * the selection in the part - */ - public void fireSelectionChanged(IFormPart part, ISelection selection); - - /** - * Returns all the parts currently managed by this form. - * - * @return the managed parts - */ - IFormPart[] getParts(); - - /** - * Adds the new part to the form. - * - * @param part - * the part to add - */ - void addPart(IFormPart part); - - /** - * Removes the part from the form. - * - * @param part - * the part to remove - */ - void removePart(IFormPart part); - - /** - * Sets the input of this page to the provided object. - * - * @param input - * the new page input - * @return true if the form contains this object, - * false otherwise. - */ - boolean setInput(Object input); - - /** - * Returns the current page input. - * - * @return page input object or null if not applicable. - */ - Object getInput(); - - /** - * Tests if form is dirty. A managed form is dirty if at least one managed - * part is dirty. - * - * @return true if at least one managed part is dirty, - * false otherwise. - */ - boolean isDirty(); - - /** - * Notifies the form that the dirty state of one of its parts has changed. - * The global dirty state of the form can be obtained by calling 'isDirty'. - * - * @see #isDirty - */ - void dirtyStateChanged(); - - /** - * Commits the dirty form. All pending changes in the widgets are flushed - * into the model. - * - * @param onSave - */ - void commit(boolean onSave); - - /** - * Tests if form is stale. A managed form is stale if at least one managed - * part is stale. This can happen when the underlying model changes, - * resulting in the presentation of the part being out of sync with the - * model and needing refreshing. - * - * @return true if the form is stale, false - * otherwise. - */ - boolean isStale(); - - /** - * Notifies the form that the stale state of one of its parts has changed. - * The global stale state of the form can be obtained by calling 'isStale'. - */ - void staleStateChanged(); - - /** - * Refreshes the form by refreshing every part that is stale. - */ - void refresh(); - - /** - * Sets the container that owns this form. Depending on the context, the - * container may be wizard, editor page, editor etc. - * - * @param container - * the container of this form - */ - void setContainer(Object container); - - /** - * Returns the container of this form. - * - * @return the form container - */ - Object getContainer(); - - /** - * Returns the message manager that will keep track of messages in this - * form. - * - * @return the message manager instance - */ -// IMessageManager getMessageManager(); -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IPartSelectionListener.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IPartSelectionListener.java deleted file mode 100644 index 0f557d4..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IPartSelectionListener.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.argeo.cms.ui.eclipse.forms; - -import org.eclipse.jface.viewers.ISelection; - -/** - * Form parts can implement this interface if they want to be - * notified when another part on the same form changes selection - * state. - * - * @see IFormPart - * @since 1.0 - */ -public interface IPartSelectionListener { - /** - * Called when the provided part has changed selection state. - * - * @param part - * the selection source - * @param selection - * the new selection - */ - public void selectionChanged(IFormPart part, ISelection selection); -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/ManagedForm.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/ManagedForm.java deleted file mode 100644 index 4140465..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/ManagedForm.java +++ /dev/null @@ -1,323 +0,0 @@ -package org.argeo.cms.ui.eclipse.forms; - -import java.util.Vector; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.swt.custom.ScrolledComposite; -import org.eclipse.swt.widgets.Composite; -//import org.eclipse.ui.forms.widgets.FormToolkit; -//import org.eclipse.ui.forms.widgets.ScrolledForm; - -/** - * Managed form wraps a form widget and adds life cycle methods for form parts. - * A form part is a portion of the form that participates in form life cycle - * events. - *

- * There is requirement for 1/1 mapping between widgets and form parts. A widget - * like Section can be a part by itself, but a number of widgets can join around - * one form part. - *

- * Note to developers: this class is left public to allow its use beyond the - * original intention (inside a multi-page editor's page). You should limit the - * use of this class to make new instances inside a form container (wizard page, - * dialog etc.). Clients that need access to the class should not do it - * directly. Instead, they should do it through IManagedForm interface as much - * as possible. - * - * @since 1.0 - */ -public class ManagedForm implements IManagedForm { - private Object input; - - private ScrolledComposite form; - - private FormToolkit toolkit; - - private Object container; - - private boolean ownsToolkit; - - private boolean initialized; - - private Vector parts = new Vector(); - - /** - * Creates a managed form in the provided parent. Form toolkit and widget - * will be created and owned by this object. - * - * @param parent - * the parent widget - */ - public ManagedForm(Composite parent) { - toolkit = new FormToolkit(parent.getDisplay()); - ownsToolkit = true; - form = toolkit.createScrolledForm(parent); - - } - - /** - * Creates a managed form that will use the provided toolkit and - * - * @param toolkit - * @param form - */ - public ManagedForm(FormToolkit toolkit, ScrolledComposite form) { - this.form = form; - this.toolkit = toolkit; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#addPart(org.eclipse.ui.forms.IFormPart) - */ - public void addPart(IFormPart part) { - parts.add(part); - part.initialize(this); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#removePart(org.eclipse.ui.forms.IFormPart) - */ - public void removePart(IFormPart part) { - parts.remove(part); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#getParts() - */ - public IFormPart[] getParts() { - return (IFormPart[]) parts.toArray(new IFormPart[parts.size()]); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#getToolkit() - */ - public FormToolkit getToolkit() { - return toolkit; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#getForm() - */ - public ScrolledComposite getForm() { - return form; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#reflow(boolean) - */ - public void reflow(boolean changed) { -// form.reflow(changed); - } - - /** - * A part can use this method to notify other parts that implement - * IPartSelectionListener about selection changes. - * - * @param part - * the part that broadcasts the selection - * @param selection - * the selection in the part - * @see IPartSelectionListener - */ - public void fireSelectionChanged(IFormPart part, ISelection selection) { - for (int i = 0; i < parts.size(); i++) { - IFormPart cpart = (IFormPart) parts.get(i); - if (part.equals(cpart)) - continue; -// if (cpart instanceof IPartSelectionListener) { -// ((IPartSelectionListener) cpart).selectionChanged(part, -// selection); -// } - } - } - - /** - * Initializes the form by looping through the managed parts and - * initializing them. Has no effect if already called once. - */ - public void initialize() { - if (initialized) - return; - for (int i = 0; i < parts.size(); i++) { - IFormPart part = (IFormPart) parts.get(i); - part.initialize(this); - } - initialized = true; - } - - /** - * Disposes all the parts in this form. - */ - public void dispose() { - for (int i = 0; i < parts.size(); i++) { - IFormPart part = (IFormPart) parts.get(i); - part.dispose(); - } - if (ownsToolkit) { - toolkit.dispose(); - } - } - - /** - * Refreshes the form by refreshes all the stale parts. Since 3.1, this - * method is performed on a UI thread when called from another thread so it - * is not needed to wrap the call in Display.syncExec or - * asyncExec. - */ - public void refresh() { - Thread t = Thread.currentThread(); - Thread dt = toolkit.getColors().getDisplay().getThread(); - if (t.equals(dt)) - doRefresh(); - else { - toolkit.getColors().getDisplay().asyncExec(new Runnable() { - public void run() { - doRefresh(); - } - }); - } - } - - private void doRefresh() { - int nrefreshed = 0; - for (int i = 0; i < parts.size(); i++) { - IFormPart part = (IFormPart) parts.get(i); - if (part.isStale()) { - part.refresh(); - nrefreshed++; - } - } -// if (nrefreshed > 0) -// form.reflow(true); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#commit(boolean) - */ - public void commit(boolean onSave) { - for (int i = 0; i < parts.size(); i++) { - IFormPart part = (IFormPart) parts.get(i); - if (part.isDirty()) - part.commit(onSave); - } - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#setInput(java.lang.Object) - */ - public boolean setInput(Object input) { - boolean pageResult = false; - - this.input = input; - for (int i = 0; i < parts.size(); i++) { - IFormPart part = (IFormPart) parts.get(i); - boolean result = part.setFormInput(input); - if (result) - pageResult = true; - } - return pageResult; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#getInput() - */ - public Object getInput() { - return input; - } - - /** - * Transfers the focus to the first form part. - */ - public void setFocus() { - if (parts.size() > 0) { - IFormPart part = (IFormPart) parts.get(0); - part.setFocus(); - } - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#isDirty() - */ - public boolean isDirty() { - for (int i = 0; i < parts.size(); i++) { - IFormPart part = (IFormPart) parts.get(i); - if (part.isDirty()) - return true; - } - return false; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#isStale() - */ - public boolean isStale() { - for (int i = 0; i < parts.size(); i++) { - IFormPart part = (IFormPart) parts.get(i); - if (part.isStale()) - return true; - } - return false; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#dirtyStateChanged() - */ - public void dirtyStateChanged() { - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#staleStateChanged() - */ - public void staleStateChanged() { - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#getContainer() - */ - public Object getContainer() { - return container; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#setContainer(java.lang.Object) - */ - public void setContainer(Object container) { - this.container = container; - } - - /* (non-Javadoc) - * @see org.eclipse.ui.forms.IManagedForm#getMessageManager() - */ -// public IMessageManager getMessageManager() { -// return form.getMessageManager(); -// } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/editor/FormEditor.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/editor/FormEditor.java deleted file mode 100644 index 484dae8..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/editor/FormEditor.java +++ /dev/null @@ -1,85 +0,0 @@ -package org.argeo.cms.ui.eclipse.forms.editor; - -import org.argeo.cms.ui.eclipse.forms.FormToolkit; -import org.eclipse.jface.dialogs.IPageChangeProvider; - -/** - * This class forms a base of multi-page form editors that typically use one or - * more pages with forms and one page for raw source of the editor input. - *

- * Pages are added 'lazily' i.e. adding a page reserves a tab for it but does - * not cause the page control to be created. Page control is created when an - * attempt is made to select the page in question. This allows editors with - * several tabs and complex pages to open quickly. - *

- * Subclasses should extend this class and implement addPages - * method. One of the two addPage methods should be called to - * contribute pages to the editor. One adds complete (standalone) editors as - * nested tabs. These editors will be created right away and will be hooked so - * that key bindings, selection service etc. is compatible with the one for the - * standalone case. The other method adds classes that implement - * IFormPage interface. These pages will be created lazily and - * they will share the common key binding and selection service. Since 3.1, - * FormEditor is a page change provider. It allows listeners to attach to it and - * get notified when pages are changed. This new API in JFace allows dynamic - * help to update on page changes. - * - * @since 1.0 - */ -// RAP [if] As RAP is still using workbench 3.4, the implementation of -// IPageChangeProvider is missing from MultiPageEditorPart. Remove this code -// with the adoption of workbench > 3.5 -//public abstract class FormEditor extends MultiPageEditorPart { -public abstract class FormEditor implements - IPageChangeProvider { - private FormToolkit formToolkit; - - -public FormToolkit getToolkit() { - return formToolkit; - } - -public void editorDirtyStateChanged() { - -} - -public FormPage getActivePageInstance() { - return null; -} - - // RAP [if] As RAP is still using workbench 3.4, the implementation of -// IPageChangeProvider is missing from MultiPageEditorPart. Remove this code -// with the adoption of workbench > 3.5 -// private ListenerList pageListeners = new ListenerList(); -// -// /* -// * (non-Javadoc) -// * -// * @see org.eclipse.jface.dialogs.IPageChangeProvider#addPageChangedListener(org.eclipse.jface.dialogs.IPageChangedListener) -// */ -// public void addPageChangedListener(IPageChangedListener listener) { -// pageListeners.add(listener); -// } -// -// /* -// * (non-Javadoc) -// * -// * @see org.eclipse.jface.dialogs.IPageChangeProvider#removePageChangedListener(org.eclipse.jface.dialogs.IPageChangedListener) -// */ -// public void removePageChangedListener(IPageChangedListener listener) { -// pageListeners.remove(listener); -// } -// -// private void firePageChanged(final PageChangedEvent event) { -// Object[] listeners = pageListeners.getListeners(); -// for (int i = 0; i < listeners.length; ++i) { -// final IPageChangedListener l = (IPageChangedListener) listeners[i]; -// SafeRunnable.run(new SafeRunnable() { -// public void run() { -// l.pageChanged(event); -// } -// }); -// } -// } -// RAPEND [if] -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/editor/FormPage.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/editor/FormPage.java deleted file mode 100644 index a788412..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/editor/FormPage.java +++ /dev/null @@ -1,276 +0,0 @@ -package org.argeo.cms.ui.eclipse.forms.editor; -import org.argeo.cms.ui.eclipse.forms.IManagedForm; -import org.argeo.cms.ui.eclipse.forms.ManagedForm; -import org.eclipse.swt.custom.BusyIndicator; -import org.eclipse.swt.custom.ScrolledComposite; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -/** - * A base class that all pages that should be added to FormEditor must subclass. - * Form page has an instance of PageForm that extends managed form. Subclasses - * should override method 'createFormContent(ManagedForm)' to fill the form with - * content. Note that page itself can be loaded lazily (on first open). - * Consequently, the call to create the form content can come after the editor - * has been opened for a while (in fact, it is possible to open and close the - * editor and never create the form because no attempt has been made to show the - * page). - * - * @since 1.0 - */ -public class FormPage implements IFormPage { - private FormEditor editor; - private PageForm mform; - private int index; - private String id; - - private String partName; - - - - public void setPartName(String partName) { - this.partName = partName; - } - private static class PageForm extends ManagedForm { - public PageForm(FormPage page, ScrolledComposite form) { - super(page.getEditor().getToolkit(), form); - setContainer(page); - } - - public FormPage getPage() { - return (FormPage)getContainer(); - } - public void dirtyStateChanged() { - getPage().getEditor().editorDirtyStateChanged(); - } - public void staleStateChanged() { - if (getPage().isActive()) - refresh(); - } - } - /** - * A constructor that creates the page and initializes it with the editor. - * - * @param editor - * the parent editor - * @param id - * the unique identifier - * @param title - * the page title - */ - public FormPage(FormEditor editor, String id, String title) { - this(id, title); - initialize(editor); - } - /** - * The constructor. The parent editor need to be passed in the - * initialize method if this constructor is used. - * - * @param id - * a unique page identifier - * @param title - * a user-friendly page title - */ - public FormPage(String id, String title) { - this.id = id; - setPartName(title); - } - /** - * Initializes the form page. - * - * @see IEditorPart#init - */ -// public void init(IEditorSite site, IEditorInput input) { -// setSite(site); -// setInput(input); -// } - /** - * Primes the form page with the parent editor instance. - * - * @param editor - * the parent editor - */ - public void initialize(FormEditor editor) { - this.editor = editor; - } - /** - * Returns the parent editor. - * - * @return parent editor instance - */ - public FormEditor getEditor() { - return editor; - } - /** - * Returns the managed form owned by this page. - * - * @return the managed form - */ - public IManagedForm getManagedForm() { - return mform; - } - /** - * Implements the required method by refreshing the form when set active. - * Subclasses must call super when overriding this method. - */ - public void setActive(boolean active) { - if (active) { - // We are switching to this page - refresh it - // if needed. - if (mform != null) - mform.refresh(); - } - } - /** - * Tests if the page is active by asking the parent editor if this page is - * the currently active page. - * - * @return true if the page is currently active, - * false otherwise. - */ - public boolean isActive() { - return this.equals(editor.getActivePageInstance()); - } - /** - * Creates the part control by creating the managed form using the parent - * editor's toolkit. Subclasses should override - * createFormContent(IManagedForm) to populate the form with - * content. - * - * @param parent - * the page parent composite - */ - public void createPartControl(Composite parent) { - ScrolledComposite form = editor.getToolkit().createScrolledForm(parent); - mform = new PageForm(this, form); - BusyIndicator.showWhile(parent.getDisplay(), new Runnable() { - public void run() { - createFormContent(mform); - } - }); - } - /** - * Subclasses should override this method to create content in the form - * hosted in this page. - * - * @param managedForm - * the form hosted in this page. - */ - protected void createFormContent(IManagedForm managedForm) { - } - /** - * Returns the form page control. - * - * @return managed form's control - */ - public Control getPartControl() { - return mform != null ? mform.getForm() : null; - } - /** - * Disposes the managed form. - */ - public void dispose() { - if (mform != null) - mform.dispose(); - } - /** - * Returns the unique identifier that can be used to reference this page. - * - * @return the unique page identifier - */ - public String getId() { - return id; - } - /** - * Returns null- form page has no title image. Subclasses - * may override. - * - * @return null - */ - public Image getTitleImage() { - return null; - } - /** - * Sets the focus by delegating to the managed form. - */ - public void setFocus() { - if (mform != null) - mform.setFocus(); - } - /** - * @see org.eclipse.ui.ISaveablePart#doSave(org.eclipse.core.runtime.IProgressMonitor) - */ -// public void doSave(IProgressMonitor monitor) { -// if (mform != null) -// mform.commit(true); -// } - /** - * @see org.eclipse.ui.ISaveablePart#doSaveAs() - */ - public void doSaveAs() { - } - /** - * @see org.eclipse.ui.ISaveablePart#isSaveAsAllowed() - */ - public boolean isSaveAsAllowed() { - return false; - } - /** - * Implemented by testing if the managed form is dirty. - * - * @return true if the managed form is dirty, - * false otherwise. - * - * @see org.eclipse.ui.ISaveablePart#isDirty() - */ - public boolean isDirty() { - return mform != null ? mform.isDirty() : false; - } - /** - * Preserves the page index. - * - * @param index - * the assigned page index - */ - public void setIndex(int index) { - this.index = index; - } - /** - * Returns the saved page index. - * - * @return the page index - */ - public int getIndex() { - return index; - } - /** - * Form pages are not editors. - * - * @return false - */ - public boolean isEditor() { - return false; - } - /** - * Attempts to select and reveal the given object by passing the request to - * the managed form. - * - * @param object - * the object to select and reveal in the page if possible. - * @return true if the page has been successfully selected - * and revealed by one of the managed form parts, false - * otherwise. - */ - public boolean selectReveal(Object object) { - if (mform != null) - return mform.setInput(object); - return false; - } - /** - * By default, editor will be allowed to flip the page. - * @return true - */ - public boolean canLeaveThePage() { - return true; - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/editor/IFormPage.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/editor/IFormPage.java deleted file mode 100644 index eb08cb5..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/editor/IFormPage.java +++ /dev/null @@ -1,119 +0,0 @@ -package org.argeo.cms.ui.eclipse.forms.editor; -import org.argeo.cms.ui.eclipse.forms.IManagedForm; -import org.eclipse.swt.widgets.Control; -/** - * Interface that all GUI pages need to implement in order - * to be added to FormEditor part. The interface makes - * several assumptions: - *

    - *
  • The form page has a managed form
  • - *
  • The form page has a unique id
  • - *
  • The form page can be GUI but can also wrap a complete - * editor class (in that case, it should return true - * from isEditor() method).
  • - *
  • The form page is lazy i.e. understands that - * its part control will be created at the last possible - * moment.
  • . - *
- *

Existing editors can be wrapped by implementing - * this interface. In this case, 'isEditor' should return true. - * A common editor to wrap in TextEditor that is - * often added to show the raw source code of the file open into - * the multi-page editor. - * - * @since 1.0 - */ -public interface IFormPage { - /** - * @param editor - * the form editor that this page belongs to - */ - void initialize(FormEditor editor); - /** - * Returns the editor this page belongs to. - * - * @return the form editor - */ - FormEditor getEditor(); - /** - * Returns the managed form of this page, unless this is a source page. - * - * @return the managed form or null if this is a source page. - */ - IManagedForm getManagedForm(); - /** - * Indicates whether the page has become the active in the editor. Classes - * that implement this interface may use this method to commit the page (on - * false) or lazily create and/or populate the content on - * true. - * - * @param active - * true if page should be visible, false - * otherwise. - */ - void setActive(boolean active); - /** - * Returns true if page is currently active, false if not. - * - * @return true for active page. - */ - boolean isActive(); - /** - * Tests if the content of the page is in a state that allows the - * editor to flip to another page. Typically, pages that contain - * raw source with syntax errors should not allow editors to - * leave them until errors are corrected. - * @return true if the editor can flip to another page, - * false otherwise. - */ - boolean canLeaveThePage(); - /** - * Returns the control associated with this page. - * - * @return the control of this page if created or null if the - * page has not been shown yet. - */ - Control getPartControl(); - /** - * Page must have a unique id that can be used to show it without knowing - * its relative position in the editor. - * - * @return the unique page identifier - */ - String getId(); - /** - * Returns the position of the page in the editor. - * - * @return the zero-based index of the page in the editor. - */ - int getIndex(); - /** - * Sets the position of the page in the editor. - * - * @param index - * the zero-based index of the page in the editor. - */ - void setIndex(int index); - /** - * Tests whether this page wraps a complete editor that - * can be registered on its own, or represents a page - * that cannot exist outside the multi-page editor context. - * - * @return true if the page wraps an editor, - * false if this is a form page. - */ - boolean isEditor(); - /** - * A hint to bring the provided object into focus. If the object is in a - * tree or table control, select it. If it is shown on a scrollable page, - * ensure that it is visible. If the object is not presented in - * the page, false should be returned to allow another - * page to try. - * - * @param object - * object to select and reveal - * @return true if the request was successful, false - * otherwise. - */ - boolean selectReveal(Object object); -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditableLink.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditableLink.java deleted file mode 100644 index 3c1e8cd..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditableLink.java +++ /dev/null @@ -1,75 +0,0 @@ -package org.argeo.cms.ui.forms; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.swt.SwtEditablePart; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; - -/** Editable String that displays a browsable link when read-only */ -public class EditableLink extends EditablePropertyString implements - SwtEditablePart { - private static final long serialVersionUID = 5055000749992803591L; - - private String type; - private String message; - private boolean readOnly; - - public EditableLink(Composite parent, int style, Node node, - String propertyName, String type, String message) - throws RepositoryException { - super(parent, style, node, propertyName, message); - this.message = message; - this.type = type; - - readOnly = SWT.READ_ONLY == (style & SWT.READ_ONLY); - if (node.hasProperty(propertyName)) { - this.setStyle(FormStyle.propertyText.style()); - this.setText(node.getProperty(propertyName).getString()); - } else { - this.setStyle(FormStyle.propertyMessage.style()); - this.setText(""); - } - } - - public void setText(String text) { - Control child = getControl(); - if (child instanceof Label) { - Label lbl = (Label) child; - if (EclipseUiUtils.isEmpty(text)) - lbl.setText(message); - else if (readOnly) - setLinkValue(lbl, text); - else - // if canEdit() we put only the value with no link - // to avoid glitches of the edition life cycle - lbl.setText(text); - } else if (child instanceof Text) { - Text txt = (Text) child; - if (EclipseUiUtils.isEmpty(text)) { - txt.setText(""); - txt.setMessage(message); - } else - txt.setText(text); - } - } - - private void setLinkValue(Label lbl, String text) { - if (FormStyle.email.style().equals(type)) - lbl.setText(FormUtils.getMailLink(text)); - else if (FormStyle.phone.style().equals(type)) - lbl.setText(FormUtils.getPhoneLink(text)); - else if (FormStyle.website.style().equals(type)) - lbl.setText(FormUtils.getUrlLink(text)); - else if (FormStyle.facebook.style().equals(type) - || FormStyle.instagram.style().equals(type) - || FormStyle.linkedIn.style().equals(type) - || FormStyle.twitter.style().equals(type)) - lbl.setText(FormUtils.getUrlLink(text)); - } -} \ No newline at end of file diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditableMultiStringProperty.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditableMultiStringProperty.java deleted file mode 100644 index ff82700..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditableMultiStringProperty.java +++ /dev/null @@ -1,261 +0,0 @@ -package org.argeo.cms.ui.forms; - -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.swt.SwtEditablePart; -import org.argeo.cms.ui.widgets.StyledControl; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.events.TraverseEvent; -import org.eclipse.swt.events.TraverseListener; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.layout.RowLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; - -/** Display, add or remove values from a list in a CMS context */ -public class EditableMultiStringProperty extends StyledControl implements SwtEditablePart { - private static final long serialVersionUID = -7044614381252178595L; - - private String propertyName; - private String message; - // TODO implement the ability to provide a list of possible values -// private String[] possibleValues; - private boolean canEdit; - private SelectionListener removeValueSL; - private List values; - - // TODO manage within the CSS - private int rowSpacing = 5; - private int rowMarging = 0; - private int oneValueMargingRight = 5; - private int btnWidth = 16; - private int btnHeight = 16; - private int btnHorizontalIndent = 3; - - public EditableMultiStringProperty(Composite parent, int style, Node node, String propertyName, List values, - String[] possibleValues, String addValueMsg, SelectionListener removeValueSelectionListener) - throws RepositoryException { - super(parent, style, node, true); - - this.propertyName = propertyName; - this.values = values; -// this.possibleValues = new String[] { "Un", "Deux", "Trois" }; - this.message = addValueMsg; - this.canEdit = removeValueSelectionListener != null; - this.removeValueSL = removeValueSelectionListener; - } - - public List getValues() { - return values; - } - - public void setValues(List values) { - this.values = values; - } - - // Row layout items do not need explicit layout data - protected void setControlLayoutData(Control control) { - } - - /** To be overridden */ - protected void setContainerLayoutData(Composite composite) { - composite.setLayoutData(CmsSwtUtils.fillWidth()); - } - - @Override - public Control getControl() { - return super.getControl(); - } - - @Override - protected Control createControl(Composite box, String style) { - Composite row = new Composite(box, SWT.NO_FOCUS); - row.setLayoutData(EclipseUiUtils.fillAll()); - - RowLayout rl = new RowLayout(SWT.HORIZONTAL); - rl.wrap = true; - rl.spacing = rowSpacing; - rl.marginRight = rl.marginLeft = rl.marginBottom = rl.marginTop = rowMarging; - row.setLayout(rl); - - if (values != null) { - for (final String value : values) { - if (canEdit) - createRemovableValue(row, SWT.SINGLE, value); - else - createValueLabel(row, SWT.SINGLE, value); - } - } - - if (!canEdit) - return row; - else if (isEditing()) - return createText(row, style); - else - return createLabel(row, style); - } - - /** - * Override to provide specific layout for the existing values, typically adding - * a pound (#) char for tags or anchor info for browsable links. We assume the - * parent composite already has a layout and it is the caller responsibility to - * apply corresponding layout data - */ - protected Label createValueLabel(Composite parent, int style, String value) { - Label label = new Label(parent, style); - label.setText("#" + value); - CmsSwtUtils.markup(label); - CmsSwtUtils.style(label, FormStyle.propertyText.style()); - return label; - } - - private Composite createRemovableValue(Composite parent, int style, String value) { - Composite valCmp = new Composite(parent, SWT.NO_FOCUS); - GridLayout gl = EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, false)); - gl.marginRight = oneValueMargingRight; - valCmp.setLayout(gl); - - createValueLabel(valCmp, SWT.WRAP, value); - - Button deleteBtn = new Button(valCmp, SWT.FLAT); - deleteBtn.setData(FormConstants.LINKED_VALUE, value); - deleteBtn.addSelectionListener(removeValueSL); - CmsSwtUtils.style(deleteBtn, FormStyle.delete.style() + FormStyle.BUTTON_SUFFIX); - GridData gd = new GridData(); - gd.heightHint = btnHeight; - gd.widthHint = btnWidth; - gd.horizontalIndent = btnHorizontalIndent; - deleteBtn.setLayoutData(gd); - - return valCmp; - } - - protected Text createText(Composite box, String style) { - final Text text = new Text(box, getStyle()); - // The "add new value" text is not meant to change, so we can set it on - // creation - text.setMessage(message); - CmsSwtUtils.style(text, style); - text.setFocus(); - - text.addTraverseListener(new TraverseListener() { - private static final long serialVersionUID = 1L; - - public void keyTraversed(TraverseEvent e) { - if (e.keyCode == SWT.CR) { - addValue(text); - e.doit = false; - } - } - }); - - // The OK button does not work with the focusOut listener - // because focus out is called before the OK button is pressed - - // // we must call layout() now so that the row data can compute the - // height - // // of the other controls. - // text.getParent().layout(); - // int height = text.getSize().y; - // - // Button okBtn = new Button(box, SWT.BORDER | SWT.PUSH | SWT.BOTTOM); - // okBtn.setText("OK"); - // RowData rd = new RowData(SWT.DEFAULT, height - 2); - // okBtn.setLayoutData(rd); - // - // okBtn.addSelectionListener(new SelectionAdapter() { - // private static final long serialVersionUID = 2780819012423622369L; - // - // @Override - // public void widgetSelected(SelectionEvent e) { - // addValue(text); - // } - // }); - - return text; - } - - /** Performs the real addition, overwrite to make further sanity checks */ - protected void addValue(Text text) { - String value = text.getText(); - String errMsg = null; - - if (EclipseUiUtils.isEmpty(value)) - return; - - if (values.contains(value)) - errMsg = "Dupplicated value: " + value + ", please correct and try again"; - if (errMsg != null) - MessageDialog.openError(this.getShell(), "Addition not allowed", errMsg); - else { - values.add(value); - Composite newCmp = createRemovableValue(text.getParent(), SWT.SINGLE, value); - newCmp.moveAbove(text); - text.setText(""); - newCmp.getParent().layout(); - } - } - - protected Label createLabel(Composite box, String style) { - if (canEdit) { - Label lbl = new Label(box, getStyle()); - lbl.setText(message); - CmsSwtUtils.style(lbl, style); - CmsSwtUtils.markup(lbl); - if (mouseListener != null) - lbl.addMouseListener(mouseListener); - return lbl; - } - return null; - } - - protected void clear(boolean deep) { - Control child = getControl(); - if (deep) - super.clear(deep); - else { - child.getParent().dispose(); - } - } - - public void setText(String text) { - Control child = getControl(); - if (child instanceof Label) { - Label lbl = (Label) child; - if (canEdit) - lbl.setText(text); - else - lbl.setText(""); - } else if (child instanceof Text) { - Text txt = (Text) child; - txt.setText(text); - } - } - - public synchronized void startEditing() { - CmsSwtUtils.style(getControl(), FormStyle.propertyText.style()); -// getControl().setData(STYLE, FormStyle.propertyText.style()); - super.startEditing(); - } - - public synchronized void stopEditing() { - CmsSwtUtils.style(getControl(), FormStyle.propertyMessage.style()); -// getControl().setData(STYLE, FormStyle.propertyMessage.style()); - super.stopEditing(); - } - - public String getPropertyName() { - return propertyName; - } -} \ No newline at end of file diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditablePropertyDate.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditablePropertyDate.java deleted file mode 100644 index 641f916..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditablePropertyDate.java +++ /dev/null @@ -1,298 +0,0 @@ -package org.argeo.cms.ui.forms; - -import java.text.DateFormat; -import java.util.Calendar; -import java.util.GregorianCalendar; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.swt.SwtEditablePart; -import org.argeo.cms.ui.widgets.StyledControl; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.events.MouseListener; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.ShellAdapter; -import org.eclipse.swt.events.ShellEvent; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.DateTime; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Text; - -/** CMS form part to display and edit a date */ -public class EditablePropertyDate extends StyledControl implements SwtEditablePart { - private static final long serialVersionUID = 2500215515778162468L; - - // Context - private String propertyName; - private String message; - private DateFormat dateFormat; - - // UI Objects - private Text dateTxt; - private Button openCalBtn; - - // TODO manage within the CSS - private int fieldBtnSpacing = 5; - - /** - * - * @param parent - * @param style - * @param node - * @param propertyName - * @param message - * @param dateFormat provide a {@link DateFormat} as contract to be able to - * read/write dates as strings - * @throws RepositoryException - */ - public EditablePropertyDate(Composite parent, int style, Node node, String propertyName, String message, - DateFormat dateFormat) throws RepositoryException { - super(parent, style, node, false); - - this.propertyName = propertyName; - this.message = message; - this.dateFormat = dateFormat; - - if (node.hasProperty(propertyName)) { - this.setStyle(FormStyle.propertyText.style()); - this.setText(dateFormat.format(node.getProperty(propertyName).getDate().getTime())); - } else { - this.setStyle(FormStyle.propertyMessage.style()); - this.setText(message); - } - } - - public void setText(String text) { - Control child = getControl(); - if (child instanceof Label) { - Label lbl = (Label) child; - if (EclipseUiUtils.isEmpty(text)) - lbl.setText(message); - else - lbl.setText(text); - } else if (child instanceof Text) { - Text txt = (Text) child; - if (EclipseUiUtils.isEmpty(text)) { - txt.setText(""); - } else - txt.setText(text); - } - } - - public synchronized void startEditing() { - // if (dateTxt != null && !dateTxt.isDisposed()) - CmsSwtUtils.style(getControl(), FormStyle.propertyText); -// getControl().setData(STYLE, FormStyle.propertyText.style()); - super.startEditing(); - } - - public synchronized void stopEditing() { - if (EclipseUiUtils.isEmpty(dateTxt.getText())) - CmsSwtUtils.style(getControl(), FormStyle.propertyMessage); -// getControl().setData(STYLE, FormStyle.propertyMessage.style()); - else - CmsSwtUtils.style(getControl(), FormStyle.propertyText); -// getControl().setData(STYLE, FormStyle.propertyText.style()); - super.stopEditing(); - } - - public String getPropertyName() { - return propertyName; - } - - @Override - protected Control createControl(Composite box, String style) { - if (isEditing()) { - return createCustomEditableControl(box, style); - } else - return createLabel(box, style); - } - - protected Label createLabel(Composite box, String style) { - Label lbl = new Label(box, getStyle() | SWT.WRAP); - lbl.setLayoutData(CmsSwtUtils.fillWidth()); - CmsSwtUtils.style(lbl, style); - CmsSwtUtils.markup(lbl); - if (mouseListener != null) - lbl.addMouseListener(mouseListener); - return lbl; - } - - private Control createCustomEditableControl(Composite box, String style) { - box.setLayoutData(CmsSwtUtils.fillWidth()); - Composite dateComposite = new Composite(box, SWT.NONE); - GridLayout gl = EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, false)); - gl.horizontalSpacing = fieldBtnSpacing; - dateComposite.setLayout(gl); - dateTxt = new Text(dateComposite, SWT.BORDER); - CmsSwtUtils.style(dateTxt, style); - dateTxt.setLayoutData(new GridData(120, SWT.DEFAULT)); - dateTxt.setToolTipText( - "Enter a date with form \"" + FormUtils.DEFAULT_SHORT_DATE_FORMAT + "\" or use the calendar"); - openCalBtn = new Button(dateComposite, SWT.FLAT); - CmsSwtUtils.style(openCalBtn, FormStyle.calendar.style() + FormStyle.BUTTON_SUFFIX); - GridData gd = new GridData(SWT.CENTER, SWT.CENTER, false, false); - gd.heightHint = 17; - openCalBtn.setLayoutData(gd); - // openCalBtn.setImage(PeopleRapImages.CALENDAR_BTN); - - openCalBtn.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = 1L; - - public void widgetSelected(SelectionEvent event) { - CalendarPopup popup = new CalendarPopup(dateTxt); - popup.open(); - } - }); - - // dateTxt.addFocusListener(new FocusListener() { - // private static final long serialVersionUID = 1L; - // - // @Override - // public void focusLost(FocusEvent event) { - // String newVal = dateTxt.getText(); - // // Enable reset of the field - // if (FormUtils.notNull(newVal)) - // calendar = null; - // else { - // try { - // Calendar newCal = parseDate(newVal); - // // DateText.this.setText(newCal); - // calendar = newCal; - // } catch (ParseException pe) { - // // Silent. Manage error popup? - // if (calendar != null) - // EditablePropertyDate.this.setText(calendar); - // } - // } - // } - // - // @Override - // public void focusGained(FocusEvent event) { - // } - // }); - return dateTxt; - } - - protected void clear(boolean deep) { - Control child = getControl(); - if (deep || child instanceof Label) - super.clear(deep); - else { - child.getParent().dispose(); - } - } - - /** Enable setting a custom tooltip on the underlying text */ - @Deprecated - public void setToolTipText(String toolTipText) { - dateTxt.setToolTipText(toolTipText); - } - - @Deprecated - /** Enable setting a custom message on the underlying text */ - public void setMessage(String message) { - dateTxt.setMessage(message); - } - - @Deprecated - public void setText(Calendar cal) { - String newValueStr = ""; - if (cal != null) - newValueStr = dateFormat.format(cal.getTime()); - if (!newValueStr.equals(dateTxt.getText())) - dateTxt.setText(newValueStr); - } - - // UTILITIES TO MANAGE THE CALENDAR POPUP - // TODO manage the popup shell in a cleaner way - private class CalendarPopup extends Shell { - private static final long serialVersionUID = 1L; - private DateTime dateTimeCtl; - - public CalendarPopup(Control source) { - super(source.getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP); - populate(); - // Add border and shadow style - CmsSwtUtils.markup(CalendarPopup.this); - CmsSwtUtils.style(CalendarPopup.this, FormStyle.popupCalendar.style()); - pack(); - layout(); - setLocation(source.toDisplay((source.getLocation().x - 2), (source.getSize().y) + 3)); - - addShellListener(new ShellAdapter() { - private static final long serialVersionUID = 5178980294808435833L; - - @Override - public void shellDeactivated(ShellEvent e) { - close(); - dispose(); - } - }); - open(); - } - - private void setProperty() { - // Direct set does not seems to work. investigate - // cal.set(dateTimeCtl.getYear(), dateTimeCtl.getMonth(), - // dateTimeCtl.getDay(), 12, 0); - Calendar cal = new GregorianCalendar(); - cal.set(Calendar.YEAR, dateTimeCtl.getYear()); - cal.set(Calendar.MONTH, dateTimeCtl.getMonth()); - cal.set(Calendar.DAY_OF_MONTH, dateTimeCtl.getDay()); - String dateStr = dateFormat.format(cal.getTime()); - dateTxt.setText(dateStr); - } - - protected void populate() { - setLayout(EclipseUiUtils.noSpaceGridLayout()); - - dateTimeCtl = new DateTime(this, SWT.CALENDAR); - dateTimeCtl.setLayoutData(EclipseUiUtils.fillAll()); - - Calendar calendar = FormUtils.parseDate(dateFormat, dateTxt.getText()); - - if (calendar != null) - dateTimeCtl.setDate(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), - calendar.get(Calendar.DAY_OF_MONTH)); - - dateTimeCtl.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = -8414377364434281112L; - - @Override - public void widgetSelected(SelectionEvent e) { - setProperty(); - } - }); - - dateTimeCtl.addMouseListener(new MouseListener() { - private static final long serialVersionUID = 1L; - - @Override - public void mouseUp(MouseEvent e) { - } - - @Override - public void mouseDown(MouseEvent e) { - } - - @Override - public void mouseDoubleClick(MouseEvent e) { - setProperty(); - close(); - dispose(); - } - }); - } - } -} \ No newline at end of file diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditablePropertyString.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditablePropertyString.java deleted file mode 100644 index f2575e1..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditablePropertyString.java +++ /dev/null @@ -1,80 +0,0 @@ -package org.argeo.cms.ui.forms; - -import static org.argeo.cms.ui.forms.FormStyle.propertyMessage; -import static org.argeo.cms.ui.forms.FormStyle.propertyText; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.swt.SwtEditablePart; -import org.argeo.cms.ui.widgets.EditableText; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; - -/** Editable String in a CMS context */ -public class EditablePropertyString extends EditableText implements SwtEditablePart { - private static final long serialVersionUID = 5055000749992803591L; - - private String propertyName; - private String message; - - // encode the '&' character in rap - private final static String AMPERSAND = "&"; - private final static String AMPERSAND_REGEX = "&(?![#a-zA-Z0-9]+;)"; - - public EditablePropertyString(Composite parent, int style, Node node, String propertyName, String message) - throws RepositoryException { - super(parent, style, node, true); - //setUseTextAsLabel(true); - this.propertyName = propertyName; - this.message = message; - - if (node.hasProperty(propertyName)) { - this.setStyle(propertyText.style()); - this.setText(node.getProperty(propertyName).getString()); - } else { - this.setStyle(propertyMessage.style()); - this.setText(message + " "); - } - } - - public void setText(String text) { - Control child = getControl(); - if (child instanceof Label) { - Label lbl = (Label) child; - if (EclipseUiUtils.isEmpty(text)) - lbl.setText(message + " "); - else - // TODO enhance this - lbl.setText(text.replaceAll(AMPERSAND_REGEX, AMPERSAND)); - } else if (child instanceof Text) { - Text txt = (Text) child; - if (EclipseUiUtils.isEmpty(text)) { - txt.setText(""); - txt.setMessage(message + " "); - } else - txt.setText(text.replaceAll("
", "\n")); - } - } - - public synchronized void startEditing() { - CmsSwtUtils.style(getControl(), FormStyle.propertyText); - super.startEditing(); - } - - public synchronized void stopEditing() { - if (EclipseUiUtils.isEmpty(((Text) getControl()).getText())) - CmsSwtUtils.style(getControl(), FormStyle.propertyMessage); - else - CmsSwtUtils.style(getControl(), FormStyle.propertyText); - super.stopEditing(); - } - - public String getPropertyName() { - return propertyName; - } -} \ No newline at end of file diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormConstants.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormConstants.java deleted file mode 100644 index fe9f7e7..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormConstants.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.argeo.cms.ui.forms; - -/** Constants used in the various CMS Forms */ -public interface FormConstants { - // DATAKEYS - public final static String LINKED_VALUE = "LinkedValue"; -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormEditorHeader.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormEditorHeader.java deleted file mode 100644 index a75c191..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormEditorHeader.java +++ /dev/null @@ -1,114 +0,0 @@ -package org.argeo.cms.ui.forms; - -import java.util.Observable; -import java.util.Observer; - -import javax.jcr.Node; - -import org.argeo.api.cms.ux.CmsEditable; -import org.argeo.cms.swt.CmsSwtUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; - -/** Add life cycle management abilities to an editable form page */ -public class FormEditorHeader implements SelectionListener, Observer { - private static final long serialVersionUID = 7392898696542484282L; - - // private final Node context; - private final CmsEditable cmsEditable; - private Button publishBtn; - - // Should we provide here the ability to switch from read only to edition - // mode? - // private Button editBtn; - // private boolean readOnly; - - // TODO add information about the current node status, typically if it is - // dirty or not - - private Composite parent; - private Composite display; - private Object layoutData; - - public FormEditorHeader(Composite parent, int style, Node context, - CmsEditable cmsEditable) { - this.cmsEditable = cmsEditable; - this.parent = parent; - // readOnly = SWT.READ_ONLY == (style & SWT.READ_ONLY); - // this.context = context; - if (this.cmsEditable instanceof Observable) - ((Observable) this.cmsEditable).addObserver(this); - refresh(); - } - - public void setLayoutData(Object layoutData) { - this.layoutData = layoutData; - if (display != null && !display.isDisposed()) - display.setLayoutData(layoutData); - } - - protected void refresh() { - if (display != null && !display.isDisposed()) - display.dispose(); - - display = new Composite(parent, SWT.NONE); - display.setLayoutData(layoutData); - - CmsSwtUtils.style(display, FormStyle.header.style()); - display.setBackgroundMode(SWT.INHERIT_FORCE); - - display.setLayout(CmsSwtUtils.noSpaceGridLayout()); - - publishBtn = createSimpleBtn(display, getPublishButtonLabel()); - display.moveAbove(null); - parent.layout(); - } - - private Button createSimpleBtn(Composite parent, String label) { - Button button = new Button(parent, SWT.FLAT | SWT.PUSH); - button.setText(label); - CmsSwtUtils.style(button, FormStyle.header.style()); - button.addSelectionListener(this); - return button; - } - - private String getPublishButtonLabel() { - // Rather check if the current node differs from what has been - // previously committed - // For the time being, we always reach here, the underlying CmsEditable - // is always editing. - if (cmsEditable.isEditing()) - return " Publish "; - else - return " Edit "; - } - - @Override - public void widgetSelected(SelectionEvent e) { - if (e.getSource() == publishBtn) { - // For the time being, the underlying CmsEditable - // is always editing when we reach this point - if (cmsEditable.isEditing()) { - // we always leave the node in a check outed state - cmsEditable.stopEditing(); - cmsEditable.startEditing(); - } else { - cmsEditable.startEditing(); - } - } - } - - @Override - public void widgetDefaultSelected(SelectionEvent e) { - } - - @Override - public void update(Observable o, Object arg) { - if (o == cmsEditable) { - refresh(); - } - } -} \ No newline at end of file diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormPageViewer.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormPageViewer.java deleted file mode 100644 index 1888055..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormPageViewer.java +++ /dev/null @@ -1,608 +0,0 @@ -package org.argeo.cms.ui.forms; - -import java.io.IOException; -import java.io.InputStream; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.Value; -import javax.jcr.ValueFormatException; - -import org.argeo.api.cms.CmsLog; -import org.argeo.api.cms.ux.Cms2DSize; -import org.argeo.api.cms.ux.CmsEditable; -import org.argeo.api.cms.ux.CmsImageManager; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.swt.SwtEditablePart; -import org.argeo.cms.ui.viewers.AbstractPageViewer; -import org.argeo.cms.ui.viewers.Section; -import org.argeo.cms.ui.viewers.SectionPart; -import org.argeo.cms.ui.widgets.EditableImage; -import org.argeo.cms.ui.widgets.Img; -import org.argeo.cms.ui.widgets.StyledControl; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.jcr.JcrException; -import org.argeo.jcr.JcrUtils; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.rap.fileupload.FileDetails; -import org.eclipse.rap.fileupload.FileUploadEvent; -import org.eclipse.rap.fileupload.FileUploadHandler; -import org.eclipse.rap.fileupload.FileUploadListener; -import org.eclipse.rap.fileupload.FileUploadReceiver; -import org.eclipse.rap.rwt.service.ServerPushSession; -import org.eclipse.rap.rwt.widgets.FileUpload; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.FocusEvent; -import org.eclipse.swt.events.FocusListener; -import org.eclipse.swt.events.MouseAdapter; -import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.events.MouseListener; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.layout.FormAttachment; -import org.eclipse.swt.layout.FormData; -import org.eclipse.swt.layout.FormLayout; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.layout.RowLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; - -/** Manage life cycle of a form page that is linked to a given node */ -public class FormPageViewer extends AbstractPageViewer { - private final static CmsLog log = CmsLog.getLog(FormPageViewer.class); - private static final long serialVersionUID = 5277789504209413500L; - - private final Section mainSection; - - // TODO manage within the CSS - private Integer labelColWidth = null; - private int rowLayoutHSpacing = 8; - - // Context cached in the viewer - // The reference to translate from text to calendar and reverse - private DateFormat dateFormat = new SimpleDateFormat(FormUtils.DEFAULT_SHORT_DATE_FORMAT); - private CmsImageManager imageManager; - private FileUploadListener fileUploadListener; - - public FormPageViewer(Section mainSection, int style, CmsEditable cmsEditable) throws RepositoryException { - super(mainSection, style, cmsEditable); - this.mainSection = mainSection; - - if (getCmsEditable().canEdit()) { - fileUploadListener = new FUL(); - } - } - - @Override - protected void prepare(SwtEditablePart part, Object caretPosition) { - if (part instanceof Img) { - ((Img) part).setFileUploadListener(fileUploadListener); - } - } - - /** To be overridden.Save the edited part. */ - protected void save(SwtEditablePart part) throws RepositoryException { - Node node = null; - if (part instanceof EditableMultiStringProperty) { - EditableMultiStringProperty ept = (EditableMultiStringProperty) part; - // SWT : View - List values = ept.getValues(); - // JCR : Model - node = ept.getNode(); - String propName = ept.getPropertyName(); - if (values.isEmpty()) { - if (node.hasProperty(propName)) - node.getProperty(propName).remove(); - } else { - node.setProperty(propName, values.toArray(new String[0])); - } - // => Viewer : Controller - } else if (part instanceof EditablePropertyString) { - EditablePropertyString ept = (EditablePropertyString) part; - // SWT : View - String txt = ((Text) ept.getControl()).getText(); - // JCR : Model - node = ept.getNode(); - String propName = ept.getPropertyName(); - if (EclipseUiUtils.isEmpty(txt)) { - if (node.hasProperty(propName)) - node.getProperty(propName).remove(); - } else { - setPropertySilently(node, propName, txt); - // node.setProperty(propName, txt); - } - // node.getSession().save(); - // => Viewer : Controller - } else if (part instanceof EditablePropertyDate) { - EditablePropertyDate ept = (EditablePropertyDate) part; - Calendar cal = FormUtils.parseDate(dateFormat, ((Text) ept.getControl()).getText()); - node = ept.getNode(); - String propName = ept.getPropertyName(); - if (cal == null) { - if (node.hasProperty(propName)) - node.getProperty(propName).remove(); - } else { - node.setProperty(propName, cal); - } - // node.getSession().save(); - // => Viewer : Controller - } - // TODO: make this configurable, sometimes we do not want to save the - // current session at this stage - if (node != null && node.getSession().hasPendingChanges()) { - JcrUtils.updateLastModified(node, true); - node.getSession().save(); - } - } - - @Override - protected void updateContent(SwtEditablePart part) throws RepositoryException { - if (part instanceof EditableMultiStringProperty) { - EditableMultiStringProperty ept = (EditableMultiStringProperty) part; - // SWT : View - Node node = ept.getNode(); - String propName = ept.getPropertyName(); - List valStrings = new ArrayList(); - if (node.hasProperty(propName)) { - Value[] values = node.getProperty(propName).getValues(); - for (Value val : values) - valStrings.add(val.getString()); - } - ept.setValues(valStrings); - } else if (part instanceof EditablePropertyString) { - // || part instanceof EditableLink - EditablePropertyString ept = (EditablePropertyString) part; - // JCR : Model - Node node = ept.getNode(); - String propName = ept.getPropertyName(); - if (node.hasProperty(propName)) { - String value = node.getProperty(propName).getString(); - ept.setText(value); - } else - ept.setText(""); - // => Viewer : Controller - } else if (part instanceof EditablePropertyDate) { - EditablePropertyDate ept = (EditablePropertyDate) part; - // JCR : Model - Node node = ept.getNode(); - String propName = ept.getPropertyName(); - if (node.hasProperty(propName)) - ept.setText(dateFormat.format(node.getProperty(propName).getDate().getTime())); - else - ept.setText(""); - } else if (part instanceof SectionPart) { - SectionPart sectionPart = (SectionPart) part; - Node partNode = sectionPart.getNode(); - // use control AFTER setting style, since it may have been reset - if (part instanceof EditableImage) { - EditableImage editableImage = (EditableImage) part; - imageManager().load(partNode, part.getControl(), editableImage.getPreferredImageSize()); - } - } - } - - // FILE UPLOAD LISTENER - protected class FUL implements FileUploadListener { - - public FUL() { - } - - public void uploadProgress(FileUploadEvent event) { - // TODO Monitor upload progress - } - - public void uploadFailed(FileUploadEvent event) { - throw new IllegalStateException("Upload failed " + event, event.getException()); - } - - public void uploadFinished(FileUploadEvent event) { - for (FileDetails file : event.getFileDetails()) { - if (log.isDebugEnabled()) - log.debug("Received: " + file.getFileName()); - } - mainSection.getDisplay().syncExec(new Runnable() { - @Override - public void run() { - saveEdit(); - } - }); - FileUploadHandler uploadHandler = (FileUploadHandler) event.getSource(); - uploadHandler.dispose(); - } - } - - // FOCUS OUT LISTENER - protected FocusListener createFocusListener() { - return new FocusOutListener(); - } - - private class FocusOutListener implements FocusListener { - private static final long serialVersionUID = -6069205786732354186L; - - @Override - public void focusLost(FocusEvent event) { - saveEdit(); - } - - @Override - public void focusGained(FocusEvent event) { - // does nothing; - } - } - - // MOUSE LISTENER - @Override - protected MouseListener createMouseListener() { - return new ML(); - } - - private class ML extends MouseAdapter { - private static final long serialVersionUID = 8526890859876770905L; - - @Override - public void mouseDoubleClick(MouseEvent e) { - if (e.button == 1) { - Control source = (Control) e.getSource(); - if (getCmsEditable().canEdit()) { - if (getCmsEditable().isEditing() && !(getEdited() instanceof Img)) { - if (source == mainSection) - return; - SwtEditablePart part = findDataParent(source); - upload(part); - } else { - getCmsEditable().startEditing(); - } - } - } - } - - @Override - public void mouseDown(MouseEvent e) { - if (getCmsEditable().isEditing()) { - if (e.button == 1) { - Control source = (Control) e.getSource(); - SwtEditablePart composite = findDataParent(source); - Point point = new Point(e.x, e.y); - if (!(composite instanceof Img)) - edit(composite, source.toDisplay(point)); - } else if (e.button == 3) { - // EditablePart composite = findDataParent((Control) e - // .getSource()); - // if (styledTools != null) - // styledTools.show(composite, new Point(e.x, e.y)); - } - } - } - - protected synchronized void upload(SwtEditablePart part) { - if (part instanceof SectionPart) { - if (part instanceof Img) { - if (getEdited() == part) - return; - edit(part, null); - layout(part.getControl()); - } - } - } - } - - @Override - public Control getControl() { - return mainSection; - } - - protected CmsImageManager imageManager() { - if (imageManager == null) - imageManager = (CmsImageManager) CmsSwtUtils.getCmsView(mainSection).getImageManager(); - return imageManager; - } - - // LOCAL UI HELPERS - protected Section createSectionIfNeeded(Composite body, Node node) throws RepositoryException { - Section section = null; - if (node != null) { - section = new Section(body, SWT.NO_FOCUS, node); - section.setLayoutData(CmsSwtUtils.fillWidth()); - section.setLayout(CmsSwtUtils.noSpaceGridLayout()); - } - return section; - } - - protected void createSimpleLT(Composite bodyRow, Node node, String propName, String label, String msg) - throws RepositoryException { - if (getCmsEditable().canEdit() || node.hasProperty(propName)) { - createPropertyLbl(bodyRow, label); - EditablePropertyString eps = new EditablePropertyString(bodyRow, SWT.WRAP | SWT.LEFT, node, propName, msg); - eps.setMouseListener(getMouseListener()); - eps.setFocusListener(getFocusListener()); - eps.setLayoutData(CmsSwtUtils.fillWidth()); - } - } - - protected void createMultiStringLT(Composite bodyRow, Node node, String propName, String label, String msg) - throws RepositoryException { - boolean canEdit = getCmsEditable().canEdit(); - if (canEdit || node.hasProperty(propName)) { - createPropertyLbl(bodyRow, label); - - List valueStrings = new ArrayList(); - - if (node.hasProperty(propName)) { - Value[] values = node.getProperty(propName).getValues(); - for (Value value : values) - valueStrings.add(value.getString()); - } - - // TODO use a drop down to display possible values to the end user - EditableMultiStringProperty emsp = new EditableMultiStringProperty(bodyRow, SWT.SINGLE | SWT.LEAD, node, - propName, valueStrings, new String[] { "Implement this" }, msg, - canEdit ? getRemoveValueSelListener() : null); - addListeners(emsp); - // emsp.setMouseListener(getMouseListener()); - emsp.setStyle(FormStyle.propertyMessage.style()); - emsp.setLayoutData(CmsSwtUtils.fillWidth()); - } - } - - protected Label createPropertyLbl(Composite parent, String value) { - return createPropertyLbl(parent, value, SWT.NONE); - } - - protected Label createPropertyLbl(Composite parent, String value, int vAlign) { - // boolean isSmall = CmsView.getCmsView(parent).getUxContext().isSmall(); - Label label = new Label(parent, SWT.LEAD | SWT.WRAP); - label.setText(value + " "); - CmsSwtUtils.style(label, FormStyle.propertyLabel.style()); - GridData gd = new GridData(SWT.LEAD, vAlign, false, false); - if (labelColWidth != null) - gd.widthHint = labelColWidth; - label.setLayoutData(gd); - return label; - } - - protected Label newStyledLabel(Composite parent, String style, String value) { - Label label = new Label(parent, SWT.NONE); - label.setText(value); - CmsSwtUtils.style(label, style); - return label; - } - - protected Composite createRowLayoutComposite(Composite parent) throws RepositoryException { - Composite bodyRow = new Composite(parent, SWT.NO_FOCUS); - bodyRow.setLayoutData(CmsSwtUtils.fillWidth()); - RowLayout rl = new RowLayout(SWT.WRAP); - rl.type = SWT.HORIZONTAL; - rl.spacing = rowLayoutHSpacing; - rl.marginHeight = rl.marginWidth = 0; - rl.marginTop = rl.marginBottom = rl.marginLeft = rl.marginRight = 0; - bodyRow.setLayout(rl); - return bodyRow; - } - - protected Composite createAddImgComposite(final Section section, Composite parent, final Node parentNode) - throws RepositoryException { - - Composite body = new Composite(parent, SWT.NO_FOCUS); - body.setLayout(new GridLayout()); - - FormFileUploadReceiver receiver = new FormFileUploadReceiver(section, parentNode, null); - final FileUploadHandler currentUploadHandler = new FileUploadHandler(receiver); - if (fileUploadListener != null) - currentUploadHandler.addUploadListener(fileUploadListener); - - // Button creation - final FileUpload fileUpload = new FileUpload(body, SWT.BORDER); - fileUpload.setText("Import an image"); - fileUpload.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true)); - fileUpload.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = 4869523412991968759L; - - @Override - public void widgetSelected(SelectionEvent e) { - ServerPushSession pushSession = new ServerPushSession(); - pushSession.start(); - String uploadURL = currentUploadHandler.getUploadUrl(); - fileUpload.submit(uploadURL); - } - }); - - return body; - } - - protected class FormFileUploadReceiver extends FileUploadReceiver { - - private Node context; - private Section section; - private String name; - - public FormFileUploadReceiver(Section section, Node context, String name) { - this.context = context; - this.section = section; - this.name = name; - } - - @Override - public void receive(InputStream stream, FileDetails details) throws IOException { - - if (name == null) - name = details.getFileName(); - - // TODO clean image name more carefully - String cleanedName = name.replaceAll("[^a-zA-Z0-9-.]", ""); - // We add a unique prefix to workaround the cache issue: when - // deleting and re-adding a new image with same name, the end user - // browser will use the cache and the image will remain unchanged - // for a while - cleanedName = System.currentTimeMillis() % 100000 + "_" + cleanedName; - - imageManager().uploadImage(context, context, cleanedName, stream, details.getContentType()); - // TODO clean refresh strategy - section.getDisplay().asyncExec(new Runnable() { - @Override - public void run() { - try { - FormPageViewer.this.refresh(section); - section.layout(); - section.getParent().layout(); - } catch (RepositoryException re) { - throw new JcrException("Unable to refresh " + "image section for " + context, re); - } - } - }); - } - } - - protected void addListeners(StyledControl control) { - control.setMouseListener(getMouseListener()); - control.setFocusListener(getFocusListener()); - } - - protected Img createImgComposite(Composite parent, Node node, Point preferredSize) throws RepositoryException { - Img img = new Img(parent, SWT.NONE, node, new Cms2DSize(preferredSize.x, preferredSize.y)) { - private static final long serialVersionUID = 1297900641952417540L; - - @Override - protected void setContainerLayoutData(Composite composite) { - composite.setLayoutData(CmsSwtUtils.grabWidth(SWT.CENTER, SWT.DEFAULT)); - } - - @Override - protected void setControlLayoutData(Control control) { - control.setLayoutData(CmsSwtUtils.grabWidth(SWT.CENTER, SWT.DEFAULT)); - } - }; - img.setLayoutData(CmsSwtUtils.grabWidth(SWT.CENTER, SWT.DEFAULT)); - updateContent(img); - addListeners(img); - return img; - } - - protected Composite addDeleteAbility(final Section section, final Node sessionNode, int topWeight, - int rightWeight) { - Composite comp = new Composite(section, SWT.NONE); - comp.setLayoutData(CmsSwtUtils.fillAll()); - comp.setLayout(new FormLayout()); - - // The body to be populated - Composite body = new Composite(comp, SWT.NO_FOCUS); - body.setLayoutData(EclipseUiUtils.fillFormData()); - - if (getCmsEditable().canEdit()) { - // the delete button - Button deleteBtn = new Button(comp, SWT.FLAT); - CmsSwtUtils.style(deleteBtn, FormStyle.deleteOverlay.style()); - FormData formData = new FormData(); - formData.right = new FormAttachment(rightWeight, 0); - formData.top = new FormAttachment(topWeight, 0); - deleteBtn.setLayoutData(formData); - deleteBtn.moveAbove(body); - - deleteBtn.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = 4304223543657238462L; - - @Override - public void widgetSelected(SelectionEvent e) { - super.widgetSelected(e); - if (MessageDialog.openConfirm(section.getShell(), "Confirm deletion", - "Are you really you want to remove this?")) { - Session session; - try { - session = sessionNode.getSession(); - Section parSection = section.getParentSection(); - sessionNode.remove(); - session.save(); - refresh(parSection); - layout(parSection); - } catch (RepositoryException re) { - throw new JcrException("Unable to delete " + sessionNode, re); - } - - } - - } - }); - } - return body; - } - -// // LOCAL HELPERS FOR NODE MANAGEMENT -// private Node getOrCreateNode(Node parent, String nodeName, String nodeType) throws RepositoryException { -// Node node = null; -// if (getCmsEditable().canEdit() && !parent.hasNode(nodeName)) { -// node = JcrUtils.mkdirs(parent, nodeName, nodeType); -// parent.getSession().save(); -// } -// -// if (getCmsEditable().canEdit() || parent.hasNode(nodeName)) -// node = parent.getNode(nodeName); -// -// return node; -// } - - private SelectionListener getRemoveValueSelListener() { - return new SelectionAdapter() { - private static final long serialVersionUID = 9022259089907445195L; - - @Override - public void widgetSelected(SelectionEvent e) { - Object source = e.getSource(); - if (source instanceof Button) { - Button btn = (Button) source; - Object obj = btn.getData(FormConstants.LINKED_VALUE); - SwtEditablePart ep = findDataParent(btn); - if (ep != null && ep instanceof EditableMultiStringProperty) { - EditableMultiStringProperty emsp = (EditableMultiStringProperty) ep; - List values = emsp.getValues(); - if (values.contains(obj)) { - values.remove(values.indexOf(obj)); - emsp.setValues(values); - try { - save(emsp); - // TODO workaround to force refresh - edit(emsp, 0); - cancelEdit(); - } catch (RepositoryException e1) { - throw new JcrException("Unable to remove value " + obj, e1); - } - layout(emsp); - } - } - } - } - }; - } - - protected void setPropertySilently(Node node, String propName, String value) throws RepositoryException { - try { - // TODO Clean this: - // Format strings to replace \n - value = value.replaceAll("\n", "
"); - // Do not make the update if validation fails - try { - MarkupValidatorCopy.getInstance().validate(value); - } catch (Exception e) { - log.warn("Cannot set [" + value + "] on prop " + propName + "of " + node - + ", String cannot be validated - " + e.getMessage()); - return; - } - // TODO check if the newly created property is of the correct type, - // otherwise the property will be silently created with a STRING - // property type. - node.setProperty(propName, value); - } catch (ValueFormatException vfe) { - log.warn("Cannot set [" + value + "] on prop " + propName + "of " + node + " - " + vfe.getMessage()); - } - } -} \ No newline at end of file diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormStyle.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormStyle.java deleted file mode 100644 index 709ecd0..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormStyle.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.argeo.cms.ui.forms; - -import org.argeo.api.cms.ux.CmsStyle; - -/** Syles used */ -public enum FormStyle implements CmsStyle { - // Main - form, title, - // main part - header, headerBtn, headerCombo, section, sectionHeader, - // Property fields - propertyLabel, propertyText, propertyMessage, errorMessage, - // Date - popupCalendar, - // Buttons - starred, unstarred, starOverlay, editOverlay, deleteOverlay, updateOverlay, deleteOverlaySmall, calendar, delete, - // Contacts - email, address, phone, website, - // Social Media - facebook, twitter, linkedIn, instagram; - - @Override - public String getClassPrefix() { - return "argeo-form"; - } - - // TODO clean button style management - public final static String BUTTON_SUFFIX = "_btn"; -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormUtils.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormUtils.java deleted file mode 100644 index eeafabb..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormUtils.java +++ /dev/null @@ -1,196 +0,0 @@ -package org.argeo.cms.ui.forms; - -import java.text.DateFormat; -import java.text.ParseException; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.api.cms.CmsLog; -import org.argeo.api.cms.ux.CmsView; -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.ui.util.CmsUiUtils; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.eclipse.jface.fieldassist.ControlDecoration; -import org.eclipse.jface.fieldassist.FieldDecorationRegistry; -import org.eclipse.jface.viewers.DoubleClickEvent; -import org.eclipse.jface.viewers.IDoubleClickListener; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Color; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; - -/** Utilitary methods to ease implementation of CMS forms */ -public class FormUtils { - private final static CmsLog log = CmsLog.getLog(FormUtils.class); - - public final static String DEFAULT_SHORT_DATE_FORMAT = "dd/MM/yyyy"; - - /** Best effort to convert a String to a calendar. Fails silently */ - public static Calendar parseDate(DateFormat dateFormat, String calStr) { - Calendar cal = null; - if (EclipseUiUtils.notEmpty(calStr)) { - try { - Date date = dateFormat.parse(calStr); - cal = new GregorianCalendar(); - cal.setTime(date); - } catch (ParseException pe) { - // Silent - log.warn("Unable to parse date: " + calStr + " - msg: " - + pe.getMessage()); - } - } - return cal; - } - - /** Add a double click listener on tables that display a JCR node list */ - public static void addCanonicalDoubleClickListener(final TableViewer v) { - v.addDoubleClickListener(new IDoubleClickListener() { - - @Override - public void doubleClick(DoubleClickEvent event) { - CmsView cmsView = CmsUiUtils.getCmsView(); - Node node = (Node) ((IStructuredSelection) event.getSelection()) - .getFirstElement(); - try { - cmsView.navigateTo(node.getPath()); - } catch (RepositoryException e) { - throw new CmsException("Unable to get path for node " - + node + " before calling navigateTo(path)", e); - } - } - }); - } - - // MANAGE ERROR DECORATION - - public static ControlDecoration addDecoration(final Text text) { - final ControlDecoration dynDecoration = new ControlDecoration(text, - SWT.LEFT); - Image icon = getDecorationImage(FieldDecorationRegistry.DEC_ERROR); - dynDecoration.setImage(icon); - dynDecoration.setMarginWidth(3); - dynDecoration.hide(); - return dynDecoration; - } - - public static void refreshDecoration(Text text, ControlDecoration deco, - boolean isValid, boolean clean) { - if (isValid || clean) { - text.setBackground(null); - deco.hide(); - } else { - text.setBackground(new Color(text.getDisplay(), 250, 200, 150)); - deco.show(); - } - } - - public static Image getDecorationImage(String image) { - FieldDecorationRegistry registry = FieldDecorationRegistry.getDefault(); - return registry.getFieldDecoration(image).getImage(); - } - - public static void addCompulsoryDecoration(Label label) { - final ControlDecoration dynDecoration = new ControlDecoration(label, - SWT.RIGHT | SWT.TOP); - Image icon = getDecorationImage(FieldDecorationRegistry.DEC_REQUIRED); - dynDecoration.setImage(icon); - dynDecoration.setMarginWidth(3); - } - - // TODO the read only generation of read only links for various contact type - // should be factorised in the cms Utils. - /** - * Creates the read-only HTML snippet to display in a label with styling - * enabled in order to provide a click-able phone number - */ - public static String getPhoneLink(String value) { - return getPhoneLink(value, value); - } - - /** - * Creates the read-only HTML snippet to display in a label with styling - * enabled in order to provide a click-able phone number - * - * @param value - * @param label - * a potentially distinct label - * @return the link - */ - public static String getPhoneLink(String value, String label) { - StringBuilder builder = new StringBuilder(); - builder.append("").append(label) - .append(""); - return builder.toString(); - } - - /** - * Creates the read-only HTML snippet to display in a label with styling - * enabled in order to provide a click-able mail - */ - public static String getMailLink(String value) { - return getMailLink(value, value); - } - - /** - * Creates the read-only HTML snippet to display in a label with styling - * enabled in order to provide a click-able mail - * - * @param value - * @param label - * a potentially distinct label - * @return the link - */ - public static String getMailLink(String value, String label) { - StringBuilder builder = new StringBuilder(); - value = replaceAmpersand(value); - builder.append("").append(label).append(""); - return builder.toString(); - } - - /** - * Creates the read-only HTML snippet to display in a label with styling - * enabled in order to provide a click-able link - */ - public static String getUrlLink(String value) { - return getUrlLink(value, value); - } - - /** - * Creates the read-only HTML snippet to display in a label with styling - * enabled in order to provide a click-able link - */ - public static String getUrlLink(String value, String label) { - StringBuilder builder = new StringBuilder(); - value = replaceAmpersand(value); - label = replaceAmpersand(label); - if (!(value.startsWith("http://") || value.startsWith("https://"))) - value = "http://" + value; - builder.append("" + label + ""); - return builder.toString(); - } - - private static String AMPERSAND = "&"; - - /** - * Cleans a String by replacing any '&' by its HTML encoding '&#38;' to - * avoid SAXParseException while rendering HTML with RWT - */ - public static String replaceAmpersand(String value) { - value = value.replaceAll("&(?![#a-zA-Z0-9]+;)", AMPERSAND); - return value; - } - - // Prevents instantiation - private FormUtils() { - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/MarkupValidatorCopy.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/MarkupValidatorCopy.java deleted file mode 100644 index 3f588d1..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/MarkupValidatorCopy.java +++ /dev/null @@ -1,169 +0,0 @@ -package org.argeo.cms.ui.forms; - -import java.io.StringReader; -import java.text.MessageFormat; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -import org.eclipse.rap.rwt.SingletonUtil; -import org.eclipse.swt.widgets.Widget; -import org.xml.sax.Attributes; -import org.xml.sax.InputSource; -import org.xml.sax.helpers.DefaultHandler; - -/** - * Copy of RAP v2.3 since it is in an internal package. - */ -class MarkupValidatorCopy { - - // Used by Eclipse Scout project - public static final String MARKUP_VALIDATION_DISABLED = "org.eclipse.rap.rwt.markupValidationDisabled"; - - private static final String DTD = createDTD(); - private static final Map SUPPORTED_ELEMENTS = createSupportedElementsMap(); - private final SAXParser saxParser; - - public static MarkupValidatorCopy getInstance() { - return SingletonUtil.getSessionInstance(MarkupValidatorCopy.class); - } - - public MarkupValidatorCopy() { - saxParser = createSAXParser(); - } - - public void validate(String text) { - StringBuilder markup = new StringBuilder(); - markup.append(DTD); - markup.append(""); - markup.append(text); - markup.append(""); - InputSource inputSource = new InputSource(new StringReader(markup.toString())); - try { - saxParser.parse(inputSource, new MarkupHandler()); - } catch (RuntimeException exception) { - throw exception; - } catch (Exception exception) { - throw new IllegalArgumentException("Failed to parse markup text", exception); - } - } - - public static boolean isValidationDisabledFor(Widget widget) { - return Boolean.TRUE.equals(widget.getData(MARKUP_VALIDATION_DISABLED)); - } - - private static SAXParser createSAXParser() { - SAXParser result = null; - SAXParserFactory parserFactory = SAXParserFactory.newInstance(); - try { - result = parserFactory.newSAXParser(); - } catch (Exception exception) { - throw new RuntimeException("Failed to create SAX parser", exception); - } - return result; - } - - private static String createDTD() { - StringBuilder result = new StringBuilder(); - result.append(""); - result.append(""); - result.append(""); - result.append(""); - result.append(""); - result.append(""); - result.append(""); - result.append(""); - result.append(""); - result.append(""); - result.append("]>"); - return result.toString(); - } - - private static Map createSupportedElementsMap() { - Map result = new HashMap(); - result.put("html", new String[0]); - result.put("br", new String[0]); - result.put("b", new String[] { "style" }); - result.put("strong", new String[] { "style" }); - result.put("i", new String[] { "style" }); - result.put("em", new String[] { "style" }); - result.put("sub", new String[] { "style" }); - result.put("sup", new String[] { "style" }); - result.put("big", new String[] { "style" }); - result.put("small", new String[] { "style" }); - result.put("del", new String[] { "style" }); - result.put("ins", new String[] { "style" }); - result.put("code", new String[] { "style" }); - result.put("samp", new String[] { "style" }); - result.put("kbd", new String[] { "style" }); - result.put("var", new String[] { "style" }); - result.put("cite", new String[] { "style" }); - result.put("dfn", new String[] { "style" }); - result.put("q", new String[] { "style" }); - result.put("abbr", new String[] { "style", "title" }); - result.put("span", new String[] { "style" }); - result.put("img", new String[] { "style", "src", "width", "height", "title", "alt" }); - result.put("a", new String[] { "style", "href", "target", "title" }); - return result; - } - - private static class MarkupHandler extends DefaultHandler { - - @Override - public void startElement(String uri, String localName, String name, Attributes attributes) { - checkSupportedElements(name, attributes); - checkSupportedAttributes(name, attributes); - checkMandatoryAttributes(name, attributes); - } - - private static void checkSupportedElements(String elementName, Attributes attributes) { - if (!SUPPORTED_ELEMENTS.containsKey(elementName)) { - throw new IllegalArgumentException("Unsupported element in markup text: " + elementName); - } - } - - private static void checkSupportedAttributes(String elementName, Attributes attributes) { - if (attributes.getLength() > 0) { - List supportedAttributes = Arrays.asList(SUPPORTED_ELEMENTS.get(elementName)); - int index = 0; - String attributeName = attributes.getQName(index); - while (attributeName != null) { - if (!supportedAttributes.contains(attributeName)) { - String message = "Unsupported attribute \"{0}\" for element \"{1}\" in markup text"; - message = MessageFormat.format(message, new Object[] { attributeName, elementName }); - throw new IllegalArgumentException(message); - } - index++; - attributeName = attributes.getQName(index); - } - } - } - - private static void checkMandatoryAttributes(String elementName, Attributes attributes) { - checkIntAttribute(elementName, attributes, "img", "width"); - checkIntAttribute(elementName, attributes, "img", "height"); - } - - private static void checkIntAttribute(String elementName, Attributes attributes, String checkedElementName, - String checkedAttributeName) { - if (checkedElementName.equals(elementName)) { - String attribute = attributes.getValue(checkedAttributeName); - try { - Integer.parseInt(attribute); - } catch (NumberFormatException exception) { - String message = "Mandatory attribute \"{0}\" for element \"{1}\" is missing or not a valid integer"; - Object[] arguments = new Object[] { checkedAttributeName, checkedElementName }; - message = MessageFormat.format(message, arguments); - throw new IllegalArgumentException(message); - } - } - } - - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/package-info.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/package-info.java deleted file mode 100644 index 5f954c1..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Argeo CMS forms, based on SWT/JFace. */ -package org.argeo.cms.ui.forms; \ No newline at end of file diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/CmsFsBrowser.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/CmsFsBrowser.java deleted file mode 100644 index d9c1c12..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/CmsFsBrowser.java +++ /dev/null @@ -1,524 +0,0 @@ -package org.argeo.cms.ui.fs; - -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.file.DirectoryStream; -import java.nio.file.FileSystem; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.spi.FileSystemProvider; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; -import java.util.ArrayList; -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.Repository; -import javax.jcr.Session; - -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.jcr.CmsJcrUtils; -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.eclipse.ui.ColumnDefinition; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.fs.FileIconNameLabelProvider; -import org.argeo.eclipse.ui.fs.FsTableViewer; -import org.argeo.eclipse.ui.fs.FsUiConstants; -import org.argeo.eclipse.ui.fs.FsUiUtils; -import org.argeo.eclipse.ui.fs.NioFileLabelProvider; -import org.argeo.jcr.JcrUtils; -import org.eclipse.jface.viewers.DoubleClickEvent; -import org.eclipse.jface.viewers.IDoubleClickListener; -import org.eclipse.jface.viewers.ISelectionChangedListener; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.SelectionChangedEvent; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.SashForm; -import org.eclipse.swt.events.KeyEvent; -import org.eclipse.swt.events.KeyListener; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.events.MouseAdapter; -import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.layout.RowData; -import org.eclipse.swt.layout.RowLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Table; -import org.eclipse.swt.widgets.Text; - -/** - * Default CMS browser composite: a sashForm layout with bookmarks at the left - * hand side, a simple table in the middle and an overview at right hand side. - */ -public class CmsFsBrowser extends Composite { - // private final static Log log = LogFactory.getLog(CmsFsBrowser.class); - private static final long serialVersionUID = -40347919096946585L; - - private final FileSystemProvider nodeFileSystemProvider; - private final Node currentBaseContext; - - // UI Parts for the browser - private Composite leftPannelCmp; - private Composite filterCmp; - private Text filterTxt; - private FsTableViewer directoryDisplayViewer; - private Composite rightPannelCmp; - - private FsContextMenu contextMenu; - - // Local context (this composite is state full) - private Path initialPath; - private Path currDisplayedFolder; - private Path currSelected; - - // local variables (to be cleaned) - private int bookmarkColWith = 500; - - /* - * WARNING: unfinalised implementation of the mechanism to retrieve base - * paths - */ - - private final static String NODE_PREFIX = "node://"; - - private String getCurrentHomePath() { - Session session = null; - try { - Repository repo = currentBaseContext.getSession().getRepository(); - session = CurrentUser.tryAs(() -> repo.login()); - String homepath = CmsJcrUtils.getUserHome(session).getPath(); - return homepath; - } catch (Exception e) { - throw new CmsException("Cannot retrieve Current User Home Path", e); - } finally { - JcrUtils.logoutQuietly(session); - } - } - - protected Path[] getMyFilesPath() { - // return Paths.get(System.getProperty("user.dir")); - String currHomeUriStr = NODE_PREFIX + getCurrentHomePath(); - try { - URI uri = new URI(currHomeUriStr); - FileSystem fileSystem = nodeFileSystemProvider.getFileSystem(uri); - if (fileSystem == null) { - PrivilegedExceptionAction pea = new PrivilegedExceptionAction() { - @Override - public FileSystem run() throws Exception { - return nodeFileSystemProvider.newFileSystem(uri, null); - } - - }; - fileSystem = CurrentUser.tryAs(pea); - } - Path[] paths = { fileSystem.getPath(getCurrentHomePath()), fileSystem.getPath("/") }; - return paths; - } catch (URISyntaxException | PrivilegedActionException e) { - throw new RuntimeException("unable to initialise home file system for " + currHomeUriStr, e); - } - } - - private Path[] getMyGroupsFilesPath() { - // TODO - Path[] paths = { Paths.get(System.getProperty("user.dir")), Paths.get("/tmp") }; - return paths; - } - - private Path[] getMyBookmarks() { - // TODO - Path[] paths = { Paths.get(System.getProperty("user.dir")), Paths.get("/tmp"), Paths.get("/opt") }; - return paths; - } - - /* End of warning */ - - public CmsFsBrowser(Composite parent, int style, Node context, FileSystemProvider fileSystemProvider) { - super(parent, style); - this.nodeFileSystemProvider = fileSystemProvider; - this.currentBaseContext = context; - - this.setLayout(EclipseUiUtils.noSpaceGridLayout()); - - SashForm form = new SashForm(this, SWT.HORIZONTAL); - - leftPannelCmp = new Composite(form, SWT.NO_FOCUS); - // Bookmarks are still static - populateBookmarks(leftPannelCmp); - - Composite centerCmp = new Composite(form, SWT.BORDER | SWT.NO_FOCUS); - createDisplay(centerCmp); - - rightPannelCmp = new Composite(form, SWT.NO_FOCUS); - - form.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - form.setWeights(new int[] { 15, 40, 20 }); - } - - void refresh() { - modifyFilter(false); - // also refresh bookmarks and groups - } - - private void createDisplay(final Composite parent) { - parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); - - // top filter - filterCmp = new Composite(parent, SWT.NO_FOCUS); - filterCmp.setLayoutData(EclipseUiUtils.fillWidth()); - addFilterPanel(filterCmp); - - // Main display - directoryDisplayViewer = new FsTableViewer(parent, SWT.MULTI); - List colDefs = new ArrayList<>(); - colDefs.add(new ColumnDefinition(new FileIconNameLabelProvider(), "Name", 250)); - colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_SIZE), "Size", 100)); - colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_TYPE), "Type", 150)); - colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_LAST_MODIFIED), - "Last modified", 400)); - final Table table = directoryDisplayViewer.configureDefaultTable(colDefs); - table.setLayoutData(EclipseUiUtils.fillAll()); - - // table.addKeyListener(new KeyListener() { - // private static final long serialVersionUID = -8083424284436715709L; - // - // @Override - // public void keyReleased(KeyEvent e) { - // } - // - // @Override - // public void keyPressed(KeyEvent e) { - // if (log.isDebugEnabled()) - // log.debug("Key event received: " + e.keyCode); - // IStructuredSelection selection = (IStructuredSelection) - // directoryDisplayViewer.getSelection(); - // Path selected = null; - // if (!selection.isEmpty()) - // selected = ((Path) selection.getFirstElement()); - // if (e.keyCode == SWT.CR) { - // if (!Files.isDirectory(selected)) - // return; - // if (selected != null) { - // currDisplayedFolder = selected; - // directoryDisplayViewer.setInput(currDisplayedFolder, "*"); - // } - // } else if (e.keyCode == SWT.BS) { - // currDisplayedFolder = currDisplayedFolder.getParent(); - // directoryDisplayViewer.setInput(currDisplayedFolder, "*"); - // directoryDisplayViewer.getTable().setFocus(); - // } - // } - // }); - - directoryDisplayViewer.addSelectionChangedListener(new ISelectionChangedListener() { - - @Override - public void selectionChanged(SelectionChangedEvent event) { - IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection(); - Path selected = null; - if (selection.isEmpty()) - setSelected(null); - else - selected = ((Path) selection.getFirstElement()); - if (selected != null) { - // TODO manage multiple selection - setSelected(selected); - } - } - }); - - directoryDisplayViewer.addDoubleClickListener(new IDoubleClickListener() { - @Override - public void doubleClick(DoubleClickEvent event) { - IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection(); - Path selected = null; - if (!selection.isEmpty()) - selected = ((Path) selection.getFirstElement()); - if (selected != null) { - if (!Files.isDirectory(selected)) - return; - setInput(selected); - } - } - }); - - // The context menu - contextMenu = new FsContextMenu(this); - - table.addMouseListener(new MouseAdapter() { - private static final long serialVersionUID = 6737579410648595940L; - - @Override - public void mouseDown(MouseEvent e) { - if (e.button == 3) { - // contextMenu.setCurrFolderPath(currDisplayedFolder); - contextMenu.show(table, new Point(e.x, e.y), currDisplayedFolder); - } - } - }); - } - - private void addPathElementBtn(Path path) { - Button elemBtn = new Button(filterCmp, SWT.PUSH); - String nameStr; - if (path.toString().equals("/")) - nameStr = "[jcr:root]"; - else - nameStr = path.getFileName().toString(); - elemBtn.setText(nameStr + " >> "); - CmsSwtUtils.style(elemBtn, FsStyles.BREAD_CRUMB_BTN); - elemBtn.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = -4103695476023480651L; - - @Override - public void widgetSelected(SelectionEvent e) { - setInput(path); - } - }); - } - - public void setInput(Path path) { - if (path.equals(currDisplayedFolder)) - return; - currDisplayedFolder = path; - - Path diff = initialPath.relativize(currDisplayedFolder); - - for (Control child : filterCmp.getChildren()) - if (!child.equals(filterTxt)) - child.dispose(); - - addPathElementBtn(initialPath); - Path currTarget = initialPath; - if (!diff.toString().equals("")) - for (Path pathElem : diff) { - currTarget = currTarget.resolve(pathElem); - addPathElementBtn(currTarget); - } - - filterTxt.setText(""); - filterTxt.moveBelow(null); - setSelected(null); - filterCmp.getParent().layout(true, true); - } - - private void setSelected(Path path) { - currSelected = path; - setOverviewInput(path); - } - - public Viewer getViewer() { - return directoryDisplayViewer; - } - - private void populateBookmarks(Composite parent) { - CmsSwtUtils.clear(parent); - parent.setLayout(new GridLayout()); - ISelectionChangedListener selList = new BookmarksSelChangeListener(); - - FsTableViewer homeViewer = new FsTableViewer(parent, SWT.SINGLE | SWT.NO_SCROLL); - Table table = homeViewer.configureDefaultSingleColumnTable(bookmarkColWith); - GridData gd = EclipseUiUtils.fillWidth(); - gd.horizontalIndent = 10; - table.setLayoutData(gd); - homeViewer.addSelectionChangedListener(selList); - homeViewer.setPathsInput(getMyFilesPath()); - - appendTitle(parent, "Shared files"); - FsTableViewer groupsViewer = new FsTableViewer(parent, SWT.SINGLE | SWT.NO_SCROLL); - table = groupsViewer.configureDefaultSingleColumnTable(bookmarkColWith); - gd = EclipseUiUtils.fillWidth(); - gd.horizontalIndent = 10; - table.setLayoutData(gd); - groupsViewer.addSelectionChangedListener(selList); - groupsViewer.setPathsInput(getMyGroupsFilesPath()); - - appendTitle(parent, "My bookmarks"); - FsTableViewer bookmarksViewer = new FsTableViewer(parent, SWT.SINGLE | SWT.NO_SCROLL); - table = bookmarksViewer.configureDefaultSingleColumnTable(bookmarkColWith); - gd = EclipseUiUtils.fillWidth(); - gd.horizontalIndent = 10; - table.setLayoutData(gd); - bookmarksViewer.addSelectionChangedListener(selList); - bookmarksViewer.setPathsInput(getMyBookmarks()); - } - - /** - * Recreates the content of the box that displays information about the - * current selected Path. - */ - private void setOverviewInput(Path path) { - try { - EclipseUiUtils.clear(rightPannelCmp); - rightPannelCmp.setLayout(new GridLayout()); - if (path != null) { - // if (isImg(context)) { - // EditableImage image = new Img(parent, RIGHT, context, - // imageWidth); - // image.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, - // true, false, - // 2, 1)); - // } - - Label contextL = new Label(rightPannelCmp, SWT.NONE); - contextL.setText(path.getFileName().toString()); - contextL.setFont(EclipseUiUtils.getBoldFont(rightPannelCmp)); - addProperty(rightPannelCmp, "Last modified", Files.getLastModifiedTime(path).toString()); - // addProperty(rightPannelCmp, "Owner", - // Files.getOwner(path).getName()); - if (Files.isDirectory(path)) { - addProperty(rightPannelCmp, "Type", "Folder"); - } else { - String mimeType = Files.probeContentType(path); - if (EclipseUiUtils.isEmpty(mimeType)) - mimeType = "Unknown"; - addProperty(rightPannelCmp, "Type", mimeType); - addProperty(rightPannelCmp, "Size", FsUiUtils.humanReadableByteCount(Files.size(path), false)); - } - } - rightPannelCmp.layout(true, true); - } catch (IOException e) { - throw new CmsException("Cannot display details for " + path.toString(), e); - } - } - - private void addFilterPanel(Composite parent) { - RowLayout rl = new RowLayout(SWT.HORIZONTAL); - rl.wrap = true; - parent.setLayout(rl); - // parent.setLayout(EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, - // false))); - - filterTxt = new Text(parent, SWT.SEARCH | SWT.ICON_CANCEL); - filterTxt.setMessage("Search current folder"); - filterTxt.setLayoutData(new RowData(250, SWT.DEFAULT)); - filterTxt.addModifyListener(new ModifyListener() { - private static final long serialVersionUID = 1L; - - public void modifyText(ModifyEvent event) { - modifyFilter(false); - } - }); - filterTxt.addKeyListener(new KeyListener() { - private static final long serialVersionUID = 2533535233583035527L; - - @Override - public void keyReleased(KeyEvent e) { - } - - @Override - public void keyPressed(KeyEvent e) { - // boolean shiftPressed = (e.stateMask & SWT.SHIFT) != 0; - // // boolean altPressed = (e.stateMask & SWT.ALT) != 0; - // FilterEntitiesVirtualTable currTable = null; - // if (currEdited != null) { - // FilterEntitiesVirtualTable table = - // browserCols.get(currEdited); - // if (table != null && !table.isDisposed()) - // currTable = table; - // } - // - // if (e.keyCode == SWT.ARROW_DOWN) - // currTable.setFocus(); - // else if (e.keyCode == SWT.BS) { - // if (filterTxt.getText().equals("") - // && !(currEdited.getNameCount() == 1 || - // currEdited.equals(initialPath))) { - // Path oldEdited = currEdited; - // Path parentPath = currEdited.getParent(); - // setEdited(parentPath); - // if (browserCols.containsKey(parentPath)) - // browserCols.get(parentPath).setSelected(oldEdited); - // filterTxt.setFocus(); - // e.doit = false; - // } - // } else if (e.keyCode == SWT.TAB && !shiftPressed) { - // Path uniqueChild = getOnlyChild(currEdited, - // filterTxt.getText()); - // if (uniqueChild != null) { - // // Highlight the unique chosen child - // currTable.setSelected(uniqueChild); - // setEdited(uniqueChild); - // } - // filterTxt.setFocus(); - // e.doit = false; - // } - } - }); - } - - private Path getOnlyChild(Path parent, String filter) { - try (DirectoryStream stream = Files.newDirectoryStream(currDisplayedFolder, filter + "*")) { - Path uniqueChild = null; - boolean moreThanOne = false; - loop: for (Path entry : stream) { - if (uniqueChild == null) { - uniqueChild = entry; - } else { - moreThanOne = true; - break loop; - } - } - if (!moreThanOne) - return uniqueChild; - return null; - } catch (IOException ioe) { - throw new CmsException( - "Unable to determine unique child existence and get it under " + parent + " with filter " + filter, - ioe); - } - } - - private void modifyFilter(boolean fromOutside) { - if (!fromOutside) - if (currDisplayedFolder != null) { - String filter = filterTxt.getText() + "*"; - directoryDisplayViewer.setInput(currDisplayedFolder, filter); - } - } - - private class BookmarksSelChangeListener implements ISelectionChangedListener { - - @Override - public void selectionChanged(SelectionChangedEvent event) { - IStructuredSelection selection = (IStructuredSelection) event.getSelection(); - if (selection.isEmpty()) - return; - else { - Path newSelected = (Path) selection.getFirstElement(); - if (newSelected.equals(currDisplayedFolder) && newSelected.equals(initialPath)) - return; - initialPath = newSelected; - setInput(newSelected); - } - } - } - - // Simplify UI implementation - private void addProperty(Composite parent, String propName, String value) { - Label contextL = new Label(parent, SWT.NONE); - contextL.setText(propName + ": " + value); - } - - private Label appendTitle(Composite parent, String value) { - Label titleLbl = new Label(parent, SWT.NONE); - titleLbl.setText(value); - titleLbl.setFont(EclipseUiUtils.getBoldFont(parent)); - GridData gd = EclipseUiUtils.fillWidth(); - gd.horizontalIndent = 5; - gd.verticalIndent = 5; - titleLbl.setLayoutData(gd); - return titleLbl; - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/FileDrop.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/FileDrop.java deleted file mode 100644 index e875b5a..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/FileDrop.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.argeo.cms.ui.fs; - -import java.io.IOException; -import java.io.InputStream; - -import org.argeo.api.cms.CmsLog; -import org.argeo.eclipse.ui.specific.FileDropAdapter; -import org.eclipse.swt.dnd.DND; -import org.eclipse.swt.dnd.DropTarget; -import org.eclipse.swt.dnd.DropTargetEvent; -import org.eclipse.swt.widgets.Control; - -/** Allows a control to receive file drops. */ -public class FileDrop { - private final static CmsLog log = CmsLog.getLog(FileDrop.class); - - public void createDropTarget(Control control) { - FileDropAdapter fileDropAdapter = new FileDropAdapter() { - @Override - protected void processUpload(InputStream in, String fileName, String contentType) throws IOException { - if (log.isDebugEnabled()) - log.debug("Process upload of " + fileName + " (" + contentType + ")"); - processFileUpload(in, fileName, contentType); - } - }; - DropTarget dropTarget = new DropTarget(control, DND.DROP_MOVE | DND.DROP_COPY); - fileDropAdapter.prepareDropTarget(control, dropTarget); - } - - public void handleFileDrop(Control control, DropTargetEvent event) { - } - - /** Executed in UI thread */ - protected void processFileUpload(InputStream in, String fileName, String contentType) throws IOException { - - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/FsContextMenu.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/FsContextMenu.java deleted file mode 100644 index 1fb3c2a..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/FsContextMenu.java +++ /dev/null @@ -1,383 +0,0 @@ -package org.argeo.cms.ui.fs; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.Method; -import java.net.URI; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import org.apache.commons.io.IOUtils; -import org.argeo.api.cms.CmsLog; -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.dialogs.SingleValue; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.ShellEvent; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.FileDialog; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Shell; - -/** Generic popup context menu to manage NIO Path in a Viewer. */ -public class FsContextMenu extends Shell { - private static final long serialVersionUID = -9120261153509855795L; - - private final static CmsLog log = CmsLog.getLog(FsContextMenu.class); - - // Default known actions - public final static String ACTION_ID_CREATE_FOLDER = "createFolder"; - public final static String ACTION_ID_BOOKMARK_FOLDER = "bookmarkFolder"; - public final static String ACTION_ID_SHARE_FOLDER = "shareFolder"; - public final static String ACTION_ID_DOWNLOAD_FOLDER = "downloadFolder"; - public final static String ACTION_ID_DELETE = "delete"; - public final static String ACTION_ID_UPLOAD_FILE = "uploadFiles"; - public final static String ACTION_ID_OPEN = "open"; - - // Local context - private final CmsFsBrowser browser; - // private final Viewer viewer; - private final static String KEY_ACTION_ID = "actionId"; - private final static String[] DEFAULT_ACTIONS = { ACTION_ID_CREATE_FOLDER, ACTION_ID_BOOKMARK_FOLDER, - ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_DELETE, ACTION_ID_UPLOAD_FILE, - ACTION_ID_OPEN }; - private Map actionButtons = new HashMap(); - - private Path currFolderPath; - - public FsContextMenu(CmsFsBrowser browser) { // Viewer viewer, Display - // display) { - super(browser.getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP); - this.browser = browser; - setLayout(EclipseUiUtils.noSpaceGridLayout()); - - Composite boxCmp = new Composite(this, SWT.NO_FOCUS | SWT.BORDER); - boxCmp.setLayout(EclipseUiUtils.noSpaceGridLayout()); - CmsSwtUtils.style(boxCmp, FsStyles.CONTEXT_MENU_BOX); - createContextMenu(boxCmp); - - addShellListener(new ActionsShellListener()); - } - - protected void createContextMenu(Composite boxCmp) { - ActionsSelListener asl = new ActionsSelListener(); - for (String actionId : DEFAULT_ACTIONS) { - Button btn = new Button(boxCmp, SWT.FLAT | SWT.PUSH | SWT.LEAD); - btn.setText(getLabel(actionId)); - btn.setLayoutData(EclipseUiUtils.fillWidth()); - CmsSwtUtils.markup(btn); - CmsSwtUtils.style(btn, actionId + FsStyles.BUTTON_SUFFIX); - btn.setData(KEY_ACTION_ID, actionId); - btn.addSelectionListener(asl); - actionButtons.put(actionId, btn); - } - } - - protected String getLabel(String actionId) { - switch (actionId) { - case ACTION_ID_CREATE_FOLDER: - return "Create Folder"; - case ACTION_ID_BOOKMARK_FOLDER: - return "Bookmark Folder"; - case ACTION_ID_SHARE_FOLDER: - return "Share Folder"; - case ACTION_ID_DOWNLOAD_FOLDER: - return "Download as zip archive"; - case ACTION_ID_DELETE: - return "Delete"; - case ACTION_ID_UPLOAD_FILE: - return "Upload Files"; - case ACTION_ID_OPEN: - return "Open"; - default: - throw new IllegalArgumentException("Unknown action ID " + actionId); - } - } - - protected void aboutToShow(Control source, Point location) { - IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection()); - boolean emptySel = true; - boolean multiSel = false; - boolean isFolder = true; - if (selection != null && !selection.isEmpty()) { - emptySel = false; - multiSel = selection.size() > 1; - if (!multiSel && selection.getFirstElement() instanceof Path) { - isFolder = Files.isDirectory((Path) selection.getFirstElement()); - } - } - if (emptySel) { - setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE); - setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_DELETE, ACTION_ID_OPEN, - // to be implemented - ACTION_ID_BOOKMARK_FOLDER); - } else if (multiSel) { - setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_DELETE); - setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_OPEN, - // to be implemented - ACTION_ID_BOOKMARK_FOLDER); - } else if (isFolder) { - setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_DELETE); - setVisible(false, ACTION_ID_OPEN, - // to be implemented - ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_BOOKMARK_FOLDER); - } else { - setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_OPEN, ACTION_ID_DELETE); - setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, - // to be implemented - ACTION_ID_BOOKMARK_FOLDER); - } - } - - private void setVisible(boolean visible, String... buttonIds) { - for (String id : buttonIds) { - Button button = actionButtons.get(id); - button.setVisible(visible); - GridData gd = (GridData) button.getLayoutData(); - gd.heightHint = visible ? SWT.DEFAULT : 0; - } - } - - public void show(Control source, Point location, Path currFolderPath) { - if (isVisible()) - setVisible(false); - // TODO find a better way to retrieve the parent path (cannot be deduced - // from table content because it will fail on an empty folder) - this.currFolderPath = currFolderPath; - aboutToShow(source, location); - pack(); - layout(); - if (source instanceof Control) - setLocation(((Control) source).toDisplay(location.x, location.y)); - open(); - } - - class StyleButton extends Label { - private static final long serialVersionUID = 7731102609123946115L; - - public StyleButton(Composite parent, int swtStyle) { - super(parent, swtStyle); - } - - } - - // class ActionsMouseListener extends MouseAdapter { - // private static final long serialVersionUID = -1041871937815812149L; - // - // @Override - // public void mouseDown(MouseEvent e) { - // Object eventSource = e.getSource(); - // if (e.button == 1) { - // if (eventSource instanceof Button) { - // Button pressedBtn = (Button) eventSource; - // String actionId = (String) pressedBtn.getData(KEY_ACTION_ID); - // switch (actionId) { - // case ACTION_ID_CREATE_FOLDER: - // createFolder(); - // break; - // case ACTION_ID_DELETE: - // deleteItems(); - // break; - // default: - // throw new IllegalArgumentException("Unimplemented action " + actionId); - // // case ACTION_ID_SHARE_FOLDER: - // // return "Share Folder"; - // // case ACTION_ID_DOWNLOAD_FOLDER: - // // return "Download as zip archive"; - // // case ACTION_ID_UPLOAD_FILE: - // // return "Upload Files"; - // // case ACTION_ID_OPEN: - // // return "Open"; - // } - // } - // } - // viewer.getControl().setFocus(); - // // setVisible(false); - // } - // } - - class ActionsSelListener extends SelectionAdapter { - private static final long serialVersionUID = -1041871937815812149L; - - @Override - public void widgetSelected(SelectionEvent e) { - Object eventSource = e.getSource(); - if (eventSource instanceof Button) { - Button pressedBtn = (Button) eventSource; - String actionId = (String) pressedBtn.getData(KEY_ACTION_ID); - switch (actionId) { - case ACTION_ID_CREATE_FOLDER: - createFolder(); - break; - case ACTION_ID_DELETE: - deleteItems(); - break; - case ACTION_ID_OPEN: - openFile(); - break; - case ACTION_ID_UPLOAD_FILE: - uploadFiles(); - break; - default: - throw new IllegalArgumentException("Unimplemented action " + actionId); - // case ACTION_ID_SHARE_FOLDER: - // return "Share Folder"; - // case ACTION_ID_DOWNLOAD_FOLDER: - // return "Download as zip archive"; - // case ACTION_ID_OPEN: - // return "Open"; - } - } - browser.setFocus(); - // viewer.getControl().setFocus(); - // setVisible(false); - - } - } - - class ActionsShellListener extends org.eclipse.swt.events.ShellAdapter { - private static final long serialVersionUID = -5092341449523150827L; - - @Override - public void shellDeactivated(ShellEvent e) { - setVisible(false); - } - } - - private void openFile() { - log.warn("Implement single sourced, workbench independant \"Open File\" action"); - } - - private void deleteItems() { - IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection()); - if (selection.isEmpty()) - return; - - StringBuilder builder = new StringBuilder(); - @SuppressWarnings("unchecked") - Iterator iterator = selection.iterator(); - List paths = new ArrayList<>(); - - while (iterator.hasNext()) { - Path path = (Path) iterator.next(); - builder.append(path.getFileName() + ", "); - paths.add(path); - } - String msg = "You are about to delete following elements: " + builder.substring(0, builder.length() - 2) - + ". Are you sure?"; - if (MessageDialog.openConfirm(this, "Confirm deletion", msg)) { - for (Path path : paths) { - try { - // Might have already been deleted if we are in a tree - Files.deleteIfExists(path); - } catch (IOException e) { - throw new CmsException("Cannot delete path " + path, e); - } - } - browser.refresh(); - } - } - - private void createFolder() { - String msg = "Please provide a name."; - String name = SingleValue.ask("Create folder", msg); - // TODO enhance check of name validity - if (EclipseUiUtils.notEmpty(name)) { - try { - Path child = currFolderPath.resolve(name); - if (Files.exists(child)) - throw new CmsException("An item with name " + name + " already exists at " - + currFolderPath.toString() + ", cannot create"); - else - Files.createDirectories(child); - browser.refresh(); - } catch (IOException e) { - throw new CmsException("Cannot create folder " + name + " at " + currFolderPath.toString(), e); - } - } - } - - private void uploadFiles() { - try { - FileDialog dialog = new FileDialog(browser.getShell(), SWT.MULTI); - dialog.setText("Choose one or more files to upload"); - - if (EclipseUiUtils.notEmpty(dialog.open())) { - String[] names = dialog.getFileNames(); - // Workaround small differences between RAP and RCP - // 1. returned names are absolute path on RAP and - // relative in RCP - // 2. in RCP we must use getFilterPath that does not - // exists on RAP - Method filterMethod = null; - Path parPath = null; - try { - filterMethod = dialog.getClass().getDeclaredMethod("getFilterPath"); - String filterPath = (String) filterMethod.invoke(dialog); - parPath = Paths.get(filterPath); - } catch (NoSuchMethodException nsme) { // RAP - } - if (names.length == 0) - return; - else { - loop: for (String name : names) { - Path tmpPath = Paths.get(name); - if (parPath != null) - tmpPath = parPath.resolve(tmpPath); - if (Files.exists(tmpPath)) { - URI uri = tmpPath.toUri(); - String uriStr = uri.toString(); - - if (Files.isDirectory(tmpPath)) { - MessageDialog.openError(browser.getShell(), "Unimplemented directory import", - "Upload of directories in the system is not yet implemented"); - continue loop; - } - Path targetPath = currFolderPath.resolve(tmpPath.getFileName().toString()); - InputStream in = null; - try { - in = new ByteArrayInputStream(Files.readAllBytes(tmpPath)); - Files.copy(in, targetPath); - Files.delete(tmpPath); - } finally { - IOUtils.closeQuietly(in); - } - if (log.isDebugEnabled()) - log.debug("copied uploaded file " + uriStr + " to " + targetPath.toString()); - } else { - String msg = "Cannot copy tmp file from " + tmpPath.toString(); - if (parPath != null) - msg += "\nPlease remember that file upload fails when choosing files from the \"Recently Used\" bookmarks on some OS"; - MessageDialog.openError(browser.getShell(), "Missing file", msg); - continue loop; - } - } - browser.refresh(); - } - } - } catch (Exception e) { - e.printStackTrace(); - MessageDialog.openError(getShell(), "Upload has failed", "Cannot import files to " + currFolderPath); - } - } - - public void setCurrFolderPath(Path currFolderPath) { - this.currFolderPath = currFolderPath; - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/FsStyles.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/FsStyles.java deleted file mode 100644 index 9ae3192..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/FsStyles.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.argeo.cms.ui.fs; - -/** FS Ui specific CSS styles */ -public interface FsStyles { - String BREAD_CRUMB_BTN = "breadCrumb_btn"; - String CONTEXT_MENU_BOX = "contextMenu_box"; - String BUTTON_SUFFIX = "_btn"; -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/package-info.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/package-info.java deleted file mode 100644 index 6a6c272..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** SWT/JFace file system components. */ -package org.argeo.cms.ui.fs; \ No newline at end of file diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/Activator.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/Activator.java deleted file mode 100644 index e10da3a..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/Activator.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.argeo.cms.ui.internal; - -import org.argeo.api.cms.CmsState; -import org.osgi.framework.BundleActivator; -import org.osgi.framework.BundleContext; -import org.osgi.util.tracker.ServiceTracker; - -public class Activator implements BundleActivator { - - // avoid dependency to RWT OSGi - private final static String CONTEXT_NAME_PROP = "contextName"; - - private static ServiceTracker nodeState; - - // @Override - public void start(BundleContext bc) throws Exception { - // UI -// bc.registerService(ApplicationConfiguration.class, new MaintenanceUi(), -// LangUtils.dico(CONTEXT_NAME_PROP, "system")); -// bc.registerService(ApplicationConfiguration.class, new UserUi(), LangUtils.dico(CONTEXT_NAME_PROP, "user")); - - nodeState = new ServiceTracker<>(bc, CmsState.class, null); - nodeState.open(); - } - - @Override - public void stop(BundleContext context) throws Exception { - if (nodeState != null) { - nodeState.close(); - nodeState = null; - } - } - - public static CmsState getNodeState() { - return nodeState.getService(); - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/JcrContentProvider.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/JcrContentProvider.java deleted file mode 100644 index ea0abdf..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/JcrContentProvider.java +++ /dev/null @@ -1,81 +0,0 @@ -package org.argeo.cms.ui.internal; - -import java.util.ArrayList; - -import javax.jcr.Node; -import javax.jcr.NodeIterator; -import javax.jcr.RepositoryException; - -import org.argeo.cms.swt.CmsException; -import org.eclipse.jface.viewers.ITreeContentProvider; -import org.eclipse.jface.viewers.Viewer; - -@Deprecated -class JcrContentProvider implements ITreeContentProvider { - private static final long serialVersionUID = -1333678161322488674L; - - @Override - public void dispose() { - } - - @Override - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - if (newInput == null) - return; - if (!(newInput instanceof Node)) - throw new CmsException("Input " + newInput + " must be a node"); - } - - @Override - public Object[] getElements(Object inputElement) { - try { - Node node = (Node) inputElement; - ArrayList arr = new ArrayList(); - NodeIterator nit = node.getNodes(); - while (nit.hasNext()) { - arr.add(nit.nextNode()); - } - return arr.toArray(); - } catch (RepositoryException e) { - throw new CmsException("Cannot get elements", e); - } - } - - @Override - public Object[] getChildren(Object parentElement) { - try { - Node node = (Node) parentElement; - ArrayList arr = new ArrayList(); - NodeIterator nit = node.getNodes(); - while (nit.hasNext()) { - arr.add(nit.nextNode()); - } - return arr.toArray(); - } catch (RepositoryException e) { - throw new CmsException("Cannot get elements", e); - } - } - - @Override - public Object getParent(Object element) { - try { - Node node = (Node) element; - if (node.getName().equals("")) - return null; - else - return node.getParent(); - } catch (RepositoryException e) { - throw new CmsException("Cannot get elements", e); - } - } - - @Override - public boolean hasChildren(Object element) { - try { - Node node = (Node) element; - return node.hasNodes(); - } catch (RepositoryException e) { - throw new CmsException("Cannot get elements", e); - } - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/JcrFileUploadReceiver.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/JcrFileUploadReceiver.java deleted file mode 100644 index 60bb42b..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/JcrFileUploadReceiver.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.argeo.cms.ui.internal; - -import static javax.jcr.nodetype.NodeType.NT_FILE; - -import java.io.IOException; -import java.io.InputStream; - -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.RepositoryException; -import javax.jcr.nodetype.NodeType; - -import org.apache.commons.io.FilenameUtils; -import org.argeo.api.cms.ux.CmsImageManager; -import org.argeo.cms.ui.widgets.Img; -import org.argeo.jcr.JcrException; -import org.argeo.jcr.JcrUtils; -import org.eclipse.rap.fileupload.FileDetails; -import org.eclipse.rap.fileupload.FileUploadReceiver; - -public class JcrFileUploadReceiver extends FileUploadReceiver { - private Img img; - private final Node parentNode; - private final String nodeName; - private final CmsImageManager imageManager; - - /** If nodeName is null, use the uploaded file name */ - public JcrFileUploadReceiver(Img img, Node parentNode, String nodeName, CmsImageManager imageManager) { - super(); - this.img = img; - this.parentNode = parentNode; - this.nodeName = nodeName; - this.imageManager = imageManager; - } - - @Override - public void receive(InputStream stream, FileDetails details) throws IOException { - try { - String fileName = nodeName != null ? nodeName : details.getFileName(); - String contentType = details.getContentType(); - if (isImage(details.getFileName(), contentType)) { - imageManager.uploadImage(img.getNode(),parentNode, fileName, stream, contentType); - return; - } - - Node fileNode; - if (parentNode.hasNode(fileName)) { - fileNode = parentNode.getNode(fileName); - if (!fileNode.isNodeType(NT_FILE)) - fileNode.remove(); - } - fileNode = JcrUtils.copyStreamAsFile(parentNode, fileName, stream); - - if (contentType != null) { - fileNode.addMixin(NodeType.MIX_MIMETYPE); - fileNode.setProperty(Property.JCR_MIMETYPE, contentType); - } - processNewFile(fileNode); - fileNode.getSession().save(); - } catch (RepositoryException e) { - throw new JcrException("Cannot receive " + details, e); - } - } - - protected Boolean isImage(String fileName, String contentType) { - String ext = FilenameUtils.getExtension(fileName); - return ext != null && (ext.equals("png") || ext.equalsIgnoreCase("jpg")); - } - - protected void processNewFile(Node node) { - - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/SimpleEditableImage.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/SimpleEditableImage.java deleted file mode 100644 index 6162a74..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/SimpleEditableImage.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.argeo.cms.ui.internal; - -import javax.jcr.RepositoryException; - -import org.argeo.api.cms.ux.Cms2DSize; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ui.util.CmsUiUtils; -import org.argeo.cms.ui.widgets.EditableImage; -import org.argeo.cms.ux.AbstractImageManager; -import org.argeo.cms.ux.CmsUxUtils; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Text; - -/** NOT working yet. */ -public class SimpleEditableImage extends EditableImage { - private static final long serialVersionUID = -5689145523114022890L; - - private String src; - private Cms2DSize imageSize; - - public SimpleEditableImage(Composite parent, int swtStyle) { - super(parent, swtStyle); - // load(getControl()); - getParent().layout(); - } - - public SimpleEditableImage(Composite parent, int swtStyle, String src, Cms2DSize imageSize) { - super(parent, swtStyle); - this.src = src; - this.imageSize = imageSize; - } - - @Override - protected Control createControl(Composite box, String style) { - if (isEditing()) { - return createText(box, style); - } else { - return createLabel(box, style); - } - } - - protected String createImgTag() throws RepositoryException { - String imgTag; - if (src != null) - imgTag = CmsUxUtils.img(src, imageSize); - else - imgTag = CmsUiUtils.noImg(imageSize != null ? imageSize : AbstractImageManager.NO_IMAGE_SIZE); - return imgTag; - } - - protected Text createText(Composite box, String style) { - Text text = new Text(box, getStyle()); - CmsSwtUtils.style(text, style); - return text; - } - - public String getSrc() { - return src; - } - - public void setSrc(String src) { - this.src = src; - } - - public Cms2DSize getImageSize() { - return imageSize; - } - - public void setImageSize(Cms2DSize imageSize) { - this.imageSize = imageSize; - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/DefaultRepositoryRegister.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/DefaultRepositoryRegister.java deleted file mode 100644 index 3806341..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/DefaultRepositoryRegister.java +++ /dev/null @@ -1,75 +0,0 @@ -package org.argeo.cms.ui.jcr; - -import java.util.Collections; -import java.util.Map; -import java.util.Observable; -import java.util.TreeMap; - -import javax.jcr.Repository; -import javax.jcr.RepositoryException; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.api.cms.CmsLog; - -public class DefaultRepositoryRegister extends Observable implements RepositoryRegister { - /** Key for a JCR repository alias */ - private final static String CN = CmsConstants.CN; - /** Key for a JCR repository URI */ - // public final static String JCR_REPOSITORY_URI = "argeo.jcr.repository.uri"; - private final static CmsLog log = CmsLog.getLog(DefaultRepositoryRegister.class); - - /** Read only map which will be directly exposed. */ - private Map repositories = Collections.unmodifiableMap(new TreeMap()); - - @SuppressWarnings("rawtypes") - public synchronized Repository getRepository(Map parameters) throws RepositoryException { - if (!parameters.containsKey(CN)) - throw new RepositoryException("Parameter " + CN + " has to be defined."); - String alias = parameters.get(CN).toString(); - if (!repositories.containsKey(alias)) - throw new RepositoryException("No repository registered with alias " + alias); - - return repositories.get(alias); - } - - /** Access to the read-only map */ - public synchronized Map getRepositories() { - return repositories; - } - - /** Registers a service, typically called when OSGi services are bound. */ - @SuppressWarnings("rawtypes") - public synchronized void register(Repository repository, Map properties) { - String alias; - if (properties == null || !properties.containsKey(CN)) { - log.warn("Cannot register a repository if no " + CN + " property is specified."); - return; - } - alias = properties.get(CN).toString(); - Map map = new TreeMap(repositories); - map.put(alias, repository); - repositories = Collections.unmodifiableMap(map); - setChanged(); - notifyObservers(alias); - } - - /** Unregisters a service, typically called when OSGi services are unbound. */ - @SuppressWarnings("rawtypes") - public synchronized void unregister(Repository repository, Map properties) { - // TODO: also check bean name? - if (properties == null || !properties.containsKey(CN)) { - log.warn("Cannot unregister a repository without property " + CN); - return; - } - - String alias = properties.get(CN).toString(); - Map map = new TreeMap(repositories); - if (map.remove(alias) == null) { - log.warn("No repository was registered with alias " + alias); - return; - } - repositories = Collections.unmodifiableMap(map); - setChanged(); - notifyObservers(alias); - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/FullVersioningTreeContentProvider.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/FullVersioningTreeContentProvider.java deleted file mode 100644 index 0f7ee77..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/FullVersioningTreeContentProvider.java +++ /dev/null @@ -1,98 +0,0 @@ -package org.argeo.cms.ui.jcr; - -import java.util.ArrayList; -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.nodetype.NodeType; -import javax.jcr.version.Version; -import javax.jcr.version.VersionHistory; -import javax.jcr.version.VersionIterator; -import javax.jcr.version.VersionManager; - -import org.argeo.eclipse.ui.EclipseUiException; -import org.eclipse.jface.viewers.ITreeContentProvider; -import org.eclipse.jface.viewers.Viewer; - -/** - * Display some version information of a JCR full versionable node in a tree - * like structure - */ -public class FullVersioningTreeContentProvider implements ITreeContentProvider { - private static final long serialVersionUID = 8691772509491211112L; - - /** - * Sends back the first level of the Tree. input element must be a single - * node object - */ - public Object[] getElements(Object inputElement) { - try { - Node rootNode = (Node) inputElement; - String curPath = rootNode.getPath(); - VersionManager vm = rootNode.getSession().getWorkspace() - .getVersionManager(); - - VersionHistory vh = vm.getVersionHistory(curPath); - List result = new ArrayList(); - VersionIterator vi = vh.getAllLinearVersions(); - - while (vi.hasNext()) { - result.add(vi.nextVersion()); - } - return result.toArray(); - } catch (RepositoryException re) { - throw new EclipseUiException( - "Unexpected error while getting version elements", re); - } - } - - public Object[] getChildren(Object parentElement) { - try { - if (parentElement instanceof Version) { - List tmp = new ArrayList(); - tmp.add(((Version) parentElement).getFrozenNode()); - return tmp.toArray(); - } - } catch (RepositoryException re) { - throw new EclipseUiException("Unexpected error while getting child " - + "node for version element", re); - } - return null; - } - - public Object getParent(Object element) { - try { - // this will not work in a simpleVersionning environment, parent is - // not a node. - if (element instanceof Node - && ((Node) element).isNodeType(NodeType.NT_FROZEN_NODE)) { - Node node = (Node) element; - return node.getParent(); - } else - return null; - } catch (RepositoryException e) { - return null; - } - } - - public boolean hasChildren(Object element) { - try { - if (element instanceof Version) - return true; - else if (element instanceof Node) - return ((Node) element).hasNodes(); - else - return false; - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot check children of " + element, e); - } - } - - public void dispose() { - } - - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrBrowserUtils.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrBrowserUtils.java deleted file mode 100644 index b36acc3..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrBrowserUtils.java +++ /dev/null @@ -1,68 +0,0 @@ -package org.argeo.cms.ui.jcr; - -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.PropertyType; -import javax.jcr.RepositoryException; - -import org.argeo.cms.ui.jcr.model.RepositoriesElem; -import org.argeo.cms.ui.jcr.model.RepositoryElem; -import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; -import org.argeo.cms.ui.jcr.model.WorkspaceElem; -import org.argeo.cms.ux.widgets.TreeParent; -import org.argeo.eclipse.ui.EclipseUiException; - -/** Useful methods to manage the JCR Browser */ -public class JcrBrowserUtils { - - public static String getPropertyTypeAsString(Property prop) { - try { - return PropertyType.nameFromValue(prop.getType()); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot check type for " + prop, e); - } - } - - /** Insure that the UI component is not stale, refresh if needed */ - public static void forceRefreshIfNeeded(TreeParent element) { - Node curNode = null; - - boolean doRefresh = false; - - try { - if (element instanceof SingleJcrNodeElem) { - curNode = ((SingleJcrNodeElem) element).getNode(); - } else if (element instanceof WorkspaceElem) { - curNode = ((WorkspaceElem) element).getRootNode(); - } - - if (curNode != null && element.getChildren().length != curNode.getNodes().getSize()) - doRefresh = true; - else if (element instanceof RepositoryElem) { - RepositoryElem rn = (RepositoryElem) element; - if (rn.isConnected()) { - String[] wkpNames = rn.getAccessibleWorkspaceNames(); - if (element.getChildren().length != wkpNames.length) - doRefresh = true; - } - } else if (element instanceof RepositoriesElem) { - doRefresh = true; - // Always force refresh for RepositoriesElem : the condition - // below does not take remote repository into account and it is - // not trivial to do so. - - // RepositoriesElem rn = (RepositoriesElem) element; - // if (element.getChildren().length != - // rn.getRepositoryRegister() - // .getRepositories().size()) - // doRefresh = true; - } - if (doRefresh) { - element.clearChildren(); - element.getChildren(); - } - } catch (RepositoryException re) { - throw new EclipseUiException("Unexpected error while synchronising the UI with the JCR repository", re); - } - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrDClickListener.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrDClickListener.java deleted file mode 100644 index 1707681..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrDClickListener.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.argeo.cms.ui.jcr; - -import javax.jcr.Node; - -import org.argeo.cms.ui.jcr.model.RepositoryElem; -import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; -import org.argeo.cms.ui.jcr.model.WorkspaceElem; -import org.eclipse.jface.viewers.DoubleClickEvent; -import org.eclipse.jface.viewers.IDoubleClickListener; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.TreeViewer; - -/** Centralizes the management of double click on a NodeTreeViewer */ -public class JcrDClickListener implements IDoubleClickListener { - // private final static Log log = LogFactory - // .getLog(GenericNodeDoubleClickListener.class); - - private TreeViewer nodeViewer; - - // private JcrFileProvider jfp; - // private FileHandler fileHandler; - - public JcrDClickListener(TreeViewer nodeViewer) { - this.nodeViewer = nodeViewer; - // jfp = new JcrFileProvider(); - // Commented out. see https://www.argeo.org/bugzilla/show_bug.cgi?id=188 - // fileHandler = null; - // fileHandler = new FileHandler(jfp); - } - - public void doubleClick(DoubleClickEvent event) { - if (event.getSelection() == null || event.getSelection().isEmpty()) - return; - Object obj = ((IStructuredSelection) event.getSelection()).getFirstElement(); - if (obj instanceof RepositoryElem) { - RepositoryElem rpNode = (RepositoryElem) obj; - if (rpNode.isConnected()) { - rpNode.logout(); - } else { - rpNode.login(); - } - nodeViewer.refresh(obj); - } else if (obj instanceof WorkspaceElem) { - WorkspaceElem wn = (WorkspaceElem) obj; - if (wn.isConnected()) - wn.logout(); - else - wn.login(); - nodeViewer.refresh(obj); - } else if (obj instanceof SingleJcrNodeElem) { - SingleJcrNodeElem sjn = (SingleJcrNodeElem) obj; - Node node = sjn.getNode(); - openNode(node); - } - } - - protected void openNode(Node node) { - // TODO implement generic behaviour - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrImages.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrImages.java deleted file mode 100644 index d1d1e31..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrImages.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.argeo.cms.ui.jcr; - -import org.argeo.cms.ui.theme.CmsImages; -import org.eclipse.swt.graphics.Image; - -/** Shared icons. */ -public class JcrImages { - public final static Image NODE = CmsImages.createIcon("node.gif"); - public final static Image FOLDER = CmsImages.createIcon("folder.gif"); - public final static Image FILE = CmsImages.createIcon("file.gif"); - public final static Image BINARY = CmsImages.createIcon("binary.png"); - public final static Image HOME = CmsImages.createIcon("person-logged-in.png"); - public final static Image SORT = CmsImages.createIcon("sort.gif"); - public final static Image REMOVE = CmsImages.createIcon("remove.gif"); - - public final static Image REPOSITORIES = CmsImages.createIcon("repositories.gif"); - public final static Image REPOSITORY_DISCONNECTED = CmsImages.createIcon("repository_disconnected.gif"); - public final static Image REPOSITORY_CONNECTED = CmsImages.createIcon("repository_connected.gif"); - public final static Image REMOTE_DISCONNECTED = CmsImages.createIcon("remote_disconnected.gif"); - public final static Image REMOTE_CONNECTED = CmsImages.createIcon("remote_connected.gif"); - public final static Image WORKSPACE_DISCONNECTED = CmsImages.createIcon("workspace_disconnected.png"); - public final static Image WORKSPACE_CONNECTED = CmsImages.createIcon("workspace_connected.png"); - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrTreeContentProvider.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrTreeContentProvider.java deleted file mode 100644 index cc8479f..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrTreeContentProvider.java +++ /dev/null @@ -1,82 +0,0 @@ -package org.argeo.cms.ui.jcr; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.NodeIterator; -import javax.jcr.RepositoryException; - -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.eclipse.ui.jcr.util.JcrItemsComparator; -import org.eclipse.jface.viewers.ITreeContentProvider; -import org.eclipse.jface.viewers.Viewer; - -/** - * Implementation of the {@code ITreeContentProvider} in order to display a - * single JCR node and its children in a tree like structure - */ -public class JcrTreeContentProvider implements ITreeContentProvider { - private static final long serialVersionUID = -2128326504754297297L; - // private Node rootNode; - private JcrItemsComparator itemComparator = new JcrItemsComparator(); - - /** - * Sends back the first level of the Tree. input element must be a single node - * object - */ - public Object[] getElements(Object inputElement) { - Node rootNode = (Node) inputElement; - return childrenNodes(rootNode); - } - - public Object[] getChildren(Object parentElement) { - return childrenNodes((Node) parentElement); - } - - public Object getParent(Object element) { - try { - Node node = (Node) element; - if (!node.getPath().equals("/")) - return node.getParent(); - else - return null; - } catch (RepositoryException e) { - return null; - } - } - - public boolean hasChildren(Object element) { - try { - return ((Node) element).hasNodes(); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot check children existence on " + element, e); - } - } - - protected Object[] childrenNodes(Node parentNode) { - try { - List children = new ArrayList(); - NodeIterator nit = parentNode.getNodes(); - while (nit.hasNext()) { - Node node = nit.nextNode(); -// if (node.getName().startsWith("rep:") || node.getName().startsWith("jcr:") -// || node.getName().startsWith("nt:")) -// continue nodes; - children.add(node); - } - Node[] arr = children.toArray(new Node[0]); - Arrays.sort(arr, itemComparator); - return arr; - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot list children of " + parentNode, e); - } - } - - public void dispose() { - } - - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/NodeContentProvider.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/NodeContentProvider.java deleted file mode 100644 index 0625cc8..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/NodeContentProvider.java +++ /dev/null @@ -1,175 +0,0 @@ -package org.argeo.cms.ui.jcr; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.RepositoryFactory; -import javax.jcr.Session; -import javax.jcr.nodetype.NodeType; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.cms.jcr.CmsJcrUtils; -import org.argeo.cms.security.Keyring; -import org.argeo.cms.ui.jcr.model.RepositoriesElem; -import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; -import org.argeo.cms.ux.widgets.TreeParent; -import org.eclipse.jface.viewers.ITreeContentProvider; -import org.eclipse.jface.viewers.Viewer; - -/** - * Implementation of the {@code ITreeContentProvider} to display multiple - * repository environment in a tree like structure - */ -public class NodeContentProvider implements ITreeContentProvider { - private static final long serialVersionUID = -4083809398848374403L; - final private RepositoryRegister repositoryRegister; - final private RepositoryFactory repositoryFactory; - - // Current user session on the default workspace of the argeo Node - final private Session userSession; - final private Keyring keyring; - private boolean sortChildren; - - // Reference for cleaning - private SingleJcrNodeElem homeNode = null; - private RepositoriesElem repositoriesNode = null; - - // Utils - private TreeBrowserComparator itemComparator = new TreeBrowserComparator(); - - public NodeContentProvider(Session userSession, Keyring keyring, - RepositoryRegister repositoryRegister, - RepositoryFactory repositoryFactory, Boolean sortChildren) { - this.userSession = userSession; - this.keyring = keyring; - this.repositoryRegister = repositoryRegister; - this.repositoryFactory = repositoryFactory; - this.sortChildren = sortChildren; - } - - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - if (newInput == null)// dispose - return; - - if (userSession != null) { - Node userHome = CmsJcrUtils.getUserHome(userSession); - if (userHome != null) { - // TODO : find a way to dynamically get alias for the node - if (homeNode != null) - homeNode.dispose(); - homeNode = new SingleJcrNodeElem(null, userHome, - userSession.getUserID(), CmsConstants.EGO_REPOSITORY); - } - } - if (repositoryRegister != null) { - if (repositoriesNode != null) - repositoriesNode.dispose(); - repositoriesNode = new RepositoriesElem("Repositories", - repositoryRegister, repositoryFactory, null, userSession, - keyring); - } - } - - /** - * Sends back the first level of the Tree. Independent from inputElement - * that can be null - */ - public Object[] getElements(Object inputElement) { - List objs = new ArrayList(); - if (homeNode != null) - objs.add(homeNode); - if (repositoriesNode != null) - objs.add(repositoriesNode); - return objs.toArray(); - } - - public Object[] getChildren(Object parentElement) { - if (parentElement instanceof TreeParent) { - if (sortChildren) { - Object[] tmpArr = ((TreeParent) parentElement).getChildren(); - if (tmpArr == null) - return new Object[0]; - TreeParent[] arr = new TreeParent[tmpArr.length]; - for (int i = 0; i < tmpArr.length; i++) - arr[i] = (TreeParent) tmpArr[i]; - Arrays.sort(arr, itemComparator); - return arr; - } else - return ((TreeParent) parentElement).getChildren(); - } else - return new Object[0]; - } - - /** - * Sets whether the content provider should order the children nodes or not. - * It is user duty to call a full refresh of the tree after changing this - * parameter. - */ - public void setSortChildren(boolean sortChildren) { - this.sortChildren = sortChildren; - } - - public Object getParent(Object element) { - if (element instanceof TreeParent) { - return ((TreeParent) element).getParent(); - } else - return null; - } - - public boolean hasChildren(Object element) { - if (element instanceof RepositoriesElem) { - RepositoryRegister rr = ((RepositoriesElem) element) - .getRepositoryRegister(); - return rr.getRepositories().size() > 0; - } else if (element instanceof TreeParent) { - TreeParent tp = (TreeParent) element; - return tp.hasChildren(); - } - return false; - } - - public void dispose() { - if (homeNode != null) - homeNode.dispose(); - if (repositoriesNode != null) { - // logs out open sessions - // see https://bugzilla.argeo.org/show_bug.cgi?id=23 - repositoriesNode.dispose(); - } - } - - /** - * Specific comparator for this view. See specification here: - * https://www.argeo.org/bugzilla/show_bug.cgi?id=139 - */ - private class TreeBrowserComparator implements Comparator { - - public int category(TreeParent element) { - if (element instanceof SingleJcrNodeElem) { - Node node = ((SingleJcrNodeElem) element).getNode(); - try { - if (node.isNodeType(NodeType.NT_FOLDER)) - return 5; - } catch (RepositoryException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - return 10; - } - - public int compare(TreeParent o1, TreeParent o2) { - int cat1 = category(o1); - int cat2 = category(o2); - - if (cat1 != cat2) { - return cat1 - cat2; - } - return o1.getName().compareTo(o2.getName()); - } - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/NodeLabelProvider.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/NodeLabelProvider.java deleted file mode 100644 index a5751c0..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/NodeLabelProvider.java +++ /dev/null @@ -1,113 +0,0 @@ -package org.argeo.cms.ui.jcr; - -import javax.jcr.NamespaceException; -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.RepositoryException; -import javax.jcr.nodetype.NodeType; - -import org.argeo.api.cms.CmsLog; -import org.argeo.cms.ui.jcr.model.RemoteRepositoryElem; -import org.argeo.cms.ui.jcr.model.RepositoriesElem; -import org.argeo.cms.ui.jcr.model.RepositoryElem; -import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; -import org.argeo.cms.ui.jcr.model.WorkspaceElem; -import org.argeo.eclipse.ui.EclipseUiException; -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.swt.graphics.Image; - -/** Provides reasonable defaults for know JCR types. */ -public class NodeLabelProvider extends ColumnLabelProvider { - private static final long serialVersionUID = -3662051696443321843L; - - private final static CmsLog log = CmsLog.getLog(NodeLabelProvider.class); - - public String getText(Object element) { - try { - if (element instanceof SingleJcrNodeElem) { - SingleJcrNodeElem sjn = (SingleJcrNodeElem) element; - return getText(sjn.getNode()); - } else if (element instanceof Node) { - return getText((Node) element); - } else - return super.getText(element); - } catch (RepositoryException e) { - throw new EclipseUiException("Unexpected JCR error while getting node name."); - } - } - - protected String getText(Node node) throws RepositoryException { - String label = node.getName(); - StringBuffer mixins = new StringBuffer(""); - for (NodeType type : node.getMixinNodeTypes()) - mixins.append(' ').append(type.getName()); - - return label + " [" + node.getPrimaryNodeType().getName() + mixins + "]"; - } - - @Override - public Image getImage(Object element) { - if (element instanceof RemoteRepositoryElem) { - if (((RemoteRepositoryElem) element).isConnected()) - return JcrImages.REMOTE_CONNECTED; - else - return JcrImages.REMOTE_DISCONNECTED; - } else if (element instanceof RepositoryElem) { - if (((RepositoryElem) element).isConnected()) - return JcrImages.REPOSITORY_CONNECTED; - else - return JcrImages.REPOSITORY_DISCONNECTED; - } else if (element instanceof WorkspaceElem) { - if (((WorkspaceElem) element).isConnected()) - return JcrImages.WORKSPACE_CONNECTED; - else - return JcrImages.WORKSPACE_DISCONNECTED; - } else if (element instanceof RepositoriesElem) { - return JcrImages.REPOSITORIES; - } else if (element instanceof SingleJcrNodeElem) { - Node nodeElem = ((SingleJcrNodeElem) element).getNode(); - return getImage(nodeElem); - - // if (element instanceof Node) { - // return getImage((Node) element); - // } else if (element instanceof WrappedNode) { - // return getImage(((WrappedNode) element).getNode()); - // } else if (element instanceof NodesWrapper) { - // return getImage(((NodesWrapper) element).getNode()); - // } - } - // try { - // return super.getImage(); - // } catch (RepositoryException e) { - // return null; - // } - return super.getImage(element); - } - - protected Image getImage(Node node) { - try { - if (node.getPrimaryNodeType().isNodeType(NodeType.NT_FILE)) - return JcrImages.FILE; - else if (node.getPrimaryNodeType().isNodeType(NodeType.NT_FOLDER)) - return JcrImages.FOLDER; - else if (node.getPrimaryNodeType().isNodeType(NodeType.NT_RESOURCE)) - return JcrImages.BINARY; - try { - // TODO check workspace type? - if (node.getDepth() == 1 && node.hasProperty(Property.JCR_ID)) - return JcrImages.HOME; - - // optimizes -// if (node.hasProperty(LdapAttrs.uid.property()) && node.isNodeType(NodeTypes.NODE_USER_HOME)) -// return JcrImages.HOME; - } catch (NamespaceException e) { - // node namespace is not registered in this repo - } - return JcrImages.NODE; - } catch (RepositoryException e) { - log.warn("Error while retrieving type for " + node + " in order to display corresponding image"); - e.printStackTrace(); - return null; - } - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/OsgiRepositoryRegister.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/OsgiRepositoryRegister.java deleted file mode 100644 index 444350a..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/OsgiRepositoryRegister.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.argeo.cms.ui.jcr; - -import java.util.HashMap; -import java.util.Map; - -import javax.jcr.Repository; - -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.ServiceReference; -import org.osgi.util.tracker.ServiceTracker; - -public class OsgiRepositoryRegister extends DefaultRepositoryRegister { - private final static BundleContext bc = FrameworkUtil.getBundle(OsgiRepositoryRegister.class).getBundleContext(); - private final ServiceTracker repositoryTracker; - - public OsgiRepositoryRegister() { - repositoryTracker = new ServiceTracker(bc, Repository.class, null) { - - @Override - public Repository addingService(ServiceReference reference) { - - Repository repository = super.addingService(reference); - Map props = new HashMap<>(); - for (String key : reference.getPropertyKeys()) { - props.put(key, reference.getProperty(key)); - } - register(repository, props); - return repository; - } - - @Override - public void removedService(ServiceReference reference, Repository service) { - Map props = new HashMap<>(); - for (String key : reference.getPropertyKeys()) { - props.put(key, reference.getProperty(key)); - } - unregister(service, props); - super.removedService(reference, service); - } - - }; - } - - public void init() { - repositoryTracker.open(); - } - - public void destroy() { - repositoryTracker.close(); - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/PropertiesContentProvider.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/PropertiesContentProvider.java deleted file mode 100644 index fd544bb..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/PropertiesContentProvider.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.argeo.cms.ui.jcr; - -import java.util.Set; -import java.util.TreeSet; - -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.PropertyIterator; -import javax.jcr.RepositoryException; - -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.eclipse.ui.jcr.util.JcrItemsComparator; -import org.eclipse.jface.viewers.IStructuredContentProvider; -import org.eclipse.jface.viewers.Viewer; - -/** Simple content provider that displays all properties of a given Node */ -public class PropertiesContentProvider implements IStructuredContentProvider { - private static final long serialVersionUID = 5227554668841613078L; - private JcrItemsComparator itemComparator = new JcrItemsComparator(); - - public void dispose() { - } - - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - } - - public Object[] getElements(Object inputElement) { - try { - if (inputElement instanceof Node) { - Set props = new TreeSet(itemComparator); - PropertyIterator pit = ((Node) inputElement).getProperties(); - while (pit.hasNext()) - props.add(pit.nextProperty()); - return props.toArray(); - } - return new Object[] {}; - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot get element for " - + inputElement, e); - } - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/PropertyLabelProvider.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/PropertyLabelProvider.java deleted file mode 100644 index 37b90f7..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/PropertyLabelProvider.java +++ /dev/null @@ -1,101 +0,0 @@ -package org.argeo.cms.ui.jcr; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; - -import javax.jcr.Property; -import javax.jcr.PropertyType; -import javax.jcr.RepositoryException; -import javax.jcr.Value; - -import org.argeo.cms.ui.CmsUiConstants; -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.jcr.JcrUtils; -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.jface.viewers.ViewerCell; - -/** Default basic label provider for a given JCR Node's properties */ -public class PropertyLabelProvider extends ColumnLabelProvider { - private static final long serialVersionUID = -5405794508731390147L; - - // To be able to change column order easily - public static final int COLUMN_PROPERTY = 0; - public static final int COLUMN_VALUE = 1; - public static final int COLUMN_TYPE = 2; - public static final int COLUMN_ATTRIBUTES = 3; - - // Utils - protected DateFormat timeFormatter = new SimpleDateFormat(CmsUiConstants.DATE_TIME_FORMAT); - - public void update(ViewerCell cell) { - Object element = cell.getElement(); - cell.setText(getColumnText(element, cell.getColumnIndex())); - } - - public String getColumnText(Object element, int columnIndex) { - try { - if (element instanceof Property) { - Property prop = (Property) element; - if (prop.isMultiple()) { - switch (columnIndex) { - case COLUMN_PROPERTY: - return prop.getName(); - case COLUMN_VALUE: - // Corresponding values are listed on children - return ""; - case COLUMN_TYPE: - return JcrBrowserUtils.getPropertyTypeAsString(prop); - case COLUMN_ATTRIBUTES: - return JcrUtils.getPropertyDefinitionAsString(prop); - } - } else { - switch (columnIndex) { - case COLUMN_PROPERTY: - return prop.getName(); - case COLUMN_VALUE: - return formatValueAsString(prop.getValue()); - case COLUMN_TYPE: - return JcrBrowserUtils.getPropertyTypeAsString(prop); - case COLUMN_ATTRIBUTES: - return JcrUtils.getPropertyDefinitionAsString(prop); - } - } - } else if (element instanceof Value) { - Value val = (Value) element; - switch (columnIndex) { - case COLUMN_PROPERTY: - // Nothing to show - return ""; - case COLUMN_VALUE: - return formatValueAsString(val); - case COLUMN_TYPE: - // listed on the parent - return ""; - case COLUMN_ATTRIBUTES: - // Corresponding attributes are listed on the parent - return ""; - } - } - } catch (RepositoryException re) { - throw new EclipseUiException("Cannot retrieve prop value on " + element, re); - } - return null; - } - - private String formatValueAsString(Value value) { - // TODO enhance this method - try { - String strValue; - - if (value.getType() == PropertyType.BINARY) - strValue = ""; - else if (value.getType() == PropertyType.DATE) - strValue = timeFormatter.format(value.getDate().getTime()); - else - strValue = value.getString(); - return strValue; - } catch (RepositoryException e) { - throw new EclipseUiException("unexpected error while formatting value", e); - } - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/RepositoryRegister.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/RepositoryRegister.java deleted file mode 100644 index 802c756..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/RepositoryRegister.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.argeo.cms.ui.jcr; - -import java.util.Map; - -import javax.jcr.Repository; -import javax.jcr.RepositoryFactory; - -/** Allows to register repositories by name. */ -public interface RepositoryRegister extends RepositoryFactory { - /** - * The registered {@link Repository} as a read-only map. Note that this - * method should be called for each access in order to be sure to be up to - * date in case repositories have registered/unregistered - */ - public Map getRepositories(); -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/VersionLabelProvider.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/VersionLabelProvider.java deleted file mode 100644 index 37dfe2b..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/VersionLabelProvider.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.argeo.cms.ui.jcr; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.version.Version; - -import org.argeo.eclipse.ui.EclipseUiException; -import org.eclipse.jface.viewers.ColumnLabelProvider; - -/** - * Simple wrapping of the ColumnLabelProvider class to provide text display in - * order to build a tree for version. The getText() method does not assume that - * {@link Version} extends {@link Node} class to respect JCR 2.0 specification - * - */ -public class VersionLabelProvider extends ColumnLabelProvider { - private static final long serialVersionUID = 5270739851193688238L; - - public String getText(Object element) { - try { - if (element instanceof Version) { - Version version = (Version) element; - return version.getName(); - } else if (element instanceof Node) { - return ((Node) element).getName(); - } - } catch (RepositoryException re) { - throw new EclipseUiException( - "Unexpected error while getting element name", re); - } - return super.getText(element); - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/MaintainedRepositoryElem.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/MaintainedRepositoryElem.java deleted file mode 100644 index d33b33f..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/MaintainedRepositoryElem.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.argeo.cms.ui.jcr.model; - -import javax.jcr.Repository; - -import org.argeo.cms.ux.widgets.TreeParent; - -/** Wrap a MaintainedRepository */ -public class MaintainedRepositoryElem extends RepositoryElem { - - public MaintainedRepositoryElem(String alias, Repository repository, TreeParent parent) { - super(alias, repository, parent); - // if (!(repository instanceof MaintainedRepository)) { - // throw new ArgeoException("Repository " + alias - // + " is not a maintained repository"); - // } - } - - // protected MaintainedRepository getMaintainedRepository() { - // return (MaintainedRepository) getRepository(); - // } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/RemoteRepositoryElem.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/RemoteRepositoryElem.java deleted file mode 100644 index 908d1b1..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/RemoteRepositoryElem.java +++ /dev/null @@ -1,76 +0,0 @@ -package org.argeo.cms.ui.jcr.model; - -import java.util.Arrays; - -import javax.jcr.Node; -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.jcr.RepositoryFactory; -import javax.jcr.Session; -import javax.jcr.SimpleCredentials; - -import org.argeo.cms.ArgeoNames; -import org.argeo.cms.jcr.CmsJcrUtils; -import org.argeo.cms.security.Keyring; -import org.argeo.cms.ux.widgets.TreeParent; -import org.argeo.eclipse.ui.EclipseUiException; - -/** Root of a remote repository */ -public class RemoteRepositoryElem extends RepositoryElem { - private final Keyring keyring; - /** - * A session of the logged in user on the default workspace of the node - * repository. - */ - private final Session userSession; - private final String remoteNodePath; - - private final RepositoryFactory repositoryFactory; - private final String uri; - - public RemoteRepositoryElem(String alias, RepositoryFactory repositoryFactory, String uri, TreeParent parent, - Session userSession, Keyring keyring, String remoteNodePath) { - super(alias, null, parent); - this.repositoryFactory = repositoryFactory; - this.uri = uri; - this.keyring = keyring; - this.userSession = userSession; - this.remoteNodePath = remoteNodePath; - } - - @Override - protected Session repositoryLogin(String workspaceName) throws RepositoryException { - Node remoteRepository = userSession.getNode(remoteNodePath); - String userID = remoteRepository.getProperty(ArgeoNames.ARGEO_USER_ID).getString(); - if (userID.trim().equals("")) { - return getRepository().login(workspaceName); - } else { - String pwdPath = remoteRepository.getPath() + '/' + ArgeoNames.ARGEO_PASSWORD; - char[] password = keyring.getAsChars(pwdPath); - try { - SimpleCredentials credentials = new SimpleCredentials(userID, password); - return getRepository().login(credentials, workspaceName); - } finally { - Arrays.fill(password, 0, password.length, ' '); - } - } - } - - @Override - public Repository getRepository() { - if (repository == null) - repository = CmsJcrUtils.getRepositoryByUri(repositoryFactory, uri); - return super.getRepository(); - } - - public void remove() { - try { - Node remoteNode = userSession.getNode(remoteNodePath); - remoteNode.remove(); - remoteNode.getSession().save(); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot remove " + remoteNodePath, e); - } - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/RepositoriesElem.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/RepositoriesElem.java deleted file mode 100644 index 8c40f8b..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/RepositoriesElem.java +++ /dev/null @@ -1,112 +0,0 @@ -package org.argeo.cms.ui.jcr.model; - -import java.util.Map; - -import javax.jcr.Node; -import javax.jcr.NodeIterator; -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.jcr.RepositoryFactory; -import javax.jcr.Session; - -import org.argeo.cms.ArgeoNames; -import org.argeo.cms.jcr.CmsJcrUtils; -import org.argeo.cms.security.Keyring; -import org.argeo.cms.ui.jcr.RepositoryRegister; -import org.argeo.cms.ux.widgets.TreeParent; -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.eclipse.ui.dialogs.ErrorFeedback; - -/** - * UI Tree component that implements the Argeo abstraction of a - * {@link RepositoryFactory} that enable a user to "mount" various repositories - * in a single Tree like View. It is usually meant to be at the root of the UI - * Tree and thus {@link #getParent()} method will return null. - * - * The {@link RepositoryFactory} is injected at instantiation time and must be - * use get or register new {@link Repository} objects upon which a reference is - * kept here. - */ - -public class RepositoriesElem extends TreeParent implements ArgeoNames { - private final RepositoryRegister repositoryRegister; - private final RepositoryFactory repositoryFactory; - - /** - * A session of the logged in user on the default workspace of the node - * repository. - */ - private final Session userSession; - private final Keyring keyring; - - public RepositoriesElem(String name, RepositoryRegister repositoryRegister, RepositoryFactory repositoryFactory, - TreeParent parent, Session userSession, Keyring keyring) { - super(name); - this.repositoryRegister = repositoryRegister; - this.repositoryFactory = repositoryFactory; - this.userSession = userSession; - this.keyring = keyring; - } - - /** - * Override normal behavior to initialize the various repositories only at - * request time - */ - @Override - public synchronized Object[] getChildren() { - if (isLoaded()) { - return super.getChildren(); - } else { - // initialize current object - Map refRepos = repositoryRegister.getRepositories(); - for (String name : refRepos.keySet()) { - Repository repository = refRepos.get(name); - // if (repository instanceof MaintainedRepository) - // super.addChild(new MaintainedRepositoryElem(name, - // repository, this)); - // else - super.addChild(new RepositoryElem(name, repository, this)); - } - - // remote - if (keyring != null) { - try { - addRemoteRepositories(keyring); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot browse remote repositories", e); - } - } - return super.getChildren(); - } - } - - protected void addRemoteRepositories(Keyring jcrKeyring) throws RepositoryException { - Node userHome = CmsJcrUtils.getUserHome(userSession); - if (userHome != null && userHome.hasNode(ARGEO_REMOTE)) { - NodeIterator it = userHome.getNode(ARGEO_REMOTE).getNodes(); - while (it.hasNext()) { - Node remoteNode = it.nextNode(); - String uri = remoteNode.getProperty(ARGEO_URI).getString(); - try { - RemoteRepositoryElem remoteRepositoryNode = new RemoteRepositoryElem(remoteNode.getName(), - repositoryFactory, uri, this, userSession, jcrKeyring, remoteNode.getPath()); - super.addChild(remoteRepositoryNode); - } catch (Exception e) { - ErrorFeedback.show("Cannot add remote repository " + remoteNode, e); - } - } - } - } - - public void registerNewRepository(String alias, Repository repository) { - // TODO: implement this - // Create a new RepositoryNode Object - // add it - // super.addChild(new RepositoriesNode(...)); - } - - /** Returns the {@link RepositoryRegister} wrapped by this object. */ - public RepositoryRegister getRepositoryRegister() { - return repositoryRegister; - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/RepositoryElem.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/RepositoryElem.java deleted file mode 100644 index 296c369..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/RepositoryElem.java +++ /dev/null @@ -1,98 +0,0 @@ -package org.argeo.cms.ui.jcr.model; - -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.jcr.Session; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.cms.ux.widgets.TreeParent; -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.jcr.JcrUtils; - -/** - * UI Tree component that wraps a JCR {@link Repository}. It also keeps a - * reference to its parent Tree Ui component; typically the unique - * {@link RepositoriesElem} object of the current view to enable bi-directionnal - * browsing in the tree. - */ - -public class RepositoryElem extends TreeParent { - private String alias; - protected Repository repository; - private Session defaultSession = null; - - /** Create a new repository with distinct name and alias */ - public RepositoryElem(String alias, Repository repository, TreeParent parent) { - super(alias); - this.repository = repository; - setParent(parent); - this.alias = alias; - } - - public void login() { - try { - defaultSession = repositoryLogin(CmsConstants.SYS_WORKSPACE); - String[] wkpNames = defaultSession.getWorkspace().getAccessibleWorkspaceNames(); - for (String wkpName : wkpNames) { - if (wkpName.equals(defaultSession.getWorkspace().getName())) - addChild(new WorkspaceElem(this, wkpName, defaultSession)); - else - addChild(new WorkspaceElem(this, wkpName)); - } - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot connect to repository " + alias, e); - } - } - - public synchronized void logout() { - for (Object child : getChildren()) { - if (child instanceof WorkspaceElem) - ((WorkspaceElem) child).logout(); - } - clearChildren(); - JcrUtils.logoutQuietly(defaultSession); - defaultSession = null; - } - - /** - * Actual call to the {@link Repository#login(javax.jcr.Credentials, String)} - * method. To be overridden. - */ - protected Session repositoryLogin(String workspaceName) throws RepositoryException { - return repository.login(workspaceName); - } - - public String[] getAccessibleWorkspaceNames() { - try { - return defaultSession.getWorkspace().getAccessibleWorkspaceNames(); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot retrieve workspace names", e); - } - } - - public void createWorkspace(String workspaceName) { - if (!isConnected()) - login(); - try { - defaultSession.getWorkspace().createWorkspace(workspaceName); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot create workspace", e); - } - } - - /** returns the {@link Repository} referenced by the current UI Node */ - public Repository getRepository() { - return repository; - } - - public String getAlias() { - return alias; - } - - public Boolean isConnected() { - if (defaultSession != null && defaultSession.isLive()) - return true; - else - return false; - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/SingleJcrNodeElem.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/SingleJcrNodeElem.java deleted file mode 100644 index a2584a5..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/SingleJcrNodeElem.java +++ /dev/null @@ -1,84 +0,0 @@ -package org.argeo.cms.ui.jcr.model; - -import javax.jcr.Node; -import javax.jcr.NodeIterator; -import javax.jcr.RepositoryException; -import javax.jcr.Workspace; - -import org.argeo.cms.ux.widgets.TreeParent; -import org.argeo.eclipse.ui.EclipseUiException; - -/** - * UI Tree component. Wraps a node of a JCR {@link Workspace}. It also keeps a - * reference to its parent node that can either be a {@link WorkspaceElem}, a - * {@link SingleJcrNodeElem} or null if the node is "mounted" as the root of the - * UI tree. - */ -public class SingleJcrNodeElem extends TreeParent { - - private final Node node; - private String alias = null; - - /** Creates a new UiNode in the UI Tree */ - public SingleJcrNodeElem(TreeParent parent, Node node, String name) { - super(name); - setParent(parent); - this.node = node; - } - - /** - * Creates a new UiNode in the UI Tree, keeping a reference to the alias of - * the corresponding repository in the current UI environment. It is useful - * to be able to mount nodes as roots of the UI tree. - */ - public SingleJcrNodeElem(TreeParent parent, Node node, String name, String alias) { - super(name); - setParent(parent); - this.node = node; - this.alias = alias; - } - - /** Returns the node wrapped by the current UI object */ - public Node getNode() { - return node; - } - - protected String getRepositoryAlias() { - return alias; - } - - /** - * Overrides normal behaviour to initialise children only when first - * requested - */ - @Override - public synchronized Object[] getChildren() { - if (isLoaded()) { - return super.getChildren(); - } else { - // initialize current object - try { - NodeIterator ni = node.getNodes(); - while (ni.hasNext()) { - Node curNode = ni.nextNode(); - addChild(new SingleJcrNodeElem(this, curNode, curNode.getName())); - } - return super.getChildren(); - } catch (RepositoryException re) { - throw new EclipseUiException("Cannot initialize SingleJcrNode children", re); - } - } - } - - @Override - public boolean hasChildren() { - try { - if (node.getSession().isLive()) - return node.hasNodes(); - else - return false; - } catch (RepositoryException re) { - throw new EclipseUiException("Cannot check children node existence", re); - } - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/WorkspaceElem.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/WorkspaceElem.java deleted file mode 100644 index 2d78666..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/WorkspaceElem.java +++ /dev/null @@ -1,117 +0,0 @@ -package org.argeo.cms.ui.jcr.model; - -import javax.jcr.AccessDeniedException; -import javax.jcr.Node; -import javax.jcr.NodeIterator; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -// import javax.jcr.Workspace; -import javax.jcr.Workspace; - -import org.argeo.cms.ux.widgets.TreeParent; -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.jcr.JcrUtils; - -/** - * UI Tree component. Wraps the root node of a JCR {@link Workspace}. It also - * keeps a reference to its parent {@link RepositoryElem}, to be able to - * retrieve alias of the current used repository - */ -public class WorkspaceElem extends TreeParent { - private Session session = null; - - public WorkspaceElem(RepositoryElem parent, String name) { - this(parent, name, null); - } - - public WorkspaceElem(RepositoryElem parent, String name, Session session) { - super(name); - this.session = session; - setParent(parent); - } - - public synchronized Session getSession() { - return session; - } - - public synchronized Node getRootNode() { - try { - if (session != null) - return session.getRootNode(); - else - return null; - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot get root node of workspace " + getName(), e); - } - } - - public synchronized void login() { - try { - session = ((RepositoryElem) getParent()).repositoryLogin(getName()); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot connect to repository " + getName(), e); - } - } - - public Boolean isConnected() { - if (session != null && session.isLive()) - return true; - else - return false; - } - - @Override - public synchronized void dispose() { - logout(); - super.dispose(); - } - - /** Logouts the session, does not nothing if there is no live session. */ - public synchronized void logout() { - clearChildren(); - JcrUtils.logoutQuietly(session); - session = null; - } - - @Override - public synchronized boolean hasChildren() { - try { - if (isConnected()) - try { - return session.getRootNode().hasNodes(); - } catch (AccessDeniedException e) { - // current user may not have access to the root node - return false; - } - else - return false; - } catch (RepositoryException re) { - throw new EclipseUiException("Unexpected error while checking children node existence", re); - } - } - - /** Override normal behaviour to initialize display of the workspace */ - @Override - public synchronized Object[] getChildren() { - if (isLoaded()) { - return super.getChildren(); - } else { - // initialize current object - try { - Node rootNode; - if (session == null) - return null; - else - rootNode = session.getRootNode(); - NodeIterator ni = rootNode.getNodes(); - while (ni.hasNext()) { - Node node = ni.nextNode(); - addChild(new SingleJcrNodeElem(this, node, node.getName())); - } - return super.getChildren(); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot initialize WorkspaceNode UI object." + getName(), e); - } - } - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/package-info.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/package-info.java deleted file mode 100644 index 8f54744..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Model for SWT/JFace JCR components. */ -package org.argeo.cms.ui.jcr.model; \ No newline at end of file diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/package-info.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/package-info.java deleted file mode 100644 index 26ae330..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** SWT/JFace JCR components. */ -package org.argeo.cms.ui.jcr; \ No newline at end of file diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/package-info.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/package-info.java deleted file mode 100644 index 82fdee7..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** SWT/JFace components for Argeo CMS. */ -package org.argeo.cms.ui; \ No newline at end of file diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/CmsLink.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/CmsLink.java deleted file mode 100644 index e91f9ba..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/CmsLink.java +++ /dev/null @@ -1,282 +0,0 @@ -package org.argeo.cms.ui.util; - -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.api.cms.CmsLog; -import org.argeo.api.cms.ux.CmsStyle; -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.jcr.CmsJcrUtils; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.jcr.JcrException; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.service.ResourceManager; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.MouseListener; -import org.eclipse.swt.graphics.ImageData; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.osgi.framework.BundleContext; - -/** A link to an internal or external location. */ -public class CmsLink implements CmsUiProvider { - private final static CmsLog log = CmsLog.getLog(CmsLink.class); - private BundleContext bundleContext; - - private String label; - private String style; - private String target; - private String image; - private boolean openNew = false; - private MouseListener mouseListener; - - private int horizontalAlignment = SWT.CENTER; - private int verticalAlignment = SWT.CENTER; - - private String loggedInLabel = null; - private String loggedInTarget = null; - - // internal - // private Boolean isUrl = false; - private Integer imageWidth, imageHeight; - - public CmsLink() { - super(); - } - - public CmsLink(String label, String target) { - this(label, target, (String) null); - } - - public CmsLink(String label, String target, CmsStyle style) { - this(label, target, style != null ? style.style() : null); - } - - public CmsLink(String label, String target, String style) { - super(); - this.label = label; - this.target = target; - this.style = style; - init(); - } - - public void init() { - if (image != null) { - ImageData image = loadImage(); - if (imageHeight == null && imageWidth == null) { - imageWidth = image.width; - imageHeight = image.height; - } else if (imageHeight == null) { - imageHeight = (imageWidth * image.height) / image.width; - } else if (imageWidth == null) { - imageWidth = (imageHeight * image.width) / image.height; - } - } - } - - /** @return {@link Composite} with a single {@link Label} child. */ - @Override - public Control createUi(final Composite parent, Node context) { -// if (image != null && (imageWidth == null || imageHeight == null)) { -// throw new CmsException("Image is not properly configured." -// + " Make sure bundleContext property is set and init() method has been called."); -// } - - Composite comp = new Composite(parent, SWT.NONE); - comp.setLayout(CmsSwtUtils.noSpaceGridLayout()); - - Label link = new Label(comp, SWT.NONE); - CmsSwtUtils.markup(link); - GridData layoutData = new GridData(horizontalAlignment, verticalAlignment, false, false); - if (image != null) { - if (imageHeight != null) - layoutData.heightHint = imageHeight; - if (label == null) - if (imageWidth != null) - layoutData.widthHint = imageWidth; - } - - link.setLayoutData(layoutData); - CmsSwtUtils.style(comp, style != null ? style : getDefaultStyle()); - CmsSwtUtils.style(link, style != null ? style : getDefaultStyle()); - - // label - StringBuilder labelText = new StringBuilder(); - if (loggedInTarget != null && isLoggedIn()) { - labelText.append(""); - } else if (target != null) { - labelText.append(""); - } - if (image != null) { - registerImageIfNeeded(); - String imageLocation = RWT.getResourceManager().getLocation(image); - labelText.append(""); - - } - - if (loggedInLabel != null && isLoggedIn()) { - labelText.append(' ').append(loggedInLabel); - } else if (label != null) { - labelText.append(' ').append(label); - } - - if ((loggedInTarget != null && isLoggedIn()) || target != null) - labelText.append(""); - - link.setText(labelText.toString()); - - if (mouseListener != null) - link.addMouseListener(mouseListener); - - return comp; - } - - private void registerImageIfNeeded() { - ResourceManager resourceManager = RWT.getResourceManager(); - if (!resourceManager.isRegistered(image)) { - URL res = getImageUrl(); - try (InputStream inputStream = res.openStream()) { - resourceManager.register(image, inputStream); - if (log.isTraceEnabled()) - log.trace("Registered image " + image); - } catch (IOException e) { - throw new RuntimeException("Cannot load image " + image, e); - } - } - } - - private ImageData loadImage() { - URL url = getImageUrl(); - ImageData result = null; - try (InputStream inputStream = url.openStream()) { - result = new ImageData(inputStream); - if (log.isTraceEnabled()) - log.trace("Loaded image " + image); - } catch (IOException e) { - throw new RuntimeException("Cannot load image " + image, e); - } - return result; - } - - private URL getImageUrl() { - URL url; - try { - // pure URL - url = new URL(image); - } catch (MalformedURLException e1) { - url = bundleContext.getBundle().getResource(image); - } - - if (url == null) - throw new IllegalStateException("No image " + image + " available."); - - return url; - } - - public void setBundleContext(BundleContext bundleContext) { - this.bundleContext = bundleContext; - } - - public void setLabel(String label) { - this.label = label; - } - - public void setStyle(String style) { - this.style = style; - } - - /** @deprecated Use {@link #setStyle(String)} instead. */ - @Deprecated - public void setCustom(String custom) { - this.style = custom; - } - - public void setTarget(String target) { - this.target = target; - // try { - // new URL(target); - // isUrl = true; - // } catch (MalformedURLException e1) { - // isUrl = false; - // } - } - - public void setImage(String image) { - this.image = image; - } - - public void setLoggedInLabel(String loggedInLabel) { - this.loggedInLabel = loggedInLabel; - } - - public void setLoggedInTarget(String loggedInTarget) { - this.loggedInTarget = loggedInTarget; - } - - public void setMouseListener(MouseListener mouseListener) { - this.mouseListener = mouseListener; - } - - public void setvAlign(String vAlign) { - if ("bottom".equals(vAlign)) { - verticalAlignment = SWT.BOTTOM; - } else if ("top".equals(vAlign)) { - verticalAlignment = SWT.TOP; - } else if ("center".equals(vAlign)) { - verticalAlignment = SWT.CENTER; - } else { - throw new IllegalArgumentException( - "Unsupported vertical alignment " + vAlign + " (must be: top, bottom or center)"); - } - } - - protected boolean isLoggedIn() { - return !CurrentUser.isAnonymous(); - } - - public void setImageWidth(Integer imageWidth) { - this.imageWidth = imageWidth; - } - - public void setImageHeight(Integer imageHeight) { - this.imageHeight = imageHeight; - } - - public void setOpenNew(boolean openNew) { - this.openNew = openNew; - } - - protected String getDefaultStyle() { - return SimpleStyle.link.name(); - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/CmsPane.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/CmsPane.java deleted file mode 100644 index fc0c821..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/CmsPane.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.argeo.cms.ui.util; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.layout.RowLayout; -import org.eclipse.swt.widgets.Composite; - -/** The main pane of a CMS display, with QA and support areas. */ -public class CmsPane { - - private Composite mainArea; - private Composite qaArea; - private Composite supportArea; - - public CmsPane(Composite parent, int style) { - parent.setLayout(CmsSwtUtils.noSpaceGridLayout()); - -// qaArea = new Composite(parent, SWT.NONE); -// qaArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); -// RowLayout qaLayout = new RowLayout(); -// qaLayout.spacing = 0; -// qaArea.setLayout(qaLayout); - - mainArea = new Composite(parent, SWT.NONE); - mainArea.setLayout(new GridLayout()); - mainArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - -// supportArea = new Composite(parent, SWT.NONE); -// supportArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); -// RowLayout supportLayout = new RowLayout(); -// supportLayout.spacing = 0; -// supportArea.setLayout(supportLayout); - } - - public Composite getMainArea() { - return mainArea; - } - - public Composite getQaArea() { - return qaArea; - } - - public Composite getSupportArea() { - return supportArea; - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/CmsUiUtils.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/CmsUiUtils.java deleted file mode 100644 index 3522f1b..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/CmsUiUtils.java +++ /dev/null @@ -1,192 +0,0 @@ -package org.argeo.cms.ui.util; - -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.servlet.http.HttpServletRequest; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.api.cms.ux.Cms2DSize; -import org.argeo.api.cms.ux.CmsView; -import org.argeo.cms.jcr.CmsJcrUtils; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ui.CmsUiConstants; -import org.argeo.cms.ux.AbstractImageManager; -import org.argeo.cms.ux.CmsUxUtils; -import org.argeo.jcr.JcrUtils; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.service.ResourceManager; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.graphics.ImageData; -import org.eclipse.swt.layout.RowData; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Table; - -/** Static utilities for the CMS framework. */ -public class CmsUiUtils { - // private final static Log log = LogFactory.getLog(CmsUiUtils.class); - - /* - * CMS VIEW - */ - - /** - * The CMS view related to this display, or null if none is available from this - * call. - * - * @deprecated Use {@link CmsSwtUtils#getCmsView(Composite)} instead. - */ - @Deprecated - public static CmsView getCmsView() { -// return UiContext.getData(CmsView.class.getName()); - return CmsSwtUtils.getCmsView(Display.getCurrent().getActiveShell()); - } - - public static StringBuilder getServerBaseUrl(HttpServletRequest request) { - try { - URL url = new URL(request.getRequestURL().toString()); - StringBuilder buf = new StringBuilder(); - buf.append(url.getProtocol()).append("://").append(url.getHost()); - if (url.getPort() != -1) - buf.append(':').append(url.getPort()); - return buf; - } catch (MalformedURLException e) { - throw new IllegalArgumentException("Cannot extract server base URL from " + request.getRequestURL(), e); - } - } - - // - public static String getDataUrl(Node node, HttpServletRequest request) { - try { - StringBuilder buf = getServerBaseUrl(request); - buf.append(getDataPath(node)); - return new URL(buf.toString()).toString(); - } catch (MalformedURLException e) { - throw new IllegalArgumentException("Cannot build data URL for " + node, e); - } - } - - /** A path in the node repository */ - public static String getDataPath(Node node) { - return getDataPath(CmsConstants.EGO_REPOSITORY, node); - } - - public static String getDataPath(String cn, Node node) { - return CmsJcrUtils.getDataPath(cn, node); - } - - /** Clean reserved URL characters for use in HTTP links. */ - public static String getDataPathForUrl(Node node) { - return CmsSwtUtils.cleanPathForUrl(getDataPath(node)); - } - - /** @deprecated Use rowData16px() instead. GridData should not be reused. */ - @Deprecated - public static RowData ROW_DATA_16px = new RowData(16, 16); - - - - /* - * FORM LAYOUT - */ - - - - @Deprecated - public static void setItemHeight(Table table, int height) { - table.setData(CmsUiConstants.ITEM_HEIGHT, height); - } - - // - // JCR - // - public static Node getOrAddEmptyFile(Node parent, Enum child) throws RepositoryException { - if (has(parent, child)) - return child(parent, child); - return JcrUtils.copyBytesAsFile(parent, child.name(), new byte[0]); - } - - public static Node child(Node parent, Enum en) throws RepositoryException { - return parent.getNode(en.name()); - } - - public static Boolean has(Node parent, Enum en) throws RepositoryException { - return parent.hasNode(en.name()); - } - - public static Node getOrAdd(Node parent, Enum en) throws RepositoryException { - return getOrAdd(parent, en, null); - } - - public static Node getOrAdd(Node parent, Enum en, String primaryType) throws RepositoryException { - if (has(parent, en)) - return child(parent, en); - else if (primaryType == null) - return parent.addNode(en.name()); - else - return parent.addNode(en.name(), primaryType); - } - - // IMAGES - - public static String img(Node fileNode, String width, String height) { - return img(null, fileNode, width, height); - } - - public static String img(String serverBase, Node fileNode, String width, String height) { -// String src = (serverBase != null ? serverBase : "") + NodeUtils.getDataPath(fileNode); - String src; - src = (serverBase != null ? serverBase : "") + getDataPathForUrl(fileNode); - return CmsUxUtils.imgBuilder(src, width, height).append("/>").toString(); - } - - public static String noImg(Cms2DSize size) { - ResourceManager rm = RWT.getResourceManager(); - return CmsUxUtils.img(rm.getLocation(AbstractImageManager.NO_IMAGE), size); - } - - public static String noImg() { - return noImg(AbstractImageManager.NO_IMAGE_SIZE); - } - -// public static Image noImage(Cms2DSize size) { -// ResourceManager rm = RWT.getResourceManager(); -// InputStream in = null; -// try { -// in = rm.getRegisteredContent(AbstractImageManager.NO_IMAGE); -// ImageData id = new ImageData(in); -// ImageData scaled = id.scaledTo(size.getWidth(), size.getHeight()); -// Image image = new Image(Display.getCurrent(), scaled); -// return image; -// } finally { -// try { -// in.close(); -// } catch (IOException e) { -// // silent -// } -// } -// } - - /** Lorem ipsum text to be used during development. */ - public final static String LOREM_IPSUM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit." - + " Etiam eleifend hendrerit sem, ac ultricies massa ornare ac." - + " Cras aliquam sodales risus, vitae varius lacus molestie quis." - + " Vivamus consequat, leo id lacinia volutpat, eros diam efficitur urna, finibus interdum risus turpis at nisi." - + " Curabitur vulputate nulla quis scelerisque fringilla. Integer consectetur turpis id lobortis accumsan." - + " Pellentesque commodo turpis ac diam ultricies dignissim." - + " Curabitur sit amet dolor volutpat lacus aliquam ornare quis sed velit." - + " Integer varius quis est et tristique." - + " Suspendisse pharetra porttitor purus, eget condimentum magna." - + " Duis vitae turpis eros. Sed tincidunt lacinia rutrum." - + " Aliquam velit velit, rutrum ut augue sed, condimentum lacinia augue."; - - /** Singleton. */ - private CmsUiUtils() { - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/DefaultImageManager.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/DefaultImageManager.java deleted file mode 100644 index b431fc3..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/DefaultImageManager.java +++ /dev/null @@ -1,133 +0,0 @@ -package org.argeo.cms.ui.util; - -import static javax.jcr.Node.JCR_CONTENT; -import static javax.jcr.Property.JCR_DATA; -import static javax.jcr.nodetype.NodeType.NT_FILE; -import static javax.jcr.nodetype.NodeType.NT_RESOURCE; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Paths; - -import javax.jcr.Binary; -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.RepositoryException; - -import org.apache.commons.io.IOUtils; -import org.argeo.api.cms.CmsLog; -import org.argeo.api.cms.ux.Cms2DSize; -import org.argeo.cms.swt.AbstractSwtImageManager; -import org.argeo.jcr.JcrException; -import org.argeo.jcr.JcrUtils; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.service.ResourceManager; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.graphics.ImageData; -import org.eclipse.swt.widgets.Display; - -/** Manages only public images so far. */ -public class DefaultImageManager extends AbstractSwtImageManager { - private final static CmsLog log = CmsLog.getLog(DefaultImageManager.class); - - /** @return null if not available */ - @Override - public String getImageUrl(Node node) { - return CmsUiUtils.getDataPathForUrl(node); - } - - protected String getResourceName(Node node) { - try { - String workspace = node.getSession().getWorkspace().getName(); - if (node.hasNode(JCR_CONTENT)) - return workspace + '_' + node.getNode(JCR_CONTENT).getIdentifier(); - else - return workspace + '_' + node.getIdentifier(); - } catch (RepositoryException e) { - throw new JcrException(e); - } - } - - public Binary getImageBinary(Node node) { - try { - if (node.isNodeType(NT_FILE)) { - return node.getNode(JCR_CONTENT).getProperty(JCR_DATA).getBinary(); - } else { - return null; - } - } catch (RepositoryException e) { - throw new JcrException(e); - } - } - - public Image getSwtImage(Node node) { - InputStream inputStream = null; - Binary binary = getImageBinary(node); - if (binary == null) - return null; - try { - inputStream = binary.getStream(); - return new Image(Display.getCurrent(), inputStream); - } catch (RepositoryException e) { - throw new JcrException(e); - } finally { - IOUtils.closeQuietly(inputStream); - JcrUtils.closeQuietly(binary); - } - } - - @Override - public String uploadImage(Node context, Node parentNode, String fileName, InputStream in, String contentType) { - InputStream inputStream = null; - try { - String previousResourceName = null; - if (parentNode.hasNode(fileName)) { - Node node = parentNode.getNode(fileName); - previousResourceName = getResourceName(node); - if (node.hasNode(JCR_CONTENT)) { - node.getNode(JCR_CONTENT).remove(); - node.addNode(JCR_CONTENT, NT_RESOURCE); - } - } - - byte[] arr = IOUtils.toByteArray(in); - Node fileNode = JcrUtils.copyBytesAsFile(parentNode, fileName, arr); - inputStream = new ByteArrayInputStream(arr); - ImageData id = new ImageData(inputStream); - processNewImageFile(context, fileNode, id); - - String mime = contentType != null ? contentType : Files.probeContentType(Paths.get(fileName)); - if (mime != null) { - fileNode.getNode(JCR_CONTENT).setProperty(Property.JCR_MIMETYPE, mime); - } - fileNode.getSession().save(); - - // reset resource manager - ResourceManager resourceManager = RWT.getResourceManager(); - if (previousResourceName != null && resourceManager.isRegistered(previousResourceName)) { - resourceManager.unregister(previousResourceName); - if (log.isDebugEnabled()) - log.debug("Unregistered image " + previousResourceName); - } - return CmsUiUtils.getDataPath(fileNode); - } catch (IOException e) { - throw new RuntimeException("Cannot upload image " + fileName + " in " + parentNode, e); - } catch (RepositoryException e) { - throw new JcrException(e); - } finally { - IOUtils.closeQuietly(inputStream); - } - } - - /** Does nothing by default. */ - protected void processNewImageFile(Node context, Node fileNode, ImageData id) - throws RepositoryException, IOException { - } - - @Override - protected String noImg(Cms2DSize size) { - return CmsUiUtils.noImg(size); - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/MenuLink.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/MenuLink.java deleted file mode 100644 index 284d2bd..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/MenuLink.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.argeo.cms.ui.util; - -import org.argeo.cms.swt.CmsStyles; - -/** - * Convenience class setting the custom style {@link CmsStyles#CMS_MENU_LINK} on - * a {@link CmsLink} when simple menus are used. - */ -public class MenuLink extends CmsLink { - public MenuLink() { - setCustom(CmsStyles.CMS_MENU_LINK); - } - - public MenuLink(String label, String target, String custom) { - super(label, target, custom); - } - - public MenuLink(String label, String target) { - super(label, target, CmsStyles.CMS_MENU_LINK); - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleCmsHeader.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleCmsHeader.java deleted file mode 100644 index ab6a29f..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleCmsHeader.java +++ /dev/null @@ -1,97 +0,0 @@ -package org.argeo.cms.ui.util; - -import java.util.ArrayList; -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.swt.CmsStyles; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ui.CmsUiProvider; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -/** A header in three parts */ -public class SimpleCmsHeader implements CmsUiProvider { - private List lead = new ArrayList(); - private List center = new ArrayList(); - private List end = new ArrayList(); - - private Boolean subPartsSameWidth = false; - - @Override - public Control createUi(Composite parent, Node context) throws RepositoryException { - Composite header = new Composite(parent, SWT.NONE); - header.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_HEADER); - header.setBackgroundMode(SWT.INHERIT_DEFAULT); - header.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(3, false))); - - configurePart(context, header, lead); - configurePart(context, header, center); - configurePart(context, header, end); - return header; - } - - protected void configurePart(Node context, Composite parent, List partProviders) - throws RepositoryException { - final int style; - final String custom; - if (lead == partProviders) { - style = SWT.LEAD; - custom = CmsStyles.CMS_HEADER_LEAD; - } else if (center == partProviders) { - style = SWT.CENTER; - custom = CmsStyles.CMS_HEADER_CENTER; - } else if (end == partProviders) { - style = SWT.END; - custom = CmsStyles.CMS_HEADER_END; - } else { - throw new CmsException("Unsupported part providers " + partProviders); - } - - Composite part = new Composite(parent, SWT.NONE); - part.setData(RWT.CUSTOM_VARIANT, custom); - GridData gridData = new GridData(style, SWT.FILL, true, true); - part.setLayoutData(gridData); - part.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(partProviders.size(), subPartsSameWidth))); - for (CmsUiProvider uiProvider : partProviders) { - Control subPart = uiProvider.createUi(part, context); - subPart.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - } - } - - public void setLead(List lead) { - this.lead = lead; - } - - public void setCenter(List center) { - this.center = center; - } - - public void setEnd(List end) { - this.end = end; - } - - public void setSubPartsSameWidth(Boolean subPartsSameWidth) { - this.subPartsSameWidth = subPartsSameWidth; - } - - public List getLead() { - return lead; - } - - public List getCenter() { - return center; - } - - public List getEnd() { - return end; - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleDynamicPages.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleDynamicPages.java deleted file mode 100644 index c61a2fc..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleDynamicPages.java +++ /dev/null @@ -1,118 +0,0 @@ -package org.argeo.cms.ui.util; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; - -import javax.jcr.Node; -import javax.jcr.NodeIterator; -import javax.jcr.Property; -import javax.jcr.PropertyIterator; -import javax.jcr.PropertyType; -import javax.jcr.RepositoryException; -import javax.jcr.Value; - -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.jcr.JcrUtils; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; - -public class SimpleDynamicPages implements CmsUiProvider { - - @Override - public Control createUi(Composite parent, Node context) - throws RepositoryException { - if (context == null) - throw new CmsException("Context cannot be null"); - parent.setLayout(new GridLayout(2, false)); - - // parent - if (!context.getPath().equals("/")) { - new CmsLink("..", context.getParent().getPath()).createUi(parent, - context); - new Label(parent, SWT.NONE).setText(context.getParent() - .getPrimaryNodeType().getName()); - } - - // context - Label contextL = new Label(parent, SWT.NONE); - contextL.setData(RWT.MARKUP_ENABLED, true); - contextL.setText("" + context.getName() + ""); - new Label(parent, SWT.NONE).setText(context.getPrimaryNodeType() - .getName()); - - // children - // Label childrenL = new Label(parent, SWT.NONE); - // childrenL.setData(RWT.MARKUP_ENABLED, true); - // childrenL.setText("Children:"); - // childrenL.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, - // false, 2, 1)); - - for (NodeIterator nIt = context.getNodes(); nIt.hasNext();) { - Node child = nIt.nextNode(); - new CmsLink(child.getName(), child.getPath()).createUi(parent, - context); - - new Label(parent, SWT.NONE).setText(child.getPrimaryNodeType() - .getName()); - } - - // properties - // Label propsL = new Label(parent, SWT.NONE); - // propsL.setData(RWT.MARKUP_ENABLED, true); - // propsL.setText("Properties:"); - // propsL.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false, - // 2, 1)); - for (PropertyIterator pIt = context.getProperties(); pIt.hasNext();) { - Property property = pIt.nextProperty(); - - Label label = new Label(parent, SWT.NONE); - label.setText(property.getName()); - label.setToolTipText(JcrUtils - .getPropertyDefinitionAsString(property)); - - new Label(parent, SWT.NONE).setText(getPropAsString(property)); - } - - return null; - } - - private String getPropAsString(Property property) - throws RepositoryException { - String result = ""; - DateFormat timeFormatter = new SimpleDateFormat(""); - if (property.isMultiple()) { - result = getMultiAsString(property, ", "); - } else { - Value value = property.getValue(); - if (value.getType() == PropertyType.BINARY) - result = ""; - else if (value.getType() == PropertyType.DATE) - result = timeFormatter.format(value.getDate().getTime()); - else - result = value.getString(); - } - return result; - } - - private String getMultiAsString(Property property, String separator) - throws RepositoryException { - if (separator == null) - separator = "; "; - Value[] values = property.getValues(); - StringBuilder builder = new StringBuilder(); - for (Value val : values) { - String currStr = val.getString(); - if (!"".equals(currStr.trim())) - builder.append(currStr).append(separator); - } - if (builder.lastIndexOf(separator) >= 0) - return builder.substring(0, builder.length() - separator.length()); - else - return builder.toString(); - } -} \ No newline at end of file diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleStaticPage.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleStaticPage.java deleted file mode 100644 index 63e504b..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleStaticPage.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.argeo.cms.ui.util; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.swt.CmsStyles; -import org.argeo.cms.ui.CmsUiProvider; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; - -public class SimpleStaticPage implements CmsUiProvider { - private String text; - - @Override - public Control createUi(Composite parent, Node context) - throws RepositoryException { - Label textC = new Label(parent, SWT.WRAP); - textC.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_STATIC_TEXT); - textC.setData(RWT.MARKUP_ENABLED, Boolean.TRUE); - textC.setText(text); - - return textC; - } - - public void setText(String text) { - this.text = text; - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleStyle.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleStyle.java deleted file mode 100644 index b5fca26..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleStyle.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.argeo.cms.ui.util; - -import org.argeo.api.cms.ux.CmsStyle; - -/** Simple styles used by the CMS UI utilities. */ -public enum SimpleStyle implements CmsStyle { - link; -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/StyleSheetResourceLoader.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/StyleSheetResourceLoader.java deleted file mode 100644 index 1e17dc9..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/StyleSheetResourceLoader.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.argeo.cms.ui.util; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.LinkedHashMap; -import java.util.Map; - -import org.apache.commons.io.IOUtils; -import org.argeo.cms.swt.CmsException; -import org.eclipse.rap.rwt.service.ResourceLoader; -import org.osgi.framework.Bundle; - -/** {@link ResourceLoader} caching stylesheets. */ -public class StyleSheetResourceLoader implements ResourceLoader { - private Bundle themeBundle; - private Map stylesheets = new LinkedHashMap(); - - public StyleSheetResourceLoader(Bundle themeBundle) { - this.themeBundle = themeBundle; - } - - @Override - public InputStream getResourceAsStream(String resourceName) throws IOException { - if (!stylesheets.containsKey(resourceName)) { - // TODO deal with other bundles - // Bundle bundle = bundleContext.getBundle(); - // String location = - // bundle.getLocation().substring("initial@reference:".length()); - // if (location.startsWith("file:")) { - // Path path = null; - // try { - // path = Paths.get(new URI(location)); - // } catch (URISyntaxException e) { - // e.printStackTrace(); - // } - // if (path != null) { - // Path resourcePath = path.resolve(resourceName); - // if (Files.exists(resourcePath)) - // return Files.newInputStream(resourcePath); - // } - // } - - URL res = themeBundle.getEntry(resourceName); - if (res == null) - throw new CmsException( - "Entry " + resourceName + " not found in bundle " + themeBundle.getSymbolicName()); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - IOUtils.copy(res.openStream(), out); - stylesheets.put(resourceName, new StyleSheet(out.toByteArray())); - } - return new ByteArrayInputStream(stylesheets.get(resourceName).getData()); - // return res.openStream(); - } - - private class StyleSheet { - private byte[] data; - - public StyleSheet(byte[] data) { - super(); - this.data = data; - } - - public byte[] getData() { - return data; - } - - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SystemNotifications.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SystemNotifications.java deleted file mode 100644 index 5a00781..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SystemNotifications.java +++ /dev/null @@ -1,129 +0,0 @@ -package org.argeo.cms.ui.util; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.text.SimpleDateFormat; -import java.util.Date; - -import org.apache.commons.io.IOUtils; -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.swt.CmsStyles; -import org.argeo.cms.swt.CmsSwtUtils; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.events.MouseListener; -import org.eclipse.swt.events.ShellAdapter; -import org.eclipse.swt.events.ShellEvent; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Shell; - -/** Shell displaying system notifications such as exceptions */ -public class SystemNotifications extends Shell implements CmsStyles, - MouseListener { - private static final long serialVersionUID = -8129377525216022683L; - - private Control source; - - public SystemNotifications(Control source) { - super(source.getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP); - setData(RWT.CUSTOM_VARIANT, CMS_USER_MENU); - - this.source = source; - - // TODO UI - // setLocation(source.toDisplay(source.getSize().x - getSize().x, - // source.getSize().y)); - setLayout(new GridLayout()); - addMouseListener(this); - - addShellListener(new ShellAdapter() { - private static final long serialVersionUID = 5178980294808435833L; - - @Override - public void shellDeactivated(ShellEvent e) { - close(); - dispose(); - } - }); - - } - - public void notifyException(Throwable exception) { - Composite pane = this; - - Label lbl = new Label(pane, SWT.NONE); - lbl.setText(exception.getLocalizedMessage() - + (exception instanceof CmsException ? "" : "(" - + exception.getClass().getName() + ")") + "\n"); - lbl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); - lbl.addMouseListener(this); - if (exception.getCause() != null) - appendCause(pane, exception.getCause()); - - StringBuilder mailToUrl = new StringBuilder("mailto:?"); - try { - mailToUrl.append("subject=").append( - URLEncoder.encode( - "Exception " - + new SimpleDateFormat("yyyy-MM-dd hh:mm") - .format(new Date()), "UTF-8") - .replace("+", "%20")); - - StringWriter sw = new StringWriter(); - exception.printStackTrace(new PrintWriter(sw)); - IOUtils.closeQuietly(sw); - - // see - // http://stackoverflow.com/questions/4737841/urlencoder-not-able-to-translate-space-character - String encoded = URLEncoder.encode(sw.toString(), "UTF-8").replace( - "+", "%20"); - mailToUrl.append("&body=").append(encoded); - } catch (UnsupportedEncodingException e) { - mailToUrl.append("&body=").append("Could not encode: ") - .append(e.getMessage()); - } - Label mailTo = new Label(pane, SWT.NONE); - CmsSwtUtils.markup(mailTo); - mailTo.setText("Send details"); - mailTo.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false)); - - pack(); - layout(); - - setLocation(source.toDisplay(source.getSize().x - getSize().x, - source.getSize().y - getSize().y)); - open(); - } - - private void appendCause(Composite parent, Throwable e) { - Label lbl = new Label(parent, SWT.NONE); - lbl.setText(" caused by: " + e.getLocalizedMessage() + " (" - + e.getClass().getName() + ")" + "\n"); - lbl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); - lbl.addMouseListener(this); - if (e.getCause() != null) - appendCause(parent, e.getCause()); - } - - @Override - public void mouseDoubleClick(MouseEvent e) { - } - - @Override - public void mouseDown(MouseEvent e) { - close(); - dispose(); - } - - @Override - public void mouseUp(MouseEvent e) { - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/UserMenu.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/UserMenu.java deleted file mode 100644 index 09aeff6..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/UserMenu.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.argeo.cms.ui.util; - -import javax.jcr.Node; - -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.swt.auth.CmsLoginShell; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.ShellAdapter; -import org.eclipse.swt.events.ShellEvent; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Shell; - -/** The site-related user menu */ -public class UserMenu extends CmsLoginShell { - private final Control source; - private final Node context; - - public UserMenu(Control source, Node context) { - // FIXME pass CMS context - super(CmsUiUtils.getCmsView(), null); - this.context = context; - createUi(); - if (source == null) - throw new CmsException("Source control cannot be null."); - this.source = source; - open(); - } - - @Override - protected Shell createShell() { - return new Shell(Display.getCurrent(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP); - } - - @Override - public void open() { - Shell shell = getShell(); - shell.pack(); - shell.layout(); - shell.setLocation(source.toDisplay(source.getSize().x - shell.getSize().x, source.getSize().y)); - shell.addShellListener(new ShellAdapter() { - private static final long serialVersionUID = 5178980294808435833L; - - @Override - public void shellDeactivated(ShellEvent e) { - closeShell(); - } - }); - super.open(); - } - - protected Node getContext() { - return context; - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/UserMenuLink.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/UserMenuLink.java deleted file mode 100644 index 317a7b5..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/UserMenuLink.java +++ /dev/null @@ -1,84 +0,0 @@ -package org.argeo.cms.ui.util; - -import javax.jcr.Node; - -import org.argeo.cms.CmsMsg; -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.swt.CmsStyles; -import org.argeo.cms.swt.auth.CmsLoginShell; -import org.eclipse.swt.events.DisposeEvent; -import org.eclipse.swt.events.DisposeListener; -import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.events.MouseListener; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; - -/** Open the user menu when clicked */ -public class UserMenuLink extends MenuLink { - - public UserMenuLink() { - setCustom(CmsStyles.CMS_USER_MENU_LINK); - } - - @Override - public Control createUi(Composite parent, Node context) { - if (CurrentUser.isAnonymous()) - setLabel(CmsMsg.login.lead()); - else { - setLabel(CurrentUser.getDisplayName()); - } - Label link = (Label) ((Composite) super.createUi(parent, context)).getChildren()[0]; - link.addMouseListener(new UserMenuLinkController(context)); - return link.getParent(); - } - - protected CmsLoginShell createUserMenu(Control source, Node context) { - return new UserMenu(source.getParent(), context); - } - - private class UserMenuLinkController implements MouseListener, DisposeListener { - private static final long serialVersionUID = 3634864186295639792L; - - private CmsLoginShell userMenu = null; - private long lastDisposeTS = 0l; - - private final Node context; - - public UserMenuLinkController(Node context) { - this.context = context; - } - - // - // MOUSE LISTENER - // - @Override - public void mouseDown(MouseEvent e) { - if (e.button == 1) { - Control source = (Control) e.getSource(); - if (userMenu == null) { - long durationSinceLastDispose = System.currentTimeMillis() - lastDisposeTS; - // avoid to reopen the menu, if one has clicked gain - if (durationSinceLastDispose > 200) { - userMenu = createUserMenu(source, context); - userMenu.getShell().addDisposeListener(this); - } - } - } - } - - @Override - public void mouseDoubleClick(MouseEvent e) { - } - - @Override - public void mouseUp(MouseEvent e) { - } - - @Override - public void widgetDisposed(DisposeEvent event) { - userMenu = null; - lastDisposeTS = System.currentTimeMillis(); - } - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/VerticalMenu.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/VerticalMenu.java deleted file mode 100644 index 7f846c9..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/VerticalMenu.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.argeo.cms.ui.util; - -import java.util.ArrayList; -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ui.CmsUiProvider; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -public class VerticalMenu implements CmsUiProvider { - private List items = new ArrayList(); - - @Override - public Control createUi(Composite parent, Node context) throws RepositoryException { - Composite part = new Composite(parent, SWT.NONE); - part.setLayoutData(new GridData(SWT.LEAD, SWT.TOP, false, false)); -// part.setData(RWT.CUSTOM_VARIANT, custom); - part.setLayout(CmsSwtUtils.noSpaceGridLayout()); - for (CmsUiProvider uiProvider : items) { - Control subPart = uiProvider.createUi(part, context); - subPart.setLayoutData(new GridData(SWT.LEAD, SWT.TOP, false, false)); - } - return part; - } - - public void add(CmsUiProvider uiProvider) { - items.add(uiProvider); - } - - public List getItems() { - return items; - } - - public void setItems(List items) { - this.items = items; - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/package-info.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/package-info.java deleted file mode 100644 index 566df88..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Argeo CMS UI utilities. */ -package org.argeo.cms.ui.util; \ No newline at end of file diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/AbstractPageViewer.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/AbstractPageViewer.java deleted file mode 100644 index e23846e..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/AbstractPageViewer.java +++ /dev/null @@ -1,351 +0,0 @@ -package org.argeo.cms.ui.viewers; - -import java.security.AccessControlContext; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.Observable; -import java.util.Observer; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.security.auth.Subject; - -import org.argeo.api.cms.CmsLog; -import org.argeo.api.cms.ux.CmsEditable; -import org.argeo.cms.swt.SwtEditablePart; -import org.argeo.cms.swt.widgets.ScrolledPage; -import org.argeo.jcr.JcrException; -import org.eclipse.jface.viewers.ContentViewer; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.StructuredSelection; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.FocusEvent; -import org.eclipse.swt.events.FocusListener; -import org.eclipse.swt.events.MouseAdapter; -import org.eclipse.swt.events.MouseListener; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Widget; -import org.xml.sax.SAXParseException; - -/** Base class for viewers related to a page */ -public abstract class AbstractPageViewer extends ContentViewer implements Observer { - private static final long serialVersionUID = 5438688173410341485L; - - private final static CmsLog log = CmsLog.getLog(AbstractPageViewer.class); - - private final boolean readOnly; - /** The basis for the layouts, typically a ScrolledPage. */ - private final Composite page; - private final CmsEditable cmsEditable; - - private MouseListener mouseListener; - private FocusListener focusListener; - - private SwtEditablePart edited; - private ISelection selection = StructuredSelection.EMPTY; - - private AccessControlContext accessControlContext; - - protected AbstractPageViewer(Section parent, int style, CmsEditable cmsEditable) { - // read only at UI level - readOnly = SWT.READ_ONLY == (style & SWT.READ_ONLY); - - this.cmsEditable = cmsEditable == null ? CmsEditable.NON_EDITABLE : cmsEditable; - if (this.cmsEditable instanceof Observable) - ((Observable) this.cmsEditable).addObserver(this); - - if (cmsEditable.canEdit()) { - mouseListener = createMouseListener(); - focusListener = createFocusListener(); - } - page = findPage(parent); - accessControlContext = AccessController.getContext(); - } - - /** - * Can be called to simplify the called to isModelInitialized() and initModel() - */ - protected void initModelIfNeeded(Node node) { - try { - if (!isModelInitialized(node)) - if (getCmsEditable().canEdit()) { - initModel(node); - node.getSession().save(); - } - } catch (RepositoryException e) { - throw new JcrException("Cannot initialize model", e); - } - } - - /** Called if user can edit and model is not initialized */ - protected Boolean isModelInitialized(Node node) throws RepositoryException { - return true; - } - - /** Called if user can edit and model is not initialized */ - protected void initModel(Node node) throws RepositoryException { - } - - /** Create (retrieve) the MouseListener to use. */ - protected MouseListener createMouseListener() { - return new MouseAdapter() { - private static final long serialVersionUID = 1L; - }; - } - - /** Create (retrieve) the FocusListener to use. */ - protected FocusListener createFocusListener() { - return new FocusListener() { - private static final long serialVersionUID = 1L; - - @Override - public void focusLost(FocusEvent event) { - } - - @Override - public void focusGained(FocusEvent event) { - } - }; - } - - protected Composite findPage(Composite composite) { - if (composite instanceof ScrolledPage) { - return (ScrolledPage) composite; - } else { - if (composite.getParent() == null) - return composite; - return findPage(composite.getParent()); - } - } - - public void layoutPage() { - if (page != null) - page.layout(true, true); - } - - protected void showControl(Control control) { - if (page != null && (page instanceof ScrolledPage)) - ((ScrolledPage) page).showControl(control); - } - - @Override - public void update(Observable o, Object arg) { - if (o == cmsEditable) - editingStateChanged(cmsEditable); - } - - /** To be overridden in order to provide the actual refresh */ - protected void refresh(Control control) throws RepositoryException { - } - - /** To be overridden.Save the edited part. */ - protected void save(SwtEditablePart part) throws RepositoryException { - } - - /** Prepare the edited part */ - protected void prepare(SwtEditablePart part, Object caretPosition) { - } - - /** Notified when the editing state changed. Does nothing, to be overridden */ - protected void editingStateChanged(CmsEditable cmsEditable) { - } - - @Override - public void refresh() { - // TODO check actual context in order to notice a discrepancy - Subject viewerSubject = getViewerSubject(); - Subject.doAs(viewerSubject, (PrivilegedAction) () -> { - try { - if (cmsEditable.canEdit() && !readOnly) - mouseListener = createMouseListener(); - else - mouseListener = null; - refresh(getControl()); - // layout(getControl()); - if (!getControl().isDisposed()) - layoutPage(); - } catch (RepositoryException e) { - throw new JcrException("Cannot refresh", e); - } - return null; - }); - } - - @Override - public void setSelection(ISelection selection, boolean reveal) { - this.selection = selection; - } - - protected void updateContent(SwtEditablePart part) throws RepositoryException { - } - - // LOW LEVEL EDITION - protected void edit(SwtEditablePart part, Object caretPosition) { - try { - if (edited == part) - return; - - if (edited != null && edited != part) { - SwtEditablePart previouslyEdited = edited; - try { - stopEditing(true); - } catch (Exception e) { - notifyEditionException(e); - edit(previouslyEdited, caretPosition); - return; - } - } - - part.startEditing(); - edited = part; - updateContent(part); - prepare(part, caretPosition); - edited.getControl().addFocusListener(new FocusListener() { - private static final long serialVersionUID = 6883521812717097017L; - - @Override - public void focusLost(FocusEvent event) { - stopEditing(true); - } - - @Override - public void focusGained(FocusEvent event) { - } - }); - - layout(part.getControl()); - showControl(part.getControl()); - } catch (RepositoryException e) { - throw new JcrException("Cannot edit " + part, e); - } - } - - protected void stopEditing(Boolean save) { - if (edited instanceof Widget && ((Widget) edited).isDisposed()) { - edited = null; - return; - } - - assert edited != null; - if (edited == null) { - if (log.isTraceEnabled()) - log.warn("Told to stop editing while not editing anything"); - return; - } - - try { - if (save) - save(edited); - - edited.stopEditing(); - SwtEditablePart editablePart = edited; - Control control = ((SwtEditablePart) edited).getControl(); - edited = null; - // TODO make edited state management more robust - updateContent(editablePart); - layout(control); - } catch (RepositoryException e) { - throw new JcrException("Cannot stop editing", e); - } finally { - edited = null; - } - } - - // METHODS AVAILABLE TO EXTENDING CLASSES - protected void saveEdit() { - if (edited != null) - stopEditing(true); - } - - protected void cancelEdit() { - if (edited != null) - stopEditing(false); - } - - /** Layout this controls from the related base page. */ - public void layout(Control... controls) { - page.layout(controls); - } - - /** - * Find the first {@link SwtEditablePart} in the parents hierarchy of this control - */ - protected SwtEditablePart findDataParent(Control parent) { - if (parent instanceof SwtEditablePart) { - return (SwtEditablePart) parent; - } - if (parent.getParent() != null) - return findDataParent(parent.getParent()); - else - throw new IllegalStateException("No data parent found"); - } - - // UTILITIES - /** Check whether the edited part is in a proper state */ - protected void checkEdited() { - if (edited == null || (edited instanceof Widget) && ((Widget) edited).isDisposed()) - throw new IllegalStateException("Edited should not be null or disposed at this stage"); - } - - /** Persist all changes. */ - protected void persistChanges(Session session) throws RepositoryException { - session.save(); - session.refresh(false); - // TODO notify that changes have been persisted - } - - /** Convenience method using a Node in order to save the underlying session. */ - protected void persistChanges(Node anyNode) throws RepositoryException { - persistChanges(anyNode.getSession()); - } - - /** Notify edition exception */ - protected void notifyEditionException(Throwable e) { - Throwable eToLog = e; - if (e instanceof IllegalArgumentException) - if (e.getCause() instanceof SAXParseException) - eToLog = e.getCause(); - log.error(eToLog.getMessage(), eToLog); -// if (log.isTraceEnabled()) -// log.trace("Full stack of " + eToLog.getMessage(), e); - // TODO Light error notification popup - } - - protected Subject getViewerSubject() { - Subject res = null; - if (accessControlContext != null) { - res = Subject.getSubject(accessControlContext); - } - if (res == null) - throw new IllegalStateException("No subject associated with this viewer"); - return res; - } - - // GETTERS / SETTERS - public boolean isReadOnly() { - return readOnly; - } - - protected SwtEditablePart getEdited() { - return edited; - } - - public MouseListener getMouseListener() { - return mouseListener; - } - - public FocusListener getFocusListener() { - return focusListener; - } - - public CmsEditable getCmsEditable() { - return cmsEditable; - } - - @Override - public ISelection getSelection() { - return selection; - } -} \ No newline at end of file diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/ItemPart.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/ItemPart.java deleted file mode 100644 index 4ca45d1..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/ItemPart.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.argeo.cms.ui.viewers; - -import javax.jcr.Item; -import javax.jcr.RepositoryException; - -/** An editable part related to a JCR Item */ -public interface ItemPart { - public Item getItem() throws RepositoryException; -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/JcrVersionCmsEditable.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/JcrVersionCmsEditable.java deleted file mode 100644 index 298fbde..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/JcrVersionCmsEditable.java +++ /dev/null @@ -1,94 +0,0 @@ -package org.argeo.cms.ui.viewers; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.nodetype.NodeType; -import javax.jcr.version.VersionManager; - -import org.argeo.api.cms.ux.CmsEditionEvent; -import org.argeo.cms.ux.AbstractCmsEditable; -import org.argeo.jcr.JcrException; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Event; -import org.eclipse.swt.widgets.Listener; - -/** Provides the CmsEditable semantic based on JCR versioning. */ -public class JcrVersionCmsEditable extends AbstractCmsEditable { - private final String nodePath;// cache - private final VersionManager versionManager; - private final Boolean canEdit; - - public JcrVersionCmsEditable(Node node) throws RepositoryException { - this.nodePath = node.getPath(); - if (node.getSession().hasPermission(node.getPath(), Session.ACTION_SET_PROPERTY)) { - // was Session.ACTION_ADD_NODE - canEdit = true; - if (!node.isNodeType(NodeType.MIX_VERSIONABLE)) { - node.addMixin(NodeType.MIX_VERSIONABLE); - node.getSession().save(); - } - versionManager = node.getSession().getWorkspace().getVersionManager(); - } else { - canEdit = false; - versionManager = null; - } - - // bind keys - if (canEdit) { - Display display = Display.getCurrent(); - display.setData(RWT.ACTIVE_KEYS, new String[] { "CTRL+RETURN", "CTRL+E" }); - display.addFilter(SWT.KeyDown, new Listener() { - private static final long serialVersionUID = -4378653870463187318L; - - public void handleEvent(Event e) { - boolean ctrlPressed = (e.stateMask & SWT.CTRL) != 0; - if (ctrlPressed && e.keyCode == '\r') - stopEditing(); - else if (ctrlPressed && e.keyCode == 'E') - stopEditing(); - } - }); - } - } - - @Override - public Boolean canEdit() { - return canEdit; - } - - public Boolean isEditing() { - try { - if (!canEdit()) - return false; - return versionManager.isCheckedOut(nodePath); - } catch (RepositoryException e) { - throw new JcrException("Cannot check whether " + nodePath + " is editing", e); - } - } - - @Override - public void startEditing() { - try { - versionManager.checkout(nodePath); -// setChanged(); - } catch (RepositoryException e1) { - throw new JcrException("Cannot publish " + nodePath, e1); - } - notifyListeners(new CmsEditionEvent(nodePath, CmsEditionEvent.START_EDITING, this)); - } - - @Override - public void stopEditing() { - try { - versionManager.checkin(nodePath); -// setChanged(); - } catch (RepositoryException e1) { - throw new JcrException("Cannot publish " + nodePath, e1); - } - notifyListeners(new CmsEditionEvent(nodePath, CmsEditionEvent.STOP_EDITING, this)); - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/NodePart.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/NodePart.java deleted file mode 100644 index b51d4fc..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/NodePart.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.argeo.cms.ui.viewers; - -import javax.jcr.Node; - -/** An editable part related to a node */ -public interface NodePart extends ItemPart { - public Node getNode(); -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/PropertyPart.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/PropertyPart.java deleted file mode 100644 index 793079e..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/PropertyPart.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.argeo.cms.ui.viewers; - -import javax.jcr.Property; - -/** An editable part related to a JCR Property */ -public interface PropertyPart extends ItemPart { - public Property getProperty(); -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/Section.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/Section.java deleted file mode 100644 index b27fa38..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/Section.java +++ /dev/null @@ -1,166 +0,0 @@ -package org.argeo.cms.ui.viewers; - -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.swt.SwtEditablePart; -import org.argeo.cms.ui.widgets.JcrComposite; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -/** A structured UI related to a JCR context. */ -public class Section extends JcrComposite { - private static final long serialVersionUID = -5933796173755739207L; - - private final Section parentSection; - private Composite sectionHeader; - private final Integer relativeDepth; - - public Section(Composite parent, int style, Node node) { - this(parent, findSection(parent), style, node); - } - - public Section(Section section, int style, Node node) { - this(section, section, style, node); - } - - protected Section(Composite parent, Section parentSection, int style, Node node) { - super(parent, style, node); - try { - this.parentSection = parentSection; - if (parentSection != null) { - relativeDepth = getNode().getDepth() - parentSection.getNode().getDepth(); - } else { - relativeDepth = 0; - } - setLayout(CmsSwtUtils.noSpaceGridLayout()); - } catch (RepositoryException e) { - throw new IllegalStateException("Cannot create section from " + node, e); - } - } - - public Map getSubSections() throws RepositoryException { - LinkedHashMap result = new LinkedHashMap(); - for (Control child : getChildren()) { - if (child instanceof Composite) { - collectDirectSubSections((Composite) child, result); - } - } - return Collections.unmodifiableMap(result); - } - - private void collectDirectSubSections(Composite composite, LinkedHashMap subSections) - throws RepositoryException { - if (composite == sectionHeader || composite instanceof SwtEditablePart) - return; - if (composite instanceof Section) { - Section section = (Section) composite; - subSections.put(section.getNodeId(), section); - return; - } - - for (Control child : composite.getChildren()) - if (child instanceof Composite) - collectDirectSubSections((Composite) child, subSections); - } - - public Composite createHeader() { - return createHeader(this); - } - - public Composite createHeader(Composite parent) { - if (sectionHeader != null) - sectionHeader.dispose(); - - sectionHeader = new Composite(parent, SWT.NONE); - sectionHeader.setLayoutData(CmsSwtUtils.fillWidth()); - sectionHeader.setLayout(CmsSwtUtils.noSpaceGridLayout()); - // sectionHeader.moveAbove(null); - // layout(); - return sectionHeader; - } - - public Composite getHeader() { - if (sectionHeader != null && sectionHeader.isDisposed()) - sectionHeader = null; - return sectionHeader; - } - - // SECTION PARTS - public SectionPart getSectionPart(String partId) { - for (Control child : getChildren()) { - if (child instanceof SectionPart) { - SectionPart sectionPart = (SectionPart) child; - if (sectionPart.getPartId().equals(partId)) - return sectionPart; - } - } - return null; - } - - public SectionPart nextSectionPart(SectionPart sectionPart) { - Control[] children = getChildren(); - for (int i = 0; i < children.length; i++) { - if (sectionPart == children[i]) { - for (int j = i + 1; j < children.length; j++) { - if (children[i + 1] instanceof SectionPart) { - return (SectionPart) children[i + 1]; - } - } - -// if (i + 1 < children.length) { -// Composite next = (Composite) children[i + 1]; -// return (SectionPart) next; -// } else { -// // next section -// } - } - } - return null; - } - - public SectionPart previousSectionPart(SectionPart sectionPart) { - Control[] children = getChildren(); - for (int i = 0; i < children.length; i++) { - if (sectionPart == children[i]) - if (i != 0) { - Composite previous = (Composite) children[i - 1]; - return (SectionPart) previous; - } else { - // previous section - } - } - return null; - } - - @Override - public String toString() { - if (parentSection == null) - return "Main section " + getNode(); - return "Section " + getNode(); - } - - public Section getParentSection() { - return parentSection; - } - - public Integer getRelativeDepth() { - return relativeDepth; - } - - /** Recursively finds the related section in the parents (can be itself) */ - public static Section findSection(Control control) { - if (control == null) - return null; - if (control instanceof Section) - return (Section) control; - else - return findSection(control.getParent()); - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/SectionPart.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/SectionPart.java deleted file mode 100644 index 4278c83..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/SectionPart.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.argeo.cms.ui.viewers; - -import org.argeo.cms.swt.SwtEditablePart; - -/** An editable part dynamically related to a Section */ -public interface SectionPart extends SwtEditablePart, NodePart { - public String getPartId(); - - public Section getSection(); -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/package-info.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/package-info.java deleted file mode 100644 index 2f07931..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Argeo CMS generic viewers, based on JFace. */ -package org.argeo.cms.ui.viewers; \ No newline at end of file diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/EditableImage.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/EditableImage.java deleted file mode 100644 index 95d9e8e..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/EditableImage.java +++ /dev/null @@ -1,112 +0,0 @@ -package org.argeo.cms.ui.widgets; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.api.cms.CmsLog; -import org.argeo.api.cms.ux.Cms2DSize; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ui.util.CmsUiUtils; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; - -/** A stylable and editable image. */ -public abstract class EditableImage extends StyledControl { - private static final long serialVersionUID = -5689145523114022890L; - private final static CmsLog log = CmsLog.getLog(EditableImage.class); - - private Cms2DSize preferredImageSize; - private Boolean loaded = false; - - public EditableImage(Composite parent, int swtStyle) { - super(parent, swtStyle); - } - - public EditableImage(Composite parent, int swtStyle, Cms2DSize preferredImageSize) { - super(parent, swtStyle); - this.preferredImageSize = preferredImageSize; - } - - public EditableImage(Composite parent, int style, Node node, boolean cacheImmediately, Cms2DSize preferredImageSize) - throws RepositoryException { - super(parent, style, node, cacheImmediately); - this.preferredImageSize = preferredImageSize; - } - - @Override - protected void setContainerLayoutData(Composite composite) { - // composite.setLayoutData(fillWidth()); - } - - @Override - protected void setControlLayoutData(Control control) { - // control.setLayoutData(fillWidth()); - } - - /** To be overriden. */ - protected String createImgTag() throws RepositoryException { - return CmsUiUtils - .noImg(preferredImageSize != null ? preferredImageSize : new Cms2DSize(getSize().x, getSize().y)); - } - - protected Label createLabel(Composite box, String style) { - Label lbl = new Label(box, getStyle()); - // lbl.setLayoutData(CmsUiUtils.fillWidth()); - CmsSwtUtils.markup(lbl); - CmsSwtUtils.style(lbl, style); - if (mouseListener != null) - lbl.addMouseListener(mouseListener); - load(lbl); - return lbl; - } - - /** To be overriden. */ - protected synchronized Boolean load(Control control) { - String imgTag; - try { - imgTag = createImgTag(); - } catch (Exception e) { - // throw new CmsException("Cannot retrieve image", e); - log.error("Cannot retrieve image", e); - imgTag = CmsUiUtils.noImg(preferredImageSize); - loaded = false; - } - - if (imgTag == null) { - loaded = false; - imgTag = CmsUiUtils.noImg(preferredImageSize); - } else - loaded = true; - if (control != null) { - ((Label) control).setText(imgTag); - control.setSize(preferredImageSize != null - ? new Point(preferredImageSize.getWidth(), preferredImageSize.getHeight()) - : getSize()); - } else { - loaded = false; - } - getParent().layout(); - return loaded; - } - - public void setPreferredSize(Cms2DSize size) { - this.preferredImageSize = size; - if (!loaded) { - load((Label) getControl()); - } - } - - protected Text createText(Composite box, String style) { - Text text = new Text(box, getStyle()); - CmsSwtUtils.style(text, style); - return text; - } - - public Cms2DSize getPreferredImageSize() { - return preferredImageSize; - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/EditableText.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/EditableText.java deleted file mode 100644 index e3499ac..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/EditableText.java +++ /dev/null @@ -1,145 +0,0 @@ -package org.argeo.cms.ui.widgets; - -import javax.jcr.Item; -import javax.jcr.RepositoryException; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Color; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; - -/** Editable text part displaying styled text. */ -public class EditableText extends StyledControl { - private static final long serialVersionUID = -6372283442330912755L; - - private boolean editable = true; - - private Color highlightColor; - private Composite highlight; - - private boolean useTextAsLabel = false; - - public EditableText(Composite parent, int style) { - super(parent, style); - editable = !(SWT.READ_ONLY == (style & SWT.READ_ONLY)); - highlightColor = parent.getDisplay().getSystemColor(SWT.COLOR_GRAY); - } - - public EditableText(Composite parent, int style, Item item) throws RepositoryException { - this(parent, style, item, false); - } - - public EditableText(Composite parent, int style, Item item, boolean cacheImmediately) throws RepositoryException { - super(parent, style, item, cacheImmediately); - editable = !(SWT.READ_ONLY == (style & SWT.READ_ONLY)); - highlightColor = parent.getDisplay().getSystemColor(SWT.COLOR_GRAY); - } - - @Override - protected Control createControl(Composite box, String style) { - if (isEditing() && getEditable()) { - return createText(box, style, true); - } else { - if (useTextAsLabel) { - return createTextLabel(box, style); - } else { - return createLabel(box, style); - } - } - } - - protected Label createLabel(Composite box, String style) { - Label lbl = new Label(box, getStyle() | SWT.WRAP); - lbl.setLayoutData(CmsSwtUtils.fillWidth()); - if (style != null) - CmsSwtUtils.style(lbl, style); - CmsSwtUtils.markup(lbl); - if (mouseListener != null) - lbl.addMouseListener(mouseListener); - return lbl; - } - - protected Text createTextLabel(Composite box, String style) { - Text lbl = new Text(box, getStyle() | SWT.MULTI); - lbl.setEditable(false); - lbl.setLayoutData(CmsSwtUtils.fillWidth()); - if (style != null) - CmsSwtUtils.style(lbl, style); - CmsSwtUtils.markup(lbl); - if (mouseListener != null) - lbl.addMouseListener(mouseListener); - return lbl; - } - - protected Text createText(Composite box, String style, boolean editable) { - highlight = new Composite(box, SWT.NONE); - highlight.setBackground(highlightColor); - GridData highlightGd = new GridData(SWT.FILL, SWT.FILL, false, false); - highlightGd.widthHint = 5; - highlightGd.heightHint = 3; - highlight.setLayoutData(highlightGd); - - final Text text = new Text(box, getStyle() | SWT.MULTI | SWT.WRAP); - text.setEditable(editable); - GridData textLayoutData = CmsSwtUtils.fillWidth(); - // textLayoutData.heightHint = preferredHeight; - text.setLayoutData(textLayoutData); - if (style != null) - CmsSwtUtils.style(text, style); - text.setFocus(); - return text; - } - - @Override - protected void clear(boolean deep) { - if (highlight != null) - highlight.dispose(); - super.clear(deep); - } - - public void setText(String text) { - Control child = getControl(); - if (child instanceof Label) - ((Label) child).setText(text); - else if (child instanceof Text) - ((Text) child).setText(text); - } - - public Text getAsText() { - return (Text) getControl(); - } - - public Label getAsLabel() { - return (Label) getControl(); - } - - public String getText() { - Control child = getControl(); - - if (child instanceof Label) - return ((Label) child).getText(); - else if (child instanceof Text) - return ((Text) child).getText(); - else - throw new IllegalStateException("Unsupported control " + child.getClass()); - } - - /** @deprecated Use {@link #isEditable()} instead. */ - @Deprecated - public boolean getEditable() { - return isEditable(); - } - - public boolean isEditable() { - return editable; - } - - public void setUseTextAsLabel(boolean useTextAsLabel) { - this.useTextAsLabel = useTextAsLabel; - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/Img.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/Img.java deleted file mode 100644 index 41063fa..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/Img.java +++ /dev/null @@ -1,155 +0,0 @@ -package org.argeo.cms.ui.widgets; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.api.cms.ux.Cms2DSize; -import org.argeo.api.cms.ux.CmsImageManager; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ui.internal.JcrFileUploadReceiver; -import org.argeo.cms.ui.viewers.NodePart; -import org.argeo.cms.ui.viewers.Section; -import org.argeo.cms.ui.viewers.SectionPart; -import org.argeo.jcr.Jcr; -import org.argeo.jcr.JcrException; -import org.eclipse.rap.fileupload.FileUploadHandler; -import org.eclipse.rap.fileupload.FileUploadListener; -import org.eclipse.rap.fileupload.FileUploadReceiver; -import org.eclipse.rap.rwt.service.ServerPushSession; -import org.eclipse.rap.rwt.widgets.FileUpload; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -/** An image within the Argeo Text framework */ -public class Img extends EditableImage implements SectionPart, NodePart { - private static final long serialVersionUID = 6233572783968188476L; - - private final Section section; - - private final CmsImageManager imageManager; - private FileUploadHandler currentUploadHandler = null; - private FileUploadListener fileUploadListener; - - public Img(Composite parent, int swtStyle, Node imgNode, Cms2DSize preferredImageSize) throws RepositoryException { - this(Section.findSection(parent), parent, swtStyle, imgNode, preferredImageSize, null); - setStyle(TextStyles.TEXT_IMAGE); - } - - public Img(Composite parent, int swtStyle, Node imgNode) throws RepositoryException { - this(Section.findSection(parent), parent, swtStyle, imgNode, null, null); - setStyle(TextStyles.TEXT_IMAGE); - } - - public Img(Composite parent, int swtStyle, Node imgNode, CmsImageManager imageManager) - throws RepositoryException { - this(Section.findSection(parent), parent, swtStyle, imgNode, null, imageManager); - setStyle(TextStyles.TEXT_IMAGE); - } - - Img(Section section, Composite parent, int swtStyle, Node imgNode, Cms2DSize preferredImageSize, - CmsImageManager imageManager) throws RepositoryException { - super(parent, swtStyle, imgNode, false, preferredImageSize); - this.section = section; - this.imageManager = imageManager != null ? imageManager - : (CmsImageManager) CmsSwtUtils.getCmsView(section).getImageManager(); - CmsSwtUtils.style(this, TextStyles.TEXT_IMG); - } - - @Override - protected Control createControl(Composite box, String style) { - if (isEditing()) { - try { - return createImageChooser(box, style); - } catch (RepositoryException e) { - throw new JcrException("Cannot create image chooser", e); - } - } else { - return createLabel(box, style); - } - } - - @Override - public synchronized void stopEditing() { - super.stopEditing(); - fileUploadListener = null; - } - - @Override - protected synchronized Boolean load(Control lbl) { - Node imgNode = getNode(); - boolean loaded = imageManager.load(imgNode, lbl, getPreferredImageSize()); - // getParent().layout(); - return loaded; - } - - protected Node getUploadFolder() { - return Jcr.getParent(getNode()); - } - - protected String getUploadName() { - Node node = getNode(); - return Jcr.getName(node) + '[' + Jcr.getIndex(node) + ']'; - } - - protected CmsImageManager getImageManager() { - return imageManager; - } - - protected Control createImageChooser(Composite box, String style) throws RepositoryException { - JcrFileUploadReceiver receiver = new JcrFileUploadReceiver(this, getUploadFolder(), getUploadName(), - imageManager); - if (currentUploadHandler != null) - currentUploadHandler.dispose(); - currentUploadHandler = prepareUpload(receiver); - final ServerPushSession pushSession = new ServerPushSession(); - final FileUpload fileUpload = new FileUpload(box, SWT.NONE); - CmsSwtUtils.style(fileUpload, style); - fileUpload.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = -9158471843941668562L; - - @Override - public void widgetSelected(SelectionEvent e) { - pushSession.start(); - fileUpload.submit(currentUploadHandler.getUploadUrl()); - } - }); - return fileUpload; - } - - protected FileUploadHandler prepareUpload(FileUploadReceiver receiver) { - final FileUploadHandler uploadHandler = new FileUploadHandler(receiver); - if (fileUploadListener != null) - uploadHandler.addUploadListener(fileUploadListener); - return uploadHandler; - } - - @Override - public Section getSection() { - return section; - } - - public void setFileUploadListener(FileUploadListener fileUploadListener) { - this.fileUploadListener = fileUploadListener; - if (currentUploadHandler != null) - currentUploadHandler.addUploadListener(fileUploadListener); - } - - @Override - public Node getItem() throws RepositoryException { - return getNode(); - } - - @Override - public String getPartId() { - return getNodeId(); - } - - @Override - public String toString() { - return "Img #" + getPartId(); - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/JcrComposite.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/JcrComposite.java deleted file mode 100644 index 6b54c0a..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/JcrComposite.java +++ /dev/null @@ -1,213 +0,0 @@ -package org.argeo.cms.ui.widgets; - -import javax.jcr.Item; -import javax.jcr.ItemNotFoundException; -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.RepositoryException; -import javax.jcr.Session; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.jcr.JcrException; -import org.eclipse.swt.widgets.Composite; - -/** A composite which can (optionally) manage a JCR Item. */ -public class JcrComposite extends Composite { - private static final long serialVersionUID = -1447009015451153367L; - - private Session session; - - private String nodeId; - private String property = null; - private Node cache; - - /** Regular composite constructor. No layout is set. */ - public JcrComposite(Composite parent, int style) { - super(parent, style); - session = null; - nodeId = null; - } - - public JcrComposite(Composite parent, int style, Item item) { - this(parent, style, item, false); - } - - public JcrComposite(Composite parent, int style, Item item, boolean cacheImmediately) { - super(parent, style); - if (item != null) - try { - this.session = item.getSession(); -// if (!cacheImmediately && (SWT.READ_ONLY == (style & SWT.READ_ONLY))) { -// // (useless?) optimization: we only save a pointer to the session, -// // not even a reference to the item -// this.nodeId = null; -// } else { - Node node; - Property property = null; - if (item instanceof Node) { - node = (Node) item; - } else {// Property - property = (Property) item; - if (property.isMultiple())// TODO manage property index - throw new UnsupportedOperationException("Multiple properties not supported yet."); - this.property = property.getName(); - node = property.getParent(); - } - this.nodeId = node.getIdentifier(); - if (cacheImmediately) - this.cache = node; -// } - setLayout(CmsSwtUtils.noSpaceGridLayout()); - } catch (RepositoryException e) { - throw new IllegalStateException("Cannot create composite from " + item, e); - } - } - - public synchronized Node getNode() { - try { - if (!itemIsNode()) - throw new IllegalStateException("Item is not a Node"); - return getNodeInternal(); - } catch (RepositoryException e) { - throw new JcrException("Cannot get node " + nodeId, e); - } - } - - private synchronized Node getNodeInternal() throws RepositoryException { - if (cache != null) - return cache; - else if (session != null) - if (nodeId != null) - return session.getNodeByIdentifier(nodeId); - else - return null; - else - return null; - } - - public synchronized String getPropertyName() { - try { - return getProperty().getName(); - } catch (RepositoryException e) { - throw new JcrException("Cannot get property name", e); - } - } - - public synchronized Node getPropertyNode() { - try { - return getProperty().getNode(); - } catch (RepositoryException e) { - throw new JcrException("Cannot get property name", e); - } - } - - public synchronized Property getProperty() { - try { - if (itemIsNode()) - throw new IllegalStateException("Item is not a Property"); - Node node = getNodeInternal(); - if (!node.hasProperty(property)) - throw new IllegalStateException("Property " + property + " is not set on " + node); - return node.getProperty(property); - } catch (RepositoryException e) { - throw new JcrException("Cannot get property " + property + " from node " + nodeId, e); - } - } - - public synchronized boolean itemIsNode() { - return property == null; - } - - public synchronized boolean itemExists() { - if (session == null) - return false; - try { - Node n = session.getNodeByIdentifier(nodeId); - if (!itemIsNode()) - return n.hasProperty(property); - else - return true; - } catch (ItemNotFoundException e) { - return false; - } catch (RepositoryException e) { - throw new JcrException("Cannot check whether node exists", e); - } - } - - /** Set/update the cache or change the node */ - public synchronized void setNode(Node node) { - if (!itemIsNode()) - throw new IllegalArgumentException("Cannot set a Node on a Property"); - - if (node == null) {// clear cache - this.cache = null; - return; - } - - try { -// if (session != null || session != node.getSession())// check session -// throw new IllegalArgumentException("Uncompatible session"); -// if (session == null) - session = node.getSession(); - if (nodeId == null || !nodeId.equals(node.getIdentifier())) { - nodeId = node.getIdentifier(); - cache = node; - itemUpdated(); - } else { - cache = node;// set/update cache - } - } catch (RepositoryException e) { - throw new IllegalStateException(e); - } - } - - /** Set/update the cache or change the property */ - public synchronized void setProperty(Property prop) { - if (itemIsNode()) - throw new IllegalArgumentException("Cannot set a Property on a Node"); - - if (prop == null) {// clear cache - this.cache = null; - return; - } - - try { - if (session == null || session != prop.getSession())// check session - throw new IllegalArgumentException("Uncompatible session"); - - Node node = prop.getNode(); - if (nodeId == null || !nodeId.equals(node.getIdentifier()) || !property.equals(prop.getName())) { - nodeId = node.getIdentifier(); - property = prop.getName(); - cache = node; - itemUpdated(); - } else { - cache = node;// set/update cache - } - } catch (RepositoryException e) { - throw new IllegalStateException(e); - } - } - - public synchronized String getNodeId() { - return nodeId; - } - - /** Change the node, does nothing if same. */ - public synchronized void setNodeId(String nodeId) throws RepositoryException { - if (this.nodeId != null && this.nodeId.equals(nodeId)) - return; - this.nodeId = nodeId; - if (cache != null) - cache = session.getNodeByIdentifier(this.nodeId); - itemUpdated(); - } - - protected synchronized void itemUpdated() { - layout(); - } - -// public Session getSession() { -// return session; -// } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/StyledControl.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/StyledControl.java deleted file mode 100644 index e3a5cb4..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/StyledControl.java +++ /dev/null @@ -1,153 +0,0 @@ -package org.argeo.cms.ui.widgets; - -import javax.jcr.Item; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ui.CmsUiConstants; -import org.argeo.eclipse.ui.specific.EclipseUiSpecificUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.FocusListener; -import org.eclipse.swt.events.MouseListener; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -/** Editable text part displaying styled text. */ -public abstract class StyledControl extends JcrComposite implements CmsUiConstants { - private static final long serialVersionUID = -6372283442330912755L; - private Control control; - - private Composite container; - private Composite box; - - protected MouseListener mouseListener; - protected FocusListener focusListener; - - private Boolean editing = Boolean.FALSE; - - private Composite ancestorToLayout; - - public StyledControl(Composite parent, int swtStyle) { - super(parent, swtStyle); - setLayout(CmsSwtUtils.noSpaceGridLayout()); - } - - public StyledControl(Composite parent, int style, Item item) { - super(parent, style, item); - } - - public StyledControl(Composite parent, int style, Item item, boolean cacheImmediately) { - super(parent, style, item, cacheImmediately); - } - - protected abstract Control createControl(Composite box, String style); - - protected Composite createBox() { - Composite box = new Composite(container, SWT.INHERIT_DEFAULT); - setContainerLayoutData(box); - box.setLayout(CmsSwtUtils.noSpaceGridLayout(3)); - return box; - } - - protected Composite createContainer() { - Composite container = new Composite(this, SWT.INHERIT_DEFAULT); - setContainerLayoutData(container); - container.setLayout(CmsSwtUtils.noSpaceGridLayout()); - return container; - } - - public Control getControl() { - return control; - } - - protected synchronized Boolean isEditing() { - return editing; - } - - public synchronized void startEditing() { - assert !isEditing(); - editing = true; - // int height = control.getSize().y; - String style = (String) EclipseUiSpecificUtils.getStyleData(control); - clear(false); - refreshControl(style); - - // add the focus listener to the newly created edition control - if (focusListener != null) - control.addFocusListener(focusListener); - } - - public synchronized void stopEditing() { - assert isEditing(); - editing = false; - String style = (String) EclipseUiSpecificUtils.getStyleData(control); - clear(false); - refreshControl(style); - } - - protected void refreshControl(String style) { - control = createControl(box, style); - setControlLayoutData(control); - if (ancestorToLayout != null) - ancestorToLayout.layout(true, true); - else - getParent().layout(true, true); - } - - public void setStyle(String style) { - Object currentStyle = null; - if (control != null) - currentStyle = EclipseUiSpecificUtils.getStyleData(control); - if (currentStyle != null && currentStyle.equals(style)) - return; - - clear(true); - refreshControl(style); - - if (style != null) { - CmsSwtUtils.style(box, style + "_box"); - CmsSwtUtils.style(container, style + "_container"); - } - } - - /** To be overridden */ - protected void setControlLayoutData(Control control) { - control.setLayoutData(CmsSwtUtils.fillWidth()); - } - - /** To be overridden */ - protected void setContainerLayoutData(Composite composite) { - composite.setLayoutData(CmsSwtUtils.fillWidth()); - } - - protected void clear(boolean deep) { - if (deep) { - for (Control control : getChildren()) - control.dispose(); - container = createContainer(); - box = createBox(); - } else { - control.dispose(); - } - } - - public void setMouseListener(MouseListener mouseListener) { - if (this.mouseListener != null && control != null) - control.removeMouseListener(this.mouseListener); - this.mouseListener = mouseListener; - if (control != null && this.mouseListener != null) - control.addMouseListener(mouseListener); - } - - public void setFocusListener(FocusListener focusListener) { - if (this.focusListener != null && control != null) - control.removeFocusListener(this.focusListener); - this.focusListener = focusListener; - if (control != null && this.focusListener != null) - control.addFocusListener(focusListener); - } - - public void setAncestorToLayout(Composite ancestorToLayout) { - this.ancestorToLayout = ancestorToLayout; - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/TextStyles.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/TextStyles.java deleted file mode 100644 index e461ed0..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/TextStyles.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.argeo.cms.ui.widgets; - -/** Styles references in the CSS. */ -public interface TextStyles { - /** The whole page area */ - public final static String TEXT_AREA = "text_area"; - /** Area providing controls for editing text */ - public final static String TEXT_EDITOR_HEADER = "text_editor_header"; - /** The styled composite for editing the text */ - public final static String TEXT_STYLED_COMPOSITE = "text_styled_composite"; - /** A section */ - public final static String TEXT_SECTION = "text_section"; - /** A paragraph */ - public final static String TEXT_PARAGRAPH = "text_paragraph"; - /** An image */ - public final static String TEXT_IMG = "text_img"; - /** The dialog to edit styled paragraph */ - public final static String TEXT_STYLED_TOOLS_DIALOG = "text_styled_tools_dialog"; - - /* - * DEFAULT TEXT STYLES - */ - /** Default style for text body */ - public final static String TEXT_DEFAULT = "text_default"; - /** Fixed-width, typically code */ - public final static String TEXT_PRE = "text_pre"; - /** Quote */ - public final static String TEXT_QUOTE = "text_quote"; - /** Title */ - public final static String TEXT_TITLE = "text_title"; - /** Header (to be dynamically completed with the depth, e.g. text_h1) */ - public final static String TEXT_H = "text_h"; - - /** Default style for images */ - public final static String TEXT_IMAGE = "text_image"; - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/package-info.java b/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/package-info.java deleted file mode 100644 index 514f753..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Argeo CMS generic widgets, based on SWT. */ -package org.argeo.cms.ui.widgets; \ No newline at end of file diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/AbstractNodeContentProvider.java b/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/AbstractNodeContentProvider.java deleted file mode 100644 index fdafa98..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/AbstractNodeContentProvider.java +++ /dev/null @@ -1,138 +0,0 @@ -package org.argeo.eclipse.ui.jcr; - -import java.util.ArrayList; -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.NodeIterator; -import javax.jcr.RepositoryException; -import javax.jcr.Session; - -import org.argeo.api.cms.CmsLog; -import org.argeo.eclipse.ui.AbstractTreeContentProvider; -import org.argeo.eclipse.ui.EclipseUiException; - -/** Canonical implementation of tree content provider manipulating JCR nodes. */ -public abstract class AbstractNodeContentProvider extends - AbstractTreeContentProvider { - private static final long serialVersionUID = -4905836490027272569L; - - private final static CmsLog log = CmsLog - .getLog(AbstractNodeContentProvider.class); - - private Session session; - - public AbstractNodeContentProvider(Session session) { - this.session = session; - } - - /** - * Whether this path is a base path (and thus has no parent). By default it - * returns true if path is '/' (root node) - */ - protected Boolean isBasePath(String path) { - // root node - return path.equals("/"); - } - - @Override - public Object[] getChildren(Object element) { - Object[] children; - if (element instanceof Node) { - try { - Node node = (Node) element; - children = getChildren(node); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot get children of " + element, e); - } - } else if (element instanceof WrappedNode) { - WrappedNode wrappedNode = (WrappedNode) element; - try { - children = getChildren(wrappedNode.getNode()); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot get children of " - + wrappedNode, e); - } - } else if (element instanceof NodesWrapper) { - NodesWrapper node = (NodesWrapper) element; - children = node.getChildren(); - } else { - children = super.getChildren(element); - } - - children = sort(element, children); - return children; - } - - /** Do not sort by default. To be overidden to provide custom sort. */ - protected Object[] sort(Object parent, Object[] children) { - return children; - } - - /** - * To be overridden in order to filter out some nodes. Does nothing by - * default. The provided list is a temporary one and can thus be modified - * directly . (e.g. via an iterator) - */ - protected List filterChildren(List children) - throws RepositoryException { - return children; - } - - protected Object[] getChildren(Node node) throws RepositoryException { - List nodes = new ArrayList(); - for (NodeIterator nit = node.getNodes(); nit.hasNext();) - nodes.add(nit.nextNode()); - nodes = filterChildren(nodes); - return nodes.toArray(); - } - - @Override - public Object getParent(Object element) { - if (element instanceof Node) { - Node node = (Node) element; - try { - String path = node.getPath(); - if (isBasePath(path)) - return null; - else - return node.getParent(); - } catch (RepositoryException e) { - log.warn("Cannot get parent of " + element + ": " + e); - return null; - } - } else if (element instanceof WrappedNode) { - WrappedNode wrappedNode = (WrappedNode) element; - return wrappedNode.getParent(); - } else if (element instanceof NodesWrapper) { - NodesWrapper nodesWrapper = (NodesWrapper) element; - return this.getParent(nodesWrapper.getNode()); - } - return super.getParent(element); - } - - @Override - public boolean hasChildren(Object element) { - try { - if (element instanceof Node) { - Node node = (Node) element; - return node.hasNodes(); - } else if (element instanceof WrappedNode) { - WrappedNode wrappedNode = (WrappedNode) element; - return wrappedNode.getNode().hasNodes(); - } else if (element instanceof NodesWrapper) { - NodesWrapper nodesWrapper = (NodesWrapper) element; - return nodesWrapper.hasChildren(); - } - - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot check whether " + element - + " has children", e); - } - return super.hasChildren(element); - } - - public Session getSession() { - return session; - } -} \ No newline at end of file diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/AsyncUiEventListener.java b/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/AsyncUiEventListener.java deleted file mode 100644 index b880a63..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/AsyncUiEventListener.java +++ /dev/null @@ -1,83 +0,0 @@ -package org.argeo.eclipse.ui.jcr; - -import java.util.ArrayList; -import java.util.List; - -import javax.jcr.RepositoryException; -import javax.jcr.observation.Event; -import javax.jcr.observation.EventIterator; -import javax.jcr.observation.EventListener; - -import org.argeo.api.cms.CmsLog; -import org.argeo.eclipse.ui.EclipseUiException; -import org.eclipse.swt.widgets.Display; - -/** - * {@link EventListener} which simplifies running actions within the UI thread. - */ -public abstract class AsyncUiEventListener implements EventListener { - // private final static Log logSuper = LogFactory - // .getLog(AsyncUiEventListener.class); - private final CmsLog logThis = CmsLog.getLog(getClass()); - - private final Display display; - - public AsyncUiEventListener(Display display) { - super(); - this.display = display; - } - - /** Called asynchronously in the UI thread. */ - protected abstract void onEventInUiThread(List events) throws RepositoryException; - - /** - * Whether these events should be processed in the UI or skipped with no UI - * job created. - */ - protected Boolean willProcessInUiThread(List events) throws RepositoryException { - return true; - } - - protected CmsLog getLog() { - return logThis; - } - - public final void onEvent(final EventIterator eventIterator) { - final List events = new ArrayList(); - while (eventIterator.hasNext()) - events.add(eventIterator.nextEvent()); - - if (logThis.isTraceEnabled()) - logThis.trace("Received " + events.size() + " events"); - - try { - if (!willProcessInUiThread(events)) - return; - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot test skip events " + events, e); - } - - // Job job = new Job("JCR Events") { - // protected IStatus run(IProgressMonitor monitor) { - // if (display.isDisposed()) { - // logSuper.warn("Display is disposed cannot update UI"); - // return Status.CANCEL_STATUS; - // } - - if (!display.isDisposed()) - display.asyncExec(new Runnable() { - public void run() { - try { - onEventInUiThread(events); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot process events " + events, e); - } - } - }); - - // return Status.OK_STATUS; - // } - // }; - // job.schedule(); - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/DefaultNodeLabelProvider.java b/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/DefaultNodeLabelProvider.java deleted file mode 100644 index 22ffeaf..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/DefaultNodeLabelProvider.java +++ /dev/null @@ -1,82 +0,0 @@ -package org.argeo.eclipse.ui.jcr; - -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.RepositoryException; -import javax.jcr.nodetype.NodeType; - -import org.argeo.eclipse.ui.EclipseUiException; -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.swt.graphics.Image; - -/** - * Default label provider to manage node and corresponding UI objects. It - * provides reasonable overwrite-able default for known JCR types. - */ -public class DefaultNodeLabelProvider extends ColumnLabelProvider { - private static final long serialVersionUID = 1216182332792151235L; - - public String getText(Object element) { - try { - if (element instanceof Node) { - return getText((Node) element); - } else if (element instanceof WrappedNode) { - return getText(((WrappedNode) element).getNode()); - } else if (element instanceof NodesWrapper) { - return getText(((NodesWrapper) element).getNode()); - } - return super.getText(element); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot get text for of " + element, e); - } - } - - protected String getText(Node node) throws RepositoryException { - if (node.isNodeType(NodeType.MIX_TITLE) - && node.hasProperty(Property.JCR_TITLE)) - return node.getProperty(Property.JCR_TITLE).getString(); - else - return node.getName(); - } - - @Override - public Image getImage(Object element) { - try { - if (element instanceof Node) { - return getImage((Node) element); - } else if (element instanceof WrappedNode) { - return getImage(((WrappedNode) element).getNode()); - } else if (element instanceof NodesWrapper) { - return getImage(((NodesWrapper) element).getNode()); - } - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot retrieve image for " + element, e); - } - return super.getImage(element); - } - - protected Image getImage(Node node) throws RepositoryException { - // FIXME who uses that? - return null; - } - - @Override - public String getToolTipText(Object element) { - try { - if (element instanceof Node) { - return getToolTipText((Node) element); - } else if (element instanceof WrappedNode) { - return getToolTipText(((WrappedNode) element).getNode()); - } else if (element instanceof NodesWrapper) { - return getToolTipText(((NodesWrapper) element).getNode()); - } - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot get tooltip for " + element, e); - } - return super.getToolTipText(element); - } - - protected String getToolTipText(Node node) throws RepositoryException { - return null; - } -} \ No newline at end of file diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/JcrUiUtils.java b/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/JcrUiUtils.java deleted file mode 100644 index 420154b..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/JcrUiUtils.java +++ /dev/null @@ -1,149 +0,0 @@ -package org.argeo.eclipse.ui.jcr; - -import java.util.Calendar; - -import javax.jcr.Node; -import javax.jcr.PropertyType; -import javax.jcr.RepositoryException; - -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.eclipse.ui.jcr.lists.NodeViewerComparator; -import org.argeo.eclipse.ui.jcr.lists.RowViewerComparator; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.widgets.Table; - -/** Utility methods to simplify UI development using SWT (or RWT), jface and JCR. */ -public class JcrUiUtils { - - /** - * Centralizes management of updating property value. Among other to avoid - * infinite loop when the new value is the same as the ones that is already - * stored in JCR. - * - * @return true if the value as changed - */ - public static boolean setJcrProperty(Node node, String propName, - int propertyType, Object value) { - try { - switch (propertyType) { - case PropertyType.STRING: - if ("".equals((String) value) - && (!node.hasProperty(propName) || node - .hasProperty(propName) - && "".equals(node.getProperty(propName) - .getString()))) - // workaround the fact that the Text widget value cannot be - // set to null - return false; - else if (node.hasProperty(propName) - && node.getProperty(propName).getString() - .equals((String) value)) - // nothing changed yet - return false; - else { - node.setProperty(propName, (String) value); - return true; - } - case PropertyType.BOOLEAN: - if (node.hasProperty(propName) - && node.getProperty(propName).getBoolean() == (Boolean) value) - // nothing changed yet - return false; - else { - node.setProperty(propName, (Boolean) value); - return true; - } - case PropertyType.DATE: - if (node.hasProperty(propName) - && node.getProperty(propName).getDate() - .equals((Calendar) value)) - // nothing changed yet - return false; - else { - node.setProperty(propName, (Calendar) value); - return true; - } - case PropertyType.LONG: - Long lgValue = (Long) value; - - if (lgValue == null) - lgValue = 0L; - - if (node.hasProperty(propName) - && node.getProperty(propName).getLong() == lgValue) - // nothing changed yet - return false; - else { - node.setProperty(propName, lgValue); - return true; - } - - default: - throw new EclipseUiException("Unimplemented property save"); - } - } catch (RepositoryException re) { - throw new EclipseUiException("Unexpected error while setting property", - re); - } - } - - /** - * Creates a new selection adapter in order to provide sorting abitily on a - * SWT Table that display a row list - **/ - public static SelectionAdapter getRowSelectionAdapter(final int index, - final int propertyType, final String selectorName, - final String propertyName, final RowViewerComparator comparator, - final TableViewer viewer) { - SelectionAdapter selectionAdapter = new SelectionAdapter() { - private static final long serialVersionUID = -5738918304901437720L; - - @Override - public void widgetSelected(SelectionEvent e) { - Table table = viewer.getTable(); - comparator.setColumn(propertyType, selectorName, propertyName); - int dir = table.getSortDirection(); - if (table.getSortColumn() == table.getColumn(index)) { - dir = dir == SWT.UP ? SWT.DOWN : SWT.UP; - } else { - dir = SWT.DOWN; - } - table.setSortDirection(dir); - table.setSortColumn(table.getColumn(index)); - viewer.refresh(); - } - }; - return selectionAdapter; - } - - /** - * Creates a new selection adapter in order to provide sorting abitily on a - * swt table that display a row list - **/ - public static SelectionAdapter getNodeSelectionAdapter(final int index, - final int propertyType, final String propertyName, - final NodeViewerComparator comparator, final TableViewer viewer) { - SelectionAdapter selectionAdapter = new SelectionAdapter() { - private static final long serialVersionUID = -1683220869195484625L; - - @Override - public void widgetSelected(SelectionEvent e) { - Table table = viewer.getTable(); - comparator.setColumn(propertyType, propertyName); - int dir = table.getSortDirection(); - if (table.getSortColumn() == table.getColumn(index)) { - dir = dir == SWT.UP ? SWT.DOWN : SWT.UP; - } else { - dir = SWT.DOWN; - } - table.setSortDirection(dir); - table.setSortColumn(table.getColumn(index)); - viewer.refresh(); - } - }; - return selectionAdapter; - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodeColumnLabelProvider.java b/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodeColumnLabelProvider.java deleted file mode 100644 index 7e12bec..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodeColumnLabelProvider.java +++ /dev/null @@ -1,123 +0,0 @@ -package org.argeo.eclipse.ui.jcr; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.swt.graphics.Color; -import org.eclipse.swt.graphics.Font; -import org.eclipse.swt.graphics.Image; - -/** Simplifies writing JCR-based column label provider. */ -public class NodeColumnLabelProvider extends ColumnLabelProvider { - private static final long serialVersionUID = -6586692836928505358L; - - protected String getNodeText(Node node) throws RepositoryException { - return super.getText(node); - } - - protected String getNodeToolTipText(Node node) throws RepositoryException { - return super.getToolTipText(node); - } - - protected Image getNodeImage(Node node) throws RepositoryException { - return super.getImage(node); - } - - protected Font getNodeFont(Node node) throws RepositoryException { - return super.getFont(node); - } - - public Color getNodeBackground(Node node) throws RepositoryException { - return super.getBackground(node); - } - - public Color getNodeForeground(Node node) throws RepositoryException { - return super.getForeground(node); - } - - @Override - public String getText(Object element) { - try { - if (element instanceof Node) - return getNodeText((Node) element); - else if (element instanceof NodeElement) - return getNodeText(((NodeElement) element).getNode()); - else - throw new IllegalArgumentException("Unsupported element type " + element.getClass()); - } catch (RepositoryException e) { - throw new IllegalStateException("Repository exception when accessing " + element, e); - } - } - - @Override - public Image getImage(Object element) { - try { - if (element instanceof Node) - return getNodeImage((Node) element); - else if (element instanceof NodeElement) - return getNodeImage(((NodeElement) element).getNode()); - else - throw new IllegalArgumentException("Unsupported element type " + element.getClass()); - } catch (RepositoryException e) { - throw new IllegalStateException("Repository exception when accessing " + element, e); - } - } - - @Override - public String getToolTipText(Object element) { - try { - if (element instanceof Node) - return getNodeToolTipText((Node) element); - else if (element instanceof NodeElement) - return getNodeToolTipText(((NodeElement) element).getNode()); - else - throw new IllegalArgumentException("Unsupported element type " + element.getClass()); - } catch (RepositoryException e) { - throw new IllegalStateException("Repository exception when accessing " + element, e); - } - } - - @Override - public Font getFont(Object element) { - try { - if (element instanceof Node) - return getNodeFont((Node) element); - else if (element instanceof NodeElement) - return getNodeFont(((NodeElement) element).getNode()); - else - throw new IllegalArgumentException("Unsupported element type " + element.getClass()); - } catch (RepositoryException e) { - throw new IllegalStateException("Repository exception when accessing " + element, e); - } - } - - @Override - public Color getBackground(Object element) { - try { - if (element instanceof Node) - return getNodeBackground((Node) element); - else if (element instanceof NodeElement) - return getNodeBackground(((NodeElement) element).getNode()); - else - throw new IllegalArgumentException("Unsupported element type " + element.getClass()); - } catch (RepositoryException e) { - throw new IllegalStateException("Repository exception when accessing " + element, e); - } - } - - @Override - public Color getForeground(Object element) { - try { - if (element instanceof Node) - return getNodeForeground((Node) element); - else if (element instanceof NodeElement) - return getNodeForeground(((NodeElement) element).getNode()); - else - throw new IllegalArgumentException("Unsupported element type " + element.getClass()); - } catch (RepositoryException e) { - throw new IllegalStateException("Repository exception when accessing " + element, e); - } - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodeElement.java b/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodeElement.java deleted file mode 100644 index 787c92e..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodeElement.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.argeo.eclipse.ui.jcr; - -import javax.jcr.Node; - -/** An element which is related to a JCR {@link Node}. */ -public interface NodeElement { - Node getNode(); -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodeElementComparer.java b/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodeElementComparer.java deleted file mode 100644 index 2f3d64d..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodeElementComparer.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.argeo.eclipse.ui.jcr; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.eclipse.ui.EclipseUiException; -import org.eclipse.jface.viewers.IElementComparer; - -/** Element comparer for JCR node, to be used in JFace viewers. */ -public class NodeElementComparer implements IElementComparer { - - public boolean equals(Object a, Object b) { - try { - if ((a instanceof Node) && (b instanceof Node)) { - Node nodeA = (Node) a; - Node nodeB = (Node) b; - return nodeA.getIdentifier().equals(nodeB.getIdentifier()); - } else { - return a.equals(b); - } - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot compare nodes", e); - } - } - - public int hashCode(Object element) { - try { - if (element instanceof Node) - return ((Node) element).getIdentifier().hashCode(); - return element.hashCode(); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot get hash code", e); - } - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodesWrapper.java b/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodesWrapper.java deleted file mode 100644 index 2f808a5..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodesWrapper.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.argeo.eclipse.ui.jcr; - -import java.util.ArrayList; -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.NodeIterator; -import javax.jcr.RepositoryException; - -import org.argeo.eclipse.ui.EclipseUiException; - -/** - * Element of tree which is based on a node, but whose children are not - * necessarily this node children. - */ -public class NodesWrapper { - private final Node node; - - public NodesWrapper(Node node) { - super(); - this.node = node; - } - - protected NodeIterator getNodeIterator() throws RepositoryException { - return node.getNodes(); - } - - protected List getWrappedNodes() throws RepositoryException { - List nodes = new ArrayList(); - for (NodeIterator nit = getNodeIterator(); nit.hasNext();) - nodes.add(new WrappedNode(this, nit.nextNode())); - return nodes; - } - - public Object[] getChildren() { - try { - return getWrappedNodes().toArray(); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot get wrapped children", e); - } - } - - /** - * @return true by default because we don't want to compute the wrapped - * nodes twice - */ - public Boolean hasChildren() { - return true; - } - - public Node getNode() { - return node; - } - - @Override - public int hashCode() { - return node.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof NodesWrapper) - return node.equals(((NodesWrapper) obj).getNode()); - else - return false; - } - - public String toString() { - return "nodes wrapper based on " + node; - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/QueryTableContentProvider.java b/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/QueryTableContentProvider.java deleted file mode 100644 index 934fa67..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/QueryTableContentProvider.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.argeo.eclipse.ui.jcr; - -import javax.jcr.NodeIterator; -import javax.jcr.RepositoryException; -import javax.jcr.query.Query; - -import org.argeo.jcr.JcrException; -import org.argeo.jcr.JcrUtils; -import org.eclipse.jface.viewers.IStructuredContentProvider; -import org.eclipse.jface.viewers.Viewer; - -/** Content provider based on a JCR {@link Query}. */ -public class QueryTableContentProvider implements IStructuredContentProvider { - private static final long serialVersionUID = 760371460907204722L; - - @Override - public void dispose() { - } - - @Override - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - } - - @Override - public Object[] getElements(Object inputElement) { - Query query = (Query) inputElement; - try { - NodeIterator nit = query.execute().getNodes(); - return JcrUtils.nodeIteratorToList(nit).toArray(); - } catch (RepositoryException e) { - throw new JcrException(e); - } - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/RowColumnLabelProvider.java b/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/RowColumnLabelProvider.java deleted file mode 100644 index 70c71ef..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/RowColumnLabelProvider.java +++ /dev/null @@ -1,111 +0,0 @@ -package org.argeo.eclipse.ui.jcr; - -import javax.jcr.RepositoryException; -import javax.jcr.query.Row; - -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.swt.graphics.Color; -import org.eclipse.swt.graphics.Font; -import org.eclipse.swt.graphics.Image; - -/** Simplifies writing JCR-based column label provider. */ -public class RowColumnLabelProvider extends ColumnLabelProvider { - private static final long serialVersionUID = -6586692836928505358L; - - protected String getRowText(Row row) throws RepositoryException { - return super.getText(row); - } - - protected String getRowToolTipText(Row row) throws RepositoryException { - return super.getToolTipText(row); - } - - protected Image getRowImage(Row row) throws RepositoryException { - return super.getImage(row); - } - - protected Font getRowFont(Row row) throws RepositoryException { - return super.getFont(row); - } - - public Color getRowBackground(Row row) throws RepositoryException { - return super.getBackground(row); - } - - public Color getRowForeground(Row row) throws RepositoryException { - return super.getForeground(row); - } - - @Override - public String getText(Object element) { - try { - if (element instanceof Row) - return getRowText((Row) element); - else - throw new IllegalArgumentException("Unsupported element type " + element.getClass()); - } catch (RepositoryException e) { - throw new IllegalStateException("Repository exception when accessing " + element, e); - } - } - - @Override - public Image getImage(Object element) { - try { - if (element instanceof Row) - return getRowImage((Row) element); - else - throw new IllegalArgumentException("Unsupported element type " + element.getClass()); - } catch (RepositoryException e) { - throw new IllegalStateException("Repository exception when accessing " + element, e); - } - } - - @Override - public String getToolTipText(Object element) { - try { - if (element instanceof Row) - return getRowToolTipText((Row) element); - else - throw new IllegalArgumentException("Unsupported element type " + element.getClass()); - } catch (RepositoryException e) { - throw new IllegalStateException("Repository exception when accessing " + element, e); - } - } - - @Override - public Font getFont(Object element) { - try { - if (element instanceof Row) - return getRowFont((Row) element); - else - throw new IllegalArgumentException("Unsupported element type " + element.getClass()); - } catch (RepositoryException e) { - throw new IllegalStateException("Repository exception when accessing " + element, e); - } - } - - @Override - public Color getBackground(Object element) { - try { - if (element instanceof Row) - return getRowBackground((Row) element); - else - throw new IllegalArgumentException("Unsupported element type " + element.getClass()); - } catch (RepositoryException e) { - throw new IllegalStateException("Repository exception when accessing " + element, e); - } - } - - @Override - public Color getForeground(Object element) { - try { - if (element instanceof Row) - return getRowForeground((Row) element); - else - throw new IllegalArgumentException("Unsupported element type " + element.getClass()); - } catch (RepositoryException e) { - throw new IllegalStateException("Repository exception when accessing " + element, e); - } - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/SimpleNodeContentProvider.java b/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/SimpleNodeContentProvider.java deleted file mode 100644 index cb235d7..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/SimpleNodeContentProvider.java +++ /dev/null @@ -1,59 +0,0 @@ -package org.argeo.eclipse.ui.jcr; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.Session; - -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.jcr.JcrUtils; - -/** Simple JCR node content provider taking a list of String as base path. */ -public class SimpleNodeContentProvider extends AbstractNodeContentProvider { - private static final long serialVersionUID = -8245193308831384269L; - private final List basePaths; - private Boolean mkdirs = false; - - public SimpleNodeContentProvider(Session session, String... basePaths) { - this(session, Arrays.asList(basePaths)); - } - - public SimpleNodeContentProvider(Session session, List basePaths) { - super(session); - this.basePaths = basePaths; - } - - @Override - protected Boolean isBasePath(String path) { - if (basePaths.contains(path)) - return true; - return super.isBasePath(path); - } - - public Object[] getElements(Object inputElement) { - try { - List baseNodes = new ArrayList(); - for (String basePath : basePaths) - if (mkdirs && !getSession().itemExists(basePath)) - baseNodes.add(JcrUtils.mkdirs(getSession(), basePath)); - else - baseNodes.add(getSession().getNode(basePath)); - return baseNodes.toArray(); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot get base nodes for " + basePaths, - e); - } - } - - public List getBasePaths() { - return basePaths; - } - - public void setMkdirs(Boolean mkdirs) { - this.mkdirs = mkdirs; - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/VersionColumnLabelProvider.java b/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/VersionColumnLabelProvider.java deleted file mode 100644 index 1ce3154..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/VersionColumnLabelProvider.java +++ /dev/null @@ -1,80 +0,0 @@ -package org.argeo.eclipse.ui.jcr; - -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.RepositoryException; -import javax.jcr.version.Version; - -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.swt.graphics.Image; - -/** Simplifies writing JCR-based column label provider. */ -public class VersionColumnLabelProvider extends ColumnLabelProvider { - private static final long serialVersionUID = -6117690082313161159L; - - protected String getVersionText(Version version) throws RepositoryException { - return super.getText(version); - } - - protected String getVersionToolTipText(Version version) throws RepositoryException { - return super.getToolTipText(version); - } - - protected Image getVersionImage(Version version) throws RepositoryException { - return super.getImage(version); - } - - protected String getUserName(Version version) throws RepositoryException { - Node node = version.getFrozenNode(); - if(node.hasProperty(Property.JCR_LAST_MODIFIED_BY)) - return node.getProperty(Property.JCR_LAST_MODIFIED_BY).getString(); - if(node.hasProperty(Property.JCR_CREATED_BY)) - return node.getProperty(Property.JCR_CREATED_BY).getString(); - return null; - } - -// protected String getActivityTitle(Version version) throws RepositoryException { -// Node activity = getActivity(version); -// if (activity == null) -// return null; -// if (activity.hasProperty("jcr:activityTitle")) -// return activity.getProperty("jcr:activityTitle").getString(); -// else -// return activity.getName(); -// } -// -// protected Node getActivity(Version version) throws RepositoryException { -// if (version.hasProperty(Property.JCR_ACTIVITY)) { -// return version.getProperty(Property.JCR_ACTIVITY).getNode(); -// } else -// return null; -// } - - @Override - public String getText(Object element) { - try { - return getVersionText((Version) element); - } catch (RepositoryException e) { - throw new RuntimeException("Runtime repository exception when accessing " + element, e); - } - } - - @Override - public Image getImage(Object element) { - try { - return getVersionImage((Version) element); - } catch (RepositoryException e) { - throw new RuntimeException("Runtime repository exception when accessing " + element, e); - } - } - - @Override - public String getToolTipText(Object element) { - try { - return getVersionToolTipText((Version) element); - } catch (RepositoryException e) { - throw new RuntimeException("Runtime repository exception when accessing " + element, e); - } - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/VersionHistoryContentProvider.java b/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/VersionHistoryContentProvider.java deleted file mode 100644 index 32e5d30..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/VersionHistoryContentProvider.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.argeo.eclipse.ui.jcr; - -import javax.jcr.version.VersionHistory; - -import org.argeo.jcr.Jcr; -import org.eclipse.jface.viewers.IStructuredContentProvider; -import org.eclipse.jface.viewers.Viewer; - -/** Content provider based on a {@link VersionHistory}. */ -public class VersionHistoryContentProvider implements IStructuredContentProvider { - private static final long serialVersionUID = -4921107883428887012L; - - @Override - public void dispose() { - } - - @Override - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - } - - @Override - public Object[] getElements(Object inputElement) { - VersionHistory versionHistory = (VersionHistory) inputElement; - return Jcr.getLinearVersions(versionHistory).toArray(); - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/WrappedNode.java b/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/WrappedNode.java deleted file mode 100644 index 43df1fe..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/WrappedNode.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.argeo.eclipse.ui.jcr; - -import javax.jcr.Node; - -/** Wrap a node (created from a {@link NodesWrapper}) */ -public class WrappedNode { - private final NodesWrapper parent; - private final Node node; - - public WrappedNode(NodesWrapper parent, Node node) { - super(); - this.parent = parent; - this.node = node; - } - - public NodesWrapper getParent() { - return parent; - } - - public Node getNode() { - return node; - } - - public String toString() { - return "wrapped " + node; - } - - @Override - public int hashCode() { - return node.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof WrappedNode) - return node.equals(((WrappedNode) obj).getNode()); - else - return false; - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/JcrColumnDefinition.java b/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/JcrColumnDefinition.java deleted file mode 100644 index c5dd733..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/JcrColumnDefinition.java +++ /dev/null @@ -1,116 +0,0 @@ -package org.argeo.eclipse.ui.jcr.lists; - -import javax.jcr.Node; -import javax.jcr.query.Row; - -import org.argeo.eclipse.ui.ColumnDefinition; - -/** - * Utility object to manage column in various tables and extracts displaying - * data from JCR - */ -public class JcrColumnDefinition extends ColumnDefinition { - private final static int DEFAULT_COLUMN_SIZE = 120; - - private String selectorName; - private String propertyName; - private int propertyType; - private int columnSize; - - /** - * Use this kind of columns to configure a table that displays JCR - * {@link Row} - * - * @param selectorName - * @param propertyName - * @param propertyType - * @param headerLabel - */ - public JcrColumnDefinition(String selectorName, String propertyName, - int propertyType, String headerLabel) { - super(new SimpleJcrRowLabelProvider(selectorName, propertyName), - headerLabel); - this.selectorName = selectorName; - this.propertyName = propertyName; - this.propertyType = propertyType; - this.columnSize = DEFAULT_COLUMN_SIZE; - } - - /** - * Use this kind of columns to configure a table that displays JCR - * {@link Row} - * - * @param selectorName - * @param propertyName - * @param propertyType - * @param headerLabel - * @param columnSize - */ - public JcrColumnDefinition(String selectorName, String propertyName, - int propertyType, String headerLabel, int columnSize) { - super(new SimpleJcrRowLabelProvider(selectorName, propertyName), - headerLabel, columnSize); - this.selectorName = selectorName; - this.propertyName = propertyName; - this.propertyType = propertyType; - this.columnSize = columnSize; - } - - /** - * Use this kind of columns to configure a table that displays JCR - * {@link Node} - * - * @param propertyName - * @param propertyType - * @param headerLabel - * @param columnSize - */ - public JcrColumnDefinition(String propertyName, int propertyType, - String headerLabel, int columnSize) { - super(new SimpleJcrNodeLabelProvider(propertyName), headerLabel, - columnSize); - this.propertyName = propertyName; - this.propertyType = propertyType; - this.columnSize = columnSize; - } - - public String getSelectorName() { - return selectorName; - } - - public void setSelectorName(String selectorName) { - this.selectorName = selectorName; - } - - public String getPropertyName() { - return propertyName; - } - - public void setPropertyName(String propertyName) { - this.propertyName = propertyName; - } - - public int getPropertyType() { - return propertyType; - } - - public void setPropertyType(int propertyType) { - this.propertyType = propertyType; - } - - public int getColumnSize() { - return columnSize; - } - - public void setColumnSize(int columnSize) { - this.columnSize = columnSize; - } - - public String getHeaderLabel() { - return super.getLabel(); - } - - public void setHeaderLabel(String headerLabel) { - super.setLabel(headerLabel); - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/NodeViewerComparator.java b/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/NodeViewerComparator.java deleted file mode 100644 index d990460..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/NodeViewerComparator.java +++ /dev/null @@ -1,190 +0,0 @@ -package org.argeo.eclipse.ui.jcr.lists; - -import java.math.BigDecimal; -import java.util.Calendar; - -import javax.jcr.Node; -import javax.jcr.PropertyType; -import javax.jcr.RepositoryException; -import javax.jcr.Value; -import javax.jcr.ValueFormatException; - -import org.argeo.eclipse.ui.EclipseUiException; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.jface.viewers.ViewerComparator; - -/** - * Base comparator to enable ordering on Table or Tree viewer that display Jcr - * Nodes. - * - * Note that the following snippet must be added before setting the comparator - * to the corresponding control: - * // IMPORTANT: initialize comparator before setting it - * JcrColumnDefinition firstCol = colDefs.get(0); - * comparator.setColumn(firstCol.getPropertyType(), - * firstCol.getPropertyName()); - * viewer.setComparator(comparator); - */ -public class NodeViewerComparator extends ViewerComparator { - private static final long serialVersionUID = -7782916140737279027L; - - protected String propertyName; - - protected int propertyType; - public static final int ASCENDING = 0, DESCENDING = 1; - protected int direction = DESCENDING; - - public NodeViewerComparator() { - } - - /** - * e1 and e2 must both be Jcr nodes. - * - * @param viewer - * @param e1 - * @param e2 - * @return the comparison - */ - @Override - public int compare(Viewer viewer, Object e1, Object e2) { - int rc = 0; - long lc = 0; - - try { - Node n1 = (Node) e1; - Node n2 = (Node) e2; - - Value v1 = null; - Value v2 = null; - if (n1.hasProperty(propertyName)) - v1 = n1.getProperty(propertyName).getValue(); - if (n2.hasProperty(propertyName)) - v2 = n2.getProperty(propertyName).getValue(); - - if (v2 == null && v1 == null) - return 0; - else if (v2 == null) - return -1; - else if (v1 == null) - return 1; - - switch (propertyType) { - case PropertyType.STRING: - rc = v1.getString().compareTo(v2.getString()); - break; - case PropertyType.BOOLEAN: - boolean b1 = v1.getBoolean(); - boolean b2 = v2.getBoolean(); - if (b1 == b2) - rc = 0; - else - // we assume true is greater than false - rc = b1 ? 1 : -1; - break; - case PropertyType.DATE: - Calendar c1 = v1.getDate(); - Calendar c2 = v2.getDate(); - if (c1 == null || c2 == null) - // log.trace("undefined date"); - ; - lc = c1.getTimeInMillis() - c2.getTimeInMillis(); - if (lc < Integer.MIN_VALUE) - rc = -1; - else if (lc > Integer.MAX_VALUE) - rc = 1; - else - rc = (int) lc; - break; - case PropertyType.LONG: - long l1; - long l2; - // TODO Sometimes an empty string is set instead of a long - try { - l1 = v1.getLong(); - } catch (ValueFormatException ve) { - l1 = 0; - } - try { - l2 = v2.getLong(); - } catch (ValueFormatException ve) { - l2 = 0; - } - - lc = l1 - l2; - if (lc < Integer.MIN_VALUE) - rc = -1; - else if (lc > Integer.MAX_VALUE) - rc = 1; - else - rc = (int) lc; - break; - case PropertyType.DECIMAL: - BigDecimal bd1 = v1.getDecimal(); - BigDecimal bd2 = v2.getDecimal(); - rc = bd1.compareTo(bd2); - break; - case PropertyType.DOUBLE: - Double d1 = v1.getDouble(); - Double d2 = v2.getDouble(); - rc = d1.compareTo(d2); - break; - default: - throw new EclipseUiException( - "Unimplemented comparaison for PropertyType " - + propertyType); - } - // If descending order, flip the direction - if (direction == DESCENDING) { - rc = -rc; - } - - } catch (RepositoryException re) { - throw new EclipseUiException("Unexpected error " - + "while comparing nodes", re); - } - return rc; - } - - /** - * @param propertyType - * Corresponding JCR type - * @param propertyName - * name of the property to use. - */ - public void setColumn(int propertyType, String propertyName) { - if (this.propertyName != null && this.propertyName.equals(propertyName)) { - // Same column as last sort; toggle the direction - direction = 1 - direction; - } else { - // New column; do an ascending sort - this.propertyType = propertyType; - this.propertyName = propertyName; - direction = ASCENDING; - } - } - - // Getters and setters - protected String getPropertyName() { - return propertyName; - } - - protected void setPropertyName(String propertyName) { - this.propertyName = propertyName; - } - - protected int getPropertyType() { - return propertyType; - } - - protected void setPropertyType(int propertyType) { - this.propertyType = propertyType; - } - - protected int getDirection() { - return direction; - } - - protected void setDirection(int direction) { - this.direction = direction; - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/RowViewerComparator.java b/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/RowViewerComparator.java deleted file mode 100644 index 60d637d..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/RowViewerComparator.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.argeo.eclipse.ui.jcr.lists; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.query.Row; - -import org.argeo.eclipse.ui.EclipseUiException; -import org.eclipse.jface.viewers.Viewer; - -/** - * Base comparator to enable ordering on Table or Tree viewer that display Jcr - * rows - */ -public class RowViewerComparator extends NodeViewerComparator { - private static final long serialVersionUID = 7020939505172625113L; - protected String selectorName; - - public RowViewerComparator() { - } - - /** - * e1 and e2 must both be Jcr rows. - * - * @param viewer - * @param e1 - * @param e2 - * @return the comparison - */ - @Override - public int compare(Viewer viewer, Object e1, Object e2) { - try { - Node n1 = ((Row) e1).getNode(selectorName); - Node n2 = ((Row) e2).getNode(selectorName); - return super.compare(viewer, n1, n2); - } catch (RepositoryException re) { - throw new EclipseUiException("Unexpected error " - + "while comparing nodes", re); - } - } - - /** - * @param propertyType - * Corresponding JCR type - * @param propertyName - * name of the property to use. - */ - public void setColumn(int propertyType, String selectorName, - String propertyName) { - if (this.selectorName != null && getPropertyName() != null - && this.selectorName.equals(selectorName) - && this.getPropertyName().equals(propertyName)) { - // Same column as last sort; toggle the direction - setDirection(1 - getDirection()); - } else { - // New column; do a descending sort - setPropertyType(propertyType); - setPropertyName(propertyName); - this.selectorName = selectorName; - setDirection(NodeViewerComparator.ASCENDING); - } - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/SimpleJcrNodeLabelProvider.java b/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/SimpleJcrNodeLabelProvider.java deleted file mode 100644 index aa2e337..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/SimpleJcrNodeLabelProvider.java +++ /dev/null @@ -1,120 +0,0 @@ -package org.argeo.eclipse.ui.jcr.lists; - -import java.text.DateFormat; -import java.text.DecimalFormat; -import java.text.NumberFormat; -import java.text.SimpleDateFormat; - -import javax.jcr.Node; -import javax.jcr.PropertyType; -import javax.jcr.RepositoryException; -import javax.jcr.Value; - -import org.argeo.eclipse.ui.EclipseUiException; -import org.eclipse.jface.viewers.ColumnLabelProvider; - -/** Base implementation of a label provider for controls that display JCR Nodes */ -public class SimpleJcrNodeLabelProvider extends ColumnLabelProvider { - private static final long serialVersionUID = -5215787695436221993L; - - private final static String DEFAULT_DATE_FORMAT = "EEE, dd MMM yyyy"; - private final static String DEFAULT_NUMBER_FORMAT = "#,##0.0"; - - private DateFormat dateFormat; - private NumberFormat numberFormat; - - final private String propertyName; - - /** - * Default Label provider for a given property of a node. Using default - * pattern for date and number formating - */ - public SimpleJcrNodeLabelProvider(String propertyName) { - this.propertyName = propertyName; - dateFormat = new SimpleDateFormat(DEFAULT_DATE_FORMAT); - numberFormat = DecimalFormat.getInstance(); - ((DecimalFormat) numberFormat).applyPattern(DEFAULT_NUMBER_FORMAT); - } - - /** - * Label provider for a given property of a node optionally precising date - * and/or number format patterns - */ - public SimpleJcrNodeLabelProvider(String propertyName, - String dateFormatPattern, String numberFormatPattern) { - this.propertyName = propertyName; - dateFormat = new SimpleDateFormat( - dateFormatPattern == null ? DEFAULT_DATE_FORMAT - : dateFormatPattern); - numberFormat = DecimalFormat.getInstance(); - ((DecimalFormat) numberFormat) - .applyPattern(numberFormatPattern == null ? DEFAULT_NUMBER_FORMAT - : numberFormatPattern); - } - - @Override - public String getText(Object element) { - try { - Node currNode = (Node) element; - - if (currNode.hasProperty(propertyName)) { - if (currNode.getProperty(propertyName).isMultiple()) { - StringBuilder builder = new StringBuilder(); - for (Value value : currNode.getProperty(propertyName) - .getValues()) { - String currStr = getSingleValueAsString(value); - if (notEmptyString(currStr)) - builder.append(currStr).append("; "); - } - if (builder.length() > 0) - builder.deleteCharAt(builder.length() - 2); - - return builder.toString(); - } else - return getSingleValueAsString(currNode.getProperty( - propertyName).getValue()); - } else - return ""; - } catch (RepositoryException re) { - throw new EclipseUiException("Unable to get text from row", re); - } - } - - private String getSingleValueAsString(Value value) - throws RepositoryException { - switch (value.getType()) { - case PropertyType.STRING: - return value.getString(); - case PropertyType.BOOLEAN: - return "" + value.getBoolean(); - case PropertyType.DATE: - return dateFormat.format(value.getDate().getTime()); - case PropertyType.LONG: - return "" + value.getLong(); - case PropertyType.DECIMAL: - return numberFormat.format(value.getDecimal()); - case PropertyType.DOUBLE: - return numberFormat.format(value.getDouble()); - case PropertyType.NAME: - return value.getString(); - default: - throw new EclipseUiException("Unimplemented label provider " - + "for property type " + value.getType() - + " while getting property " + propertyName + " - value: " - + value.getString()); - - } - } - - private boolean notEmptyString(String string) { - return string != null && !"".equals(string.trim()); - } - - public void setDateFormat(String dateFormatPattern) { - dateFormat = new SimpleDateFormat(dateFormatPattern); - } - - public void setNumberFormat(String numberFormatPattern) { - ((DecimalFormat) numberFormat).applyPattern(numberFormatPattern); - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/SimpleJcrRowLabelProvider.java b/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/SimpleJcrRowLabelProvider.java deleted file mode 100644 index 5d421f6..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/SimpleJcrRowLabelProvider.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.argeo.eclipse.ui.jcr.lists; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.query.Row; - -import org.argeo.eclipse.ui.EclipseUiException; - -/** - * Base implementation of a label provider for widgets that display JCR Rows. - */ -public class SimpleJcrRowLabelProvider extends SimpleJcrNodeLabelProvider { - private static final long serialVersionUID = -3414654948197181740L; - - final private String selectorName; - - /** - * Default Label provider for a given property of a row. Using default - * pattern for date and number formating - */ - public SimpleJcrRowLabelProvider(String selectorName, String propertyName) { - super(propertyName); - this.selectorName = selectorName; - } - - /** - * Label provider for a given property of a node optionally precising date - * and/or number format patterns - */ - public SimpleJcrRowLabelProvider(String selectorName, String propertyName, - String dateFormatPattern, String numberFormatPattern) { - super(propertyName, dateFormatPattern, numberFormatPattern); - this.selectorName = selectorName; - } - - @Override - public String getText(Object element) { - try { - Row currRow = (Row) element; - Node currNode = currRow.getNode(selectorName); - return super.getText(currNode); - } catch (RepositoryException re) { - throw new EclipseUiException("Unable to get Node " + selectorName - + " from row " + element, re); - } - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/package-info.java b/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/package-info.java deleted file mode 100644 index 3678aab..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Generic SWT/JFace JCR utilities for lists. */ -package org.argeo.eclipse.ui.jcr.lists; \ No newline at end of file diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/package-info.java b/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/package-info.java deleted file mode 100644 index 19e3cc3..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Generic SWT/JFace JCR utilities. */ -package org.argeo.eclipse.ui.jcr; \ No newline at end of file diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/JcrFileProvider.java b/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/JcrFileProvider.java deleted file mode 100644 index c82e666..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/JcrFileProvider.java +++ /dev/null @@ -1,129 +0,0 @@ -package org.argeo.eclipse.ui.jcr.util; - -import java.io.InputStream; - -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.RepositoryException; -import javax.jcr.nodetype.NodeType; - -import org.apache.commons.io.IOUtils; -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.eclipse.ui.FileProvider; - -/** - * Implements a FileProvider for UI purposes. Note that it might not be very - * reliable as long as we have not fixed login and multi repository issues that - * will be addressed in the next version. - * - * NOTE: id used here is the real id of the JCR Node, not the JCR Path - * - * Relies on common approach for JCR file handling implementation. - * - */ -@SuppressWarnings("deprecation") -public class JcrFileProvider implements FileProvider { - - // private Object[] rootNodes; - private Node refNode; - - /** - * Must be set in order for the provider to be able to get current session - * and thus have the ability to get the file node corresponding to a given - * file ID - * - * @param refNode - */ - public void setReferenceNode(Node refNode) { - // FIXME : this introduces some concurrency ISSUES. - this.refNode = refNode; - } - - public byte[] getByteArrayFileFromId(String fileId) { - InputStream fis = null; - byte[] ba = null; - Node child = getFileNodeFromId(fileId); - try { - fis = (InputStream) child.getProperty(Property.JCR_DATA).getBinary().getStream(); - ba = IOUtils.toByteArray(fis); - - } catch (Exception e) { - throw new EclipseUiException("Stream error while opening file", e); - } finally { - IOUtils.closeQuietly(fis); - } - return ba; - } - - public InputStream getInputStreamFromFileId(String fileId) { - try { - InputStream fis = null; - - Node child = getFileNodeFromId(fileId); - fis = (InputStream) child.getProperty(Property.JCR_DATA).getBinary().getStream(); - return fis; - } catch (RepositoryException re) { - throw new EclipseUiException("Cannot get stream from file node for Id " + fileId, re); - } - } - - /** - * Throws an exception if the node is not found in the current repository (a - * bit like a FileNotFoundException) - * - * @param fileId - * @return Returns the child node of the nt:file node. It is the child node - * that have the jcr:data property where actual file is stored. - * never null - */ - private Node getFileNodeFromId(String fileId) { - try { - Node result = refNode.getSession().getNodeByIdentifier(fileId); - - // rootNodes: for (int j = 0; j < rootNodes.length; j++) { - // // in case we have a classic JCR Node - // if (rootNodes[j] instanceof Node) { - // Node curNode = (Node) rootNodes[j]; - // if (result != null) - // break rootNodes; - // } // Case of a repository Node - // else if (rootNodes[j] instanceof RepositoryNode) { - // Object[] nodes = ((RepositoryNode) rootNodes[j]) - // .getChildren(); - // for (int i = 0; i < nodes.length; i++) { - // Node node = (Node) nodes[i]; - // result = node.getSession().getNodeByIdentifier(fileId); - // if (result != null) - // break rootNodes; - // } - // } - // } - - // Sanity checks - if (result == null) - throw new EclipseUiException("File node not found for ID" + fileId); - - Node child = null; - - boolean isValid = true; - if (!result.isNodeType(NodeType.NT_FILE)) - // useless: mandatory child node - // || !result.hasNode(Property.JCR_CONTENT)) - isValid = false; - else { - child = result.getNode(Property.JCR_CONTENT); - if (!(child.isNodeType(NodeType.NT_RESOURCE) || child.hasProperty(Property.JCR_DATA))) - isValid = false; - } - - if (!isValid) - throw new EclipseUiException("ERROR: In the current implemented model, '" + NodeType.NT_FILE - + "' file node must have a child node named jcr:content " - + "that has a BINARY Property named jcr:data " + "where the actual data is stored"); - return child; - - } catch (RepositoryException re) { - throw new EclipseUiException("Erreur while getting file node of ID " + fileId, re); - } - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/JcrItemsComparator.java b/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/JcrItemsComparator.java deleted file mode 100644 index fb12399..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/JcrItemsComparator.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.argeo.eclipse.ui.jcr.util; - -import java.util.Comparator; - -import javax.jcr.Item; -import javax.jcr.RepositoryException; - -import org.argeo.eclipse.ui.EclipseUiException; - -/** Compares two JCR items (node or properties) based on their names. */ -public class JcrItemsComparator implements Comparator { - public int compare(Item o1, Item o2) { - try { - // TODO: put folder before files - return o1.getName().toLowerCase().compareTo(o2.getName().toLowerCase()); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot compare " + o1 + " and " + o2, e); - } - } - -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/NodeViewerComparer.java b/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/NodeViewerComparer.java deleted file mode 100644 index 54b795f..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/NodeViewerComparer.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.argeo.eclipse.ui.jcr.util; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.eclipse.ui.EclipseUiException; -import org.eclipse.jface.viewers.IElementComparer; - -/** Compare JCR nodes based on their JCR identifiers, for use in JFace viewers. */ -public class NodeViewerComparer implements IElementComparer { - - // force comparison on Node IDs only. - public boolean equals(Object elementA, Object elementB) { - if (!(elementA instanceof Node) || !(elementB instanceof Node)) { - return elementA == null ? elementB == null : elementA - .equals(elementB); - } else { - - boolean result = false; - try { - String idA = ((Node) elementA).getIdentifier(); - String idB = ((Node) elementB).getIdentifier(); - result = idA == null ? idB == null : idA.equals(idB); - } catch (RepositoryException re) { - throw new EclipseUiException("cannot compare nodes", re); - } - - return result; - } - } - - public int hashCode(Object element) { - // TODO enhanced this method. - return element.getClass().toString().hashCode(); - } -} \ No newline at end of file diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/SingleSessionFileProvider.java b/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/SingleSessionFileProvider.java deleted file mode 100644 index 291d579..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/SingleSessionFileProvider.java +++ /dev/null @@ -1,98 +0,0 @@ -package org.argeo.eclipse.ui.jcr.util; - -import java.io.InputStream; - -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.nodetype.NodeType; - -import org.apache.commons.io.IOUtils; -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.eclipse.ui.FileProvider; - -/** - * Implements a FileProvider for UI purposes. Unlike the - * JcrFileProvider , it relies on a single session and manages - * nodes with path only. - * - * Note that considered id is the JCR path - * - * Relies on common approach for JCR file handling implementation. - */ -@SuppressWarnings("deprecation") -public class SingleSessionFileProvider implements FileProvider { - - private Session session; - - public SingleSessionFileProvider(Session session) { - this.session = session; - } - - public byte[] getByteArrayFileFromId(String fileId) { - InputStream fis = null; - byte[] ba = null; - Node child = getFileNodeFromId(fileId); - try { - fis = (InputStream) child.getProperty(Property.JCR_DATA) - .getBinary().getStream(); - ba = IOUtils.toByteArray(fis); - - } catch (Exception e) { - throw new EclipseUiException("Stream error while opening file", e); - } finally { - IOUtils.closeQuietly(fis); - } - return ba; - } - - public InputStream getInputStreamFromFileId(String fileId) { - try { - InputStream fis = null; - - Node child = getFileNodeFromId(fileId); - fis = (InputStream) child.getProperty(Property.JCR_DATA) - .getBinary().getStream(); - return fis; - } catch (RepositoryException re) { - throw new EclipseUiException("Cannot get stream from file node for Id " - + fileId, re); - } - } - - /** - * - * @param fileId - * @return Returns the child node of the nt:file node. It is the child node - * that have the jcr:data property where actual file is stored. - * never null - */ - private Node getFileNodeFromId(String fileId) { - try { - Node result = null; - result = session.getNode(fileId); - - // Sanity checks - if (result == null) - throw new EclipseUiException("File node not found for ID" + fileId); - - // Ensure that the node have the correct type. - if (!result.isNodeType(NodeType.NT_FILE)) - throw new EclipseUiException( - "Cannot open file children Node that are not of " - + NodeType.NT_RESOURCE + " type."); - - Node child = result.getNodes().nextNode(); - if (child == null || !child.isNodeType(NodeType.NT_RESOURCE)) - throw new EclipseUiException( - "ERROR: IN the current implemented model, " - + NodeType.NT_FILE - + " file node must have one and only one child of the nt:ressource, where actual data is stored"); - return child; - } catch (RepositoryException re) { - throw new EclipseUiException("Erreur while getting file node of ID " - + fileId, re); - } - } -} diff --git a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/package-info.java b/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/package-info.java deleted file mode 100644 index 016348c..0000000 --- a/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Generic SWT/JFace JCR helpers. */ -package org.argeo.eclipse.ui.jcr.util; \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.e4/.classpath b/swt/org.argeo.cms.jcr.e4/.classpath new file mode 100644 index 0000000..81fe078 --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/swt/org.argeo.cms.jcr.e4/.project b/swt/org.argeo.cms.jcr.e4/.project new file mode 100644 index 0000000..01f8dd2 --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/.project @@ -0,0 +1,28 @@ + + + org.argeo.cms.jcr.e4 + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/swt/org.argeo.cms.jcr.e4/bnd.bnd b/swt/org.argeo.cms.jcr.e4/bnd.bnd new file mode 100644 index 0000000..fde6c09 --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/bnd.bnd @@ -0,0 +1,12 @@ +Import-Package: \ +org.eclipse.swt,\ +org.eclipse.swt.widgets;version="0.0.0",\ +org.eclipse.jface.window,\ +org.eclipse.core.commands.common,\ +org.eclipse.e4.ui.model.application.ui,\ +org.eclipse.e4.ui.model.application,\ +javax.jcr.nodetype,\ +org.apache.jackrabbit.*;version="[2,3)",\ +org.argeo.cms,\ +org.argeo.jcr,\ +* \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.e4/build.properties b/swt/org.argeo.cms.jcr.e4/build.properties new file mode 100644 index 0000000..34d2e4d --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/EclipseJcrMonitor.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/EclipseJcrMonitor.java new file mode 100644 index 0000000..e10738e --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/EclipseJcrMonitor.java @@ -0,0 +1,44 @@ +package org.argeo.cms.e4.jcr; + +import org.argeo.jcr.JcrMonitor; +import org.eclipse.core.runtime.IProgressMonitor; + +/** + * Wraps an Eclipse {@link IProgressMonitor} so that it can be passed to + * framework agnostic Argeo routines. + */ +public class EclipseJcrMonitor implements JcrMonitor { + private final IProgressMonitor progressMonitor; + + public EclipseJcrMonitor(IProgressMonitor progressMonitor) { + this.progressMonitor = progressMonitor; + } + + public void beginTask(String name, int totalWork) { + progressMonitor.beginTask(name, totalWork); + } + + public void done() { + progressMonitor.done(); + } + + public boolean isCanceled() { + return progressMonitor.isCanceled(); + } + + public void setCanceled(boolean value) { + progressMonitor.setCanceled(value); + } + + public void setTaskName(String name) { + progressMonitor.setTaskName(name); + } + + public void subTask(String name) { + progressMonitor.subTask(name); + } + + public void worked(int work) { + progressMonitor.worked(work); + } +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/GenericPropertyPage.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/GenericPropertyPage.java new file mode 100644 index 0000000..e17f17b --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/GenericPropertyPage.java @@ -0,0 +1,141 @@ +package org.argeo.cms.e4.jcr; + +import java.util.ArrayList; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.PropertyIterator; +import javax.jcr.RepositoryException; + +import org.argeo.cms.ui.jcr.PropertyLabelProvider; +import org.argeo.eclipse.ui.EclipseUiException; +import org.eclipse.jface.layout.TreeColumnLayout; +import org.eclipse.jface.viewers.ColumnWeightData; +import org.eclipse.jface.viewers.IBaseLabelProvider; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeColumn; + +/** + * Generic editor property page. Lists all properties of current node as a + * complex tree. TODO: enable editing + */ +public class GenericPropertyPage { + + // Main business Objects + private Node currentNode; + + public GenericPropertyPage(Node currentNode) { + this.currentNode = currentNode; + } + + protected void createFormContent(Composite parent) { + Composite innerBox = new Composite(parent, SWT.NONE); + // Composite innerBox = new Composite(body, SWT.NO_FOCUS); + FillLayout layout = new FillLayout(); + layout.marginHeight = 5; + layout.marginWidth = 5; + innerBox.setLayout(layout); + createComplexTree(innerBox); + // TODO TreeColumnLayout triggers a scroll issue with the form: + // The inside body is always to big and a scroll bar is shown + // Composite tableCmp = new Composite(body, SWT.NO_FOCUS); + // createComplexTree(tableCmp); + } + + private TreeViewer createComplexTree(Composite parent) { + int style = SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION; + Tree tree = new Tree(parent, style); + TreeColumnLayout tableColumnLayout = new TreeColumnLayout(); + + createColumn(tree, tableColumnLayout, "Property", SWT.LEFT, 200, 30); + createColumn(tree, tableColumnLayout, "Value(s)", SWT.LEFT, 300, 60); + createColumn(tree, tableColumnLayout, "Type", SWT.LEFT, 75, 10); + createColumn(tree, tableColumnLayout, "Attributes", SWT.LEFT, 75, 0); + // Do not apply the treeColumnLayout it does not work yet + // parent.setLayout(tableColumnLayout); + + tree.setLinesVisible(true); + tree.setHeaderVisible(true); + + TreeViewer treeViewer = new TreeViewer(tree); + treeViewer.setContentProvider(new TreeContentProvider()); + treeViewer.setLabelProvider((IBaseLabelProvider) new PropertyLabelProvider()); + treeViewer.setInput(currentNode); + treeViewer.expandAll(); + return treeViewer; + } + + private static TreeColumn createColumn(Tree parent, TreeColumnLayout tableColumnLayout, String name, int style, + int width, int weight) { + TreeColumn column = new TreeColumn(parent, style); + column.setText(name); + column.setWidth(width); + column.setMoveable(true); + column.setResizable(true); + tableColumnLayout.setColumnData(column, new ColumnWeightData(weight, width, true)); + return column; + } + + private class TreeContentProvider implements ITreeContentProvider { + private static final long serialVersionUID = -6162736530019406214L; + + public Object[] getElements(Object parent) { + Object[] props = null; + try { + + if (parent instanceof Node) { + Node node = (Node) parent; + PropertyIterator pi; + pi = node.getProperties(); + List propList = new ArrayList(); + while (pi.hasNext()) { + propList.add(pi.nextProperty()); + } + props = propList.toArray(); + } + } catch (RepositoryException e) { + throw new EclipseUiException("Unexpected exception while listing node properties", e); + } + return props; + } + + public Object getParent(Object child) { + return null; + } + + public Object[] getChildren(Object parent) { + if (parent instanceof Property) { + Property prop = (Property) parent; + try { + if (prop.isMultiple()) + return prop.getValues(); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot get multi-prop values on " + prop, e); + } + } + return null; + } + + public boolean hasChildren(Object parent) { + try { + return (parent instanceof Property && ((Property) parent).isMultiple()); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot check if property is multiple for " + parent, e); + } + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + + public void dispose() { + } + } +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/JcrBrowserView.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/JcrBrowserView.java new file mode 100644 index 0000000..0b77c07 --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/JcrBrowserView.java @@ -0,0 +1,349 @@ +package org.argeo.cms.e4.jcr; + +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.inject.Inject; +import javax.jcr.Property; +import javax.jcr.PropertyType; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.RepositoryFactory; +import javax.jcr.Session; +import javax.jcr.Value; +import javax.jcr.observation.Event; +import javax.jcr.observation.EventListener; +import javax.jcr.observation.ObservationManager; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.cms.security.CryptoKeyring; +import org.argeo.cms.security.Keyring; +import org.argeo.cms.swt.CmsException; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ui.jcr.JcrBrowserUtils; +import org.argeo.cms.ui.jcr.NodeContentProvider; +import org.argeo.cms.ui.jcr.NodeLabelProvider; +import org.argeo.cms.ui.jcr.OsgiRepositoryRegister; +import org.argeo.cms.ui.jcr.PropertiesContentProvider; +import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; +import org.argeo.cms.ux.widgets.TreeParent; +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.eclipse.ui.jcr.AsyncUiEventListener; +import org.argeo.eclipse.ui.jcr.util.NodeViewerComparer; +import org.argeo.jcr.JcrUtils; +import org.eclipse.e4.core.contexts.IEclipseContext; +import org.eclipse.e4.core.di.annotations.Optional; +import org.eclipse.e4.ui.services.EMenuService; +import org.eclipse.e4.ui.workbench.modeling.EPartService; +import org.eclipse.e4.ui.workbench.modeling.ESelectionService; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.jface.viewers.IBaseLabelProvider; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TableViewerColumn; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.SashForm; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; + +/** + * Basic View to display a sash form to browse a JCR compliant multiple + * repository environment + */ +public class JcrBrowserView { + final static String ID = "org.argeo.cms.e4.jcrbrowser"; + final static String NODE_VIEWER_POPUP_MENU_ID = "org.argeo.cms.e4.popupmenu.nodeViewer"; + + private boolean sortChildNodes = true; + + /* DEPENDENCY INJECTION */ + @Inject + @Optional + private Keyring keyring; + @Inject + private RepositoryFactory repositoryFactory; + @Inject + private Repository nodeRepository; + + // Current user session on the home repository default workspace + private Session userSession; + + private OsgiRepositoryRegister repositoryRegister = new OsgiRepositoryRegister(); + + // This page widgets + private TreeViewer nodesViewer; + private NodeContentProvider nodeContentProvider; + private TableViewer propertiesViewer; + private EventListener resultsObserver; + + @PostConstruct + public void createPartControl(Composite parent, IEclipseContext context, EPartService partService, + ESelectionService selectionService, EMenuService menuService) { + repositoryRegister.init(); + + parent.setLayout(new FillLayout()); + SashForm sashForm = new SashForm(parent, SWT.VERTICAL); + // sashForm.setSashWidth(4); + // sashForm.setLayout(new FillLayout()); + + // Create the tree on top of the view + Composite top = new Composite(sashForm, SWT.NONE); + // GridLayout gl = new GridLayout(1, false); + top.setLayout(CmsSwtUtils.noSpaceGridLayout()); + + try { + this.userSession = this.nodeRepository.login(CmsConstants.HOME_WORKSPACE); + } catch (RepositoryException e) { + throw new CmsException("Cannot open user session", e); + } + + nodeContentProvider = new NodeContentProvider(userSession, keyring, repositoryRegister, repositoryFactory, + sortChildNodes); + + // nodes viewer + nodesViewer = createNodeViewer(top, nodeContentProvider); + + // context menu : it is completely defined in the plugin.xml file. + // MenuManager menuManager = new MenuManager(); + // Menu menu = menuManager.createContextMenu(nodesViewer.getTree()); + + // nodesViewer.getTree().setMenu(menu); + + nodesViewer.setInput(""); + + // Create the property viewer on the bottom + Composite bottom = new Composite(sashForm, SWT.NONE); + bottom.setLayout(CmsSwtUtils.noSpaceGridLayout()); + propertiesViewer = createPropertiesViewer(bottom); + + sashForm.setWeights(getWeights()); + nodesViewer.setComparer(new NodeViewerComparer()); + nodesViewer.addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + IStructuredSelection selection = (IStructuredSelection) event.getSelection(); + selectionService.setSelection(selection.toList()); + } + }); + nodesViewer.addDoubleClickListener(new JcrE4DClickListener(nodesViewer, partService)); + menuService.registerContextMenu(nodesViewer.getControl(), NODE_VIEWER_POPUP_MENU_ID); + // getSite().registerContextMenu(menuManager, nodesViewer); + // getSite().setSelectionProvider(nodesViewer); + } + + @PreDestroy + public void dispose() { + JcrUtils.logoutQuietly(userSession); + repositoryRegister.destroy(); + } + + public void refresh(Object obj) { + // Enable full refresh from a command when no element of the tree is + // selected + if (obj == null) { + Object[] elements = nodeContentProvider.getElements(null); + for (Object el : elements) { + if (el instanceof TreeParent) + JcrBrowserUtils.forceRefreshIfNeeded((TreeParent) el); + getNodeViewer().refresh(el); + } + } else + getNodeViewer().refresh(obj); + } + + /** + * To be overridden to adapt size of form and result frames. + */ + protected int[] getWeights() { + return new int[] { 70, 30 }; + } + + protected TreeViewer createNodeViewer(Composite parent, final ITreeContentProvider nodeContentProvider) { + + final TreeViewer tmpNodeViewer = new TreeViewer(parent, SWT.MULTI); + + tmpNodeViewer.getTree().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + tmpNodeViewer.setContentProvider(nodeContentProvider); + tmpNodeViewer.setLabelProvider((IBaseLabelProvider) new NodeLabelProvider()); + tmpNodeViewer.addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + if (!event.getSelection().isEmpty()) { + IStructuredSelection sel = (IStructuredSelection) event.getSelection(); + Object firstItem = sel.getFirstElement(); + if (firstItem instanceof SingleJcrNodeElem) + propertiesViewer.setInput(((SingleJcrNodeElem) firstItem).getNode()); + } else { + propertiesViewer.setInput(""); + } + } + }); + + resultsObserver = new TreeObserver(tmpNodeViewer.getTree().getDisplay()); + if (keyring != null) + try { + ObservationManager observationManager = userSession.getWorkspace().getObservationManager(); + observationManager.addEventListener(resultsObserver, Event.PROPERTY_ADDED | Event.PROPERTY_CHANGED, "/", + true, null, null, false); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot register listeners", e); + } + + // tmpNodeViewer.addDoubleClickListener(new JcrDClickListener(tmpNodeViewer)); + return tmpNodeViewer; + } + + protected TableViewer createPropertiesViewer(Composite parent) { + propertiesViewer = new TableViewer(parent, SWT.NONE); + propertiesViewer.getTable().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + propertiesViewer.getTable().setHeaderVisible(true); + propertiesViewer.setContentProvider(new PropertiesContentProvider()); + TableViewerColumn col = new TableViewerColumn(propertiesViewer, SWT.NONE); + col.getColumn().setText("Name"); + col.getColumn().setWidth(200); + col.setLabelProvider(new ColumnLabelProvider() { + private static final long serialVersionUID = -6684361063107478595L; + + public String getText(Object element) { + try { + return ((Property) element).getName(); + } catch (RepositoryException e) { + throw new EclipseUiException("Unexpected exception in label provider", e); + } + } + }); + col = new TableViewerColumn(propertiesViewer, SWT.NONE); + col.getColumn().setText("Value"); + col.getColumn().setWidth(400); + col.setLabelProvider(new ColumnLabelProvider() { + private static final long serialVersionUID = -8201994187693336657L; + + public String getText(Object element) { + try { + Property property = (Property) element; + if (property.getType() == PropertyType.BINARY) + return ""; + else if (property.isMultiple()) { + StringBuffer buf = new StringBuffer("["); + Value[] values = property.getValues(); + for (int i = 0; i < values.length; i++) { + if (i != 0) + buf.append(", "); + buf.append(values[i].getString()); + } + buf.append(']'); + return buf.toString(); + } else + return property.getValue().getString(); + } catch (RepositoryException e) { + throw new EclipseUiException("Unexpected exception in label provider", e); + } + } + }); + col = new TableViewerColumn(propertiesViewer, SWT.NONE); + col.getColumn().setText("Type"); + col.getColumn().setWidth(200); + col.setLabelProvider(new ColumnLabelProvider() { + private static final long serialVersionUID = -6009599998150286070L; + + public String getText(Object element) { + return JcrBrowserUtils.getPropertyTypeAsString((Property) element); + } + }); + propertiesViewer.setInput(""); + return propertiesViewer; + } + + protected TreeViewer getNodeViewer() { + return nodesViewer; + } + + /** + * Resets the tree content provider + * + * @param sortChildNodes if true the content provider will use a comparer to + * sort nodes that might slow down the display + */ + public void setSortChildNodes(boolean sortChildNodes) { + this.sortChildNodes = sortChildNodes; + ((NodeContentProvider) nodesViewer.getContentProvider()).setSortChildren(sortChildNodes); + nodesViewer.setInput(""); + } + + /** Notifies the current view that a node has been added */ + public void nodeAdded(TreeParent parentNode) { + // insure that Ui objects have been correctly created: + JcrBrowserUtils.forceRefreshIfNeeded(parentNode); + getNodeViewer().refresh(parentNode); + getNodeViewer().expandToLevel(parentNode, 1); + } + + /** Notifies the current view that a node has been removed */ + public void nodeRemoved(TreeParent parentNode) { + IStructuredSelection newSel = new StructuredSelection(parentNode); + getNodeViewer().setSelection(newSel, true); + // Force refresh + IStructuredSelection tmpSel = (IStructuredSelection) getNodeViewer().getSelection(); + getNodeViewer().refresh(tmpSel.getFirstElement()); + } + + class TreeObserver extends AsyncUiEventListener { + + public TreeObserver(Display display) { + super(display); + } + + @Override + protected Boolean willProcessInUiThread(List events) throws RepositoryException { + for (Event event : events) { + if (getLog().isTraceEnabled()) + getLog().debug("Received event " + event); + String path = event.getPath(); + int index = path.lastIndexOf('/'); + String propertyName = path.substring(index + 1); + if (getLog().isTraceEnabled()) + getLog().debug("Concerned property " + propertyName); + } + return false; + } + + protected void onEventInUiThread(List events) throws RepositoryException { + if (getLog().isTraceEnabled()) + getLog().trace("Refresh result list"); + nodesViewer.refresh(); + } + + } + + public boolean getSortChildNodes() { + return sortChildNodes; + } + + public void setFocus() { + getNodeViewer().getTree().setFocus(); + } + + /* DEPENDENCY INJECTION */ + // public void setRepositoryRegister(RepositoryRegister repositoryRegister) { + // this.repositoryRegister = repositoryRegister; + // } + + public void setKeyring(CryptoKeyring keyring) { + this.keyring = keyring; + } + + public void setRepositoryFactory(RepositoryFactory repositoryFactory) { + this.repositoryFactory = repositoryFactory; + } + + public void setNodeRepository(Repository nodeRepository) { + this.nodeRepository = nodeRepository; + } +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/JcrE4DClickListener.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/JcrE4DClickListener.java new file mode 100644 index 0000000..f4ee2e8 --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/JcrE4DClickListener.java @@ -0,0 +1,36 @@ +package org.argeo.cms.e4.jcr; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.swt.CmsException; +import org.argeo.cms.ui.jcr.JcrDClickListener; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.workbench.modeling.EPartService; +import org.eclipse.e4.ui.workbench.modeling.EPartService.PartState; +import org.eclipse.jface.viewers.TreeViewer; + +public class JcrE4DClickListener extends JcrDClickListener { + EPartService partService; + + public JcrE4DClickListener(TreeViewer nodeViewer, EPartService partService) { + super(nodeViewer); + this.partService = partService; + } + + @Override + protected void openNode(Node node) { + MPart part = partService.createPart(JcrNodeEditor.DESCRIPTOR_ID); + try { + part.setLabel(node.getName()); + part.getPersistedState().put("nodeWorkspace", node.getSession().getWorkspace().getName()); + part.getPersistedState().put("nodePath", node.getPath()); + } catch (RepositoryException e) { + throw new CmsException("Cannot open " + node, e); + } + + // the provided part is be shown + partService.showPart(part, PartState.ACTIVATE); + } + +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/JcrNodeEditor.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/JcrNodeEditor.java new file mode 100644 index 0000000..ae2b325 --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/JcrNodeEditor.java @@ -0,0 +1,26 @@ +package org.argeo.cms.e4.jcr; + +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.jcr.Node; + +import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.workbench.modeling.ESelectionService; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Composite; + +public class JcrNodeEditor { + final static String DESCRIPTOR_ID = "org.argeo.cms.e4.partdescriptor.nodeEditor"; + + @PostConstruct + public void createUi(Composite parent, MPart part, ESelectionService selectionService) { + parent.setLayout(new FillLayout()); + List selection = (List) selectionService.getSelection(); + Node node = ((SingleJcrNodeElem) selection.get(0)).getNode(); + GenericPropertyPage propertyPage = new GenericPropertyPage(node); + propertyPage.createFormContent(parent); + } + +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/SimplePart.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/SimplePart.java new file mode 100644 index 0000000..17d8d2a --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/SimplePart.java @@ -0,0 +1,19 @@ +package org.argeo.cms.e4.jcr; + +import javax.annotation.PostConstruct; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; + +public class SimplePart { + + @PostConstruct + void init(Composite parent) { + parent.setLayout(new GridLayout()); + Label label = new Label(parent, SWT.NONE); + label.setText("Hello e4 World"); + } + +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/AddFolderNode.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/AddFolderNode.java new file mode 100644 index 0000000..09fa760 --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/AddFolderNode.java @@ -0,0 +1,67 @@ +package org.argeo.cms.e4.jcr.handlers; + +import java.util.List; + +import javax.inject.Named; +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.nodetype.NodeType; + +import org.argeo.cms.e4.jcr.JcrBrowserView; +import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; +import org.argeo.cms.ui.jcr.model.WorkspaceElem; +import org.argeo.cms.ux.widgets.TreeParent; +import org.argeo.eclipse.ui.dialogs.ErrorFeedback; +import org.argeo.eclipse.ui.dialogs.SingleValue; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.services.IServiceConstants; +import org.eclipse.e4.ui.workbench.modeling.ESelectionService; + +/** + * Adds a node of type nt:folder, only on {@link SingleJcrNodeElem} and + * {@link WorkspaceElem} TreeObject types. + * + * This handler assumes that a selection provider is available and picks only + * first selected item. It is UI's job to enable the command only when the + * selection contains one and only one element. Thus no parameter is passed + * through the command. + */ +public class AddFolderNode { + @Execute + public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) { + List selection = (List) selectionService.getSelection(); + JcrBrowserView view = (JcrBrowserView) part.getObject(); + + if (selection != null && selection.size() == 1) { + TreeParent treeParentNode = null; + Node jcrParentNode = null; + Object obj = selection.get(0); + + if (obj instanceof SingleJcrNodeElem) { + treeParentNode = (TreeParent) obj; + jcrParentNode = ((SingleJcrNodeElem) treeParentNode).getNode(); + } else if (obj instanceof WorkspaceElem) { + treeParentNode = (TreeParent) obj; + jcrParentNode = ((WorkspaceElem) treeParentNode).getRootNode(); + } else + return; + + String folderName = SingleValue.ask("Folder name", "Enter folder name"); + if (folderName != null) { + try { + jcrParentNode.addNode(folderName, NodeType.NT_FOLDER); + jcrParentNode.getSession().save(); + view.nodeAdded(treeParentNode); + } catch (RepositoryException e) { + ErrorFeedback.show("Cannot create folder " + folderName + " under " + treeParentNode, e); + } + } + } else { + // ErrorFeedback.show(WorkbenchUiPlugin + // .getMessage("errorUnvalidNtFolderNodeType")); + ErrorFeedback.show("Invalid NT folder node type"); + } + } + +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/AddRemoteRepository.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/AddRemoteRepository.java new file mode 100644 index 0000000..dc47f6e --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/AddRemoteRepository.java @@ -0,0 +1,210 @@ +package org.argeo.cms.e4.jcr.handlers; + +import java.net.URI; +import java.util.Hashtable; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.jcr.Node; +import javax.jcr.Repository; +import javax.jcr.RepositoryFactory; +import javax.jcr.Session; +import javax.jcr.SimpleCredentials; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.cms.ArgeoNames; +import org.argeo.cms.ArgeoTypes; +import org.argeo.cms.e4.jcr.JcrBrowserView; +import org.argeo.cms.jcr.CmsJcrUtils; +import org.argeo.cms.security.Keyring; +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.eclipse.ui.dialogs.ErrorFeedback; +import org.argeo.jcr.JcrUtils; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.core.di.annotations.Optional; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.services.IServiceConstants; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IMessageProvider; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.dialogs.TitleAreaDialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +/** + * Connect to a remote repository and, if successful publish it as an OSGi + * service. + */ +public class AddRemoteRepository { + + @Inject + private RepositoryFactory repositoryFactory; + @Inject + private Repository nodeRepository; + @Inject + @Optional + private Keyring keyring; + + @Execute + public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part) { + JcrBrowserView view = (JcrBrowserView) part.getObject(); + RemoteRepositoryLoginDialog dlg = new RemoteRepositoryLoginDialog(Display.getDefault().getActiveShell()); + if (dlg.open() == Dialog.OK) { + view.refresh(null); + } + } + + // public void setRepositoryFactory(RepositoryFactory repositoryFactory) { + // this.repositoryFactory = repositoryFactory; + // } + // + // public void setKeyring(Keyring keyring) { + // this.keyring = keyring; + // } + // + // public void setNodeRepository(Repository nodeRepository) { + // this.nodeRepository = nodeRepository; + // } + + class RemoteRepositoryLoginDialog extends TitleAreaDialog { + private static final long serialVersionUID = 2234006887750103399L; + private Text name; + private Text uri; + private Text username; + private Text password; + private Button saveInKeyring; + + public RemoteRepositoryLoginDialog(Shell parentShell) { + super(parentShell); + } + + protected Point getInitialSize() { + return new Point(600, 400); + } + + protected Control createDialogArea(Composite parent) { + Composite dialogarea = (Composite) super.createDialogArea(parent); + dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + Composite composite = new Composite(dialogarea, SWT.NONE); + composite.setLayout(new GridLayout(2, false)); + composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + setMessage("Login to remote repository", IMessageProvider.NONE); + name = createLT(composite, "Name", "remoteRepository"); + uri = createLT(composite, "URI", "http://localhost:7070/jcr/node"); + username = createLT(composite, "User", ""); + password = createLP(composite, "Password"); + + saveInKeyring = createLC(composite, "Remember password", false); + parent.pack(); + return composite; + } + + @Override + protected void createButtonsForButtonBar(Composite parent) { + super.createButtonsForButtonBar(parent); + Button test = createButton(parent, 2, "Test", false); + test.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = -1829962269440419560L; + + public void widgetSelected(SelectionEvent arg0) { + testConnection(); + } + }); + } + + void testConnection() { + Session session = null; + try { + URI checkedUri = new URI(uri.getText()); + String checkedUriStr = checkedUri.toString(); + + Hashtable params = new Hashtable(); + params.put(CmsConstants.LABELED_URI, checkedUriStr); + Repository repository = repositoryFactory.getRepository(params); + if (username.getText().trim().equals("")) {// anonymous + // FIXME make it more generic + session = repository.login(CmsConstants.SYS_WORKSPACE); + } else { + // FIXME use getTextChars() when upgrading to 3.7 + // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=297412 + char[] pwd = password.getText().toCharArray(); + SimpleCredentials sc = new SimpleCredentials(username.getText(), pwd); + session = repository.login(sc, "main"); + MessageDialog.openInformation(getParentShell(), "Success", + "Connection to '" + uri.getText() + "' successful"); + } + } catch (Exception e) { + ErrorFeedback.show("Connection test failed for " + uri.getText(), e); + } finally { + JcrUtils.logoutQuietly(session); + } + } + + @Override + protected void okPressed() { + Session nodeSession = null; + try { + nodeSession = nodeRepository.login(); + Node home = CmsJcrUtils.getUserHome(nodeSession); + + Node remote = home.hasNode(ArgeoNames.ARGEO_REMOTE) ? home.getNode(ArgeoNames.ARGEO_REMOTE) + : home.addNode(ArgeoNames.ARGEO_REMOTE); + if (remote.hasNode(name.getText())) + throw new EclipseUiException("There is already a remote repository named " + name.getText()); + Node remoteRepository = remote.addNode(name.getText(), ArgeoTypes.ARGEO_REMOTE_REPOSITORY); + remoteRepository.setProperty(ArgeoNames.ARGEO_URI, uri.getText()); + remoteRepository.setProperty(ArgeoNames.ARGEO_USER_ID, username.getText()); + nodeSession.save(); + if (saveInKeyring.getSelection()) { + String pwdPath = remoteRepository.getPath() + '/' + ArgeoNames.ARGEO_PASSWORD; + keyring.set(pwdPath, password.getText().toCharArray()); + } + nodeSession.save(); + MessageDialog.openInformation(getParentShell(), "Repository Added", + "Remote repository '" + username.getText() + "@" + uri.getText() + "' added"); + + super.okPressed(); + } catch (Exception e) { + ErrorFeedback.show("Cannot add remote repository", e); + } finally { + JcrUtils.logoutQuietly(nodeSession); + } + } + + /** Creates label and text. */ + protected Text createLT(Composite parent, String label, String initial) { + new Label(parent, SWT.NONE).setText(label); + Text text = new Text(parent, SWT.SINGLE | SWT.LEAD | SWT.BORDER); + text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + text.setText(initial); + return text; + } + + /** Creates label and check. */ + protected Button createLC(Composite parent, String label, Boolean initial) { + new Label(parent, SWT.NONE).setText(label); + Button check = new Button(parent, SWT.CHECK); + check.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + check.setSelection(initial); + return check; + } + + protected Text createLP(Composite parent, String label) { + new Label(parent, SWT.NONE).setText(label); + Text text = new Text(parent, SWT.SINGLE | SWT.LEAD | SWT.BORDER | SWT.PASSWORD); + text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + return text; + } + } +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/DeleteNodes.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/DeleteNodes.java new file mode 100644 index 0000000..b8de06b --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/DeleteNodes.java @@ -0,0 +1,95 @@ +package org.argeo.cms.e4.jcr.handlers; + +import java.util.List; + +import javax.inject.Named; +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.e4.jcr.JcrBrowserView; +import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; +import org.argeo.cms.ui.jcr.model.WorkspaceElem; +import org.argeo.cms.ux.widgets.TreeParent; +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.eclipse.ui.dialogs.ErrorFeedback; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.services.IServiceConstants; +import org.eclipse.e4.ui.workbench.modeling.ESelectionService; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.swt.widgets.Display; + +/** + * Delete the selected nodes: both in the JCR repository and in the UI view. + * Warning no check is done, except implementation dependent native checks, + * handle with care. + * + * This handler is still 'hard linked' to a GenericJcrBrowser view to enable + * correct tree refresh when a node is added. This must be corrected in future + * versions. + */ +public class DeleteNodes { + @Execute + public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) { + List selection = (List) selectionService.getSelection(); + if (selection == null) + return; + + JcrBrowserView view = (JcrBrowserView) part.getObject(); + + // confirmation + StringBuffer buf = new StringBuffer(""); + for (Object o : selection) { + SingleJcrNodeElem sjn = (SingleJcrNodeElem) o; + buf.append(sjn.getName()).append(' '); + } + Boolean doRemove = MessageDialog.openConfirm(Display.getCurrent().getActiveShell(), "Confirm deletion", + "Do you want to delete " + buf + "?"); + + // operation + if (doRemove) { + SingleJcrNodeElem ancestor = null; + WorkspaceElem rootAncestor = null; + try { + for (Object obj : selection) { + if (obj instanceof SingleJcrNodeElem) { + // Cache objects + SingleJcrNodeElem sjn = (SingleJcrNodeElem) obj; + TreeParent tp = (TreeParent) sjn.getParent(); + Node node = sjn.getNode(); + + // Jcr Remove + node.remove(); + node.getSession().save(); + // UI remove + tp.removeChild(sjn); + + // Check if the parent is the root node + if (tp instanceof WorkspaceElem) + rootAncestor = (WorkspaceElem) tp; + else + ancestor = getOlder(ancestor, (SingleJcrNodeElem) tp); + } + } + if (rootAncestor != null) + view.nodeRemoved(rootAncestor); + else if (ancestor != null) + view.nodeRemoved(ancestor); + } catch (Exception e) { + ErrorFeedback.show("Cannot delete selected node ", e); + } + } + } + + private SingleJcrNodeElem getOlder(SingleJcrNodeElem A, SingleJcrNodeElem B) { + try { + if (A == null) + return B == null ? null : B; + // Todo enhanced this method + else + return A.getNode().getDepth() <= B.getNode().getDepth() ? A : B; + } catch (RepositoryException re) { + throw new EclipseUiException("Cannot find ancestor", re); + } + } +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/Refresh.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/Refresh.java new file mode 100644 index 0000000..036e70a --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/Refresh.java @@ -0,0 +1,43 @@ +package org.argeo.cms.e4.jcr.handlers; + +import java.util.List; + +import javax.inject.Named; + +import org.argeo.cms.e4.jcr.JcrBrowserView; +import org.argeo.cms.ui.jcr.JcrBrowserUtils; +import org.argeo.cms.ux.widgets.TreeParent; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.services.IServiceConstants; +import org.eclipse.e4.ui.workbench.modeling.EPartService; +import org.eclipse.e4.ui.workbench.modeling.ESelectionService; + +/** + * Force the selected objects of the active view to be refreshed doing the + * following: + *
    + *
  1. The model objects are recomputed
  2. + *
  3. the view is refreshed
  4. + *
+ */ +public class Refresh { + + @Execute + public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, EPartService partService, + ESelectionService selectionService) { + + JcrBrowserView view = (JcrBrowserView) part.getObject(); + List selection = (List) selectionService.getSelection(); + + if (selection != null && !selection.isEmpty()) { + for (Object obj : selection) + if (obj instanceof TreeParent) { + TreeParent tp = (TreeParent) obj; + JcrBrowserUtils.forceRefreshIfNeeded(tp); + view.refresh(obj); + } + } else if (view instanceof JcrBrowserView) + view.refresh(null); // force full refresh + } +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/RenameNode.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/RenameNode.java new file mode 100644 index 0000000..97674ab --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/RenameNode.java @@ -0,0 +1,59 @@ +package org.argeo.cms.e4.jcr.handlers; + +import java.util.List; + +import javax.inject.Named; +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.argeo.cms.e4.jcr.JcrBrowserView; +import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.eclipse.ui.dialogs.SingleValue; +import org.argeo.jcr.JcrUtils; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.services.IServiceConstants; +import org.eclipse.e4.ui.workbench.modeling.EPartService; +import org.eclipse.e4.ui.workbench.modeling.ESelectionService; + +/** + * Canonically call JCR Session#move(String, String) on the first element + * returned by HandlerUtil#getActiveWorkbenchWindow() + * (...getActivePage().getSelection()), if it is a {@link SingleJcrNodeElem}. + * The user must then fill a new name in and confirm + */ +public class RenameNode { + @Execute + public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, EPartService partService, + ESelectionService selectionService) { + List selection = (List) selectionService.getSelection(); + if (selection == null || selection.size() != 1) + return; + JcrBrowserView view = (JcrBrowserView) part.getObject(); + + Object element = selection.get(0); + if (element instanceof SingleJcrNodeElem) { + SingleJcrNodeElem sjn = (SingleJcrNodeElem) element; + Node node = sjn.getNode(); + Session session = null; + String newName = null; + String oldPath = null; + try { + newName = SingleValue.ask("New node name", "Please provide a new name for [" + node.getName() + "]"); + // TODO sanity check and user feedback + newName = JcrUtils.replaceInvalidChars(newName); + oldPath = node.getPath(); + session = node.getSession(); + session.move(oldPath, JcrUtils.parentPath(oldPath) + "/" + newName); + session.save(); + + // Manually refresh the browser view. Must be enhanced + view.refresh(sjn); + } catch (RepositoryException e) { + throw new EclipseUiException("Unable to rename " + node + " to " + newName, e); + } + } + } +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/package-info.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/package-info.java new file mode 100644 index 0000000..4e075e2 --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/handlers/package-info.java @@ -0,0 +1,2 @@ +/** JCR browser handlers. */ +package org.argeo.cms.e4.jcr.handlers; \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/package-info.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/package-info.java new file mode 100644 index 0000000..3e92fb0 --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/jcr/package-info.java @@ -0,0 +1,2 @@ +/** JCR browser perspective. */ +package org.argeo.cms.e4.jcr; \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/AbstractRoleEditor.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/AbstractRoleEditor.java new file mode 100644 index 0000000..137f762 --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/AbstractRoleEditor.java @@ -0,0 +1,287 @@ +package org.argeo.cms.e4.users; + +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.inject.Inject; + +import org.argeo.cms.auth.UserAdminUtils; +import org.argeo.cms.ui.eclipse.forms.AbstractFormPart; +import org.argeo.cms.ui.eclipse.forms.IManagedForm; +import org.argeo.cms.ui.eclipse.forms.ManagedForm; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.util.naming.LdapAttrs; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.e4.ui.di.Persist; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.osgi.service.useradmin.Authorization; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.User; +import org.osgi.service.useradmin.UserAdmin; +import org.osgi.service.useradmin.UserAdminEvent; + +/** Editor for a user, might be a user or a group. */ +public abstract class AbstractRoleEditor { + + // public final static String USER_EDITOR_ID = WorkbenchUiPlugin.PLUGIN_ID + + // ".userEditor"; + // public final static String GROUP_EDITOR_ID = WorkbenchUiPlugin.PLUGIN_ID + + // ".groupEditor"; + + /* DEPENDENCY INJECTION */ + @Inject + protected UserAdminWrapper userAdminWrapper; + + @Inject + private MPart mPart; + + // @Inject + // Composite parent; + + private UserAdmin userAdmin; + + // Context + private User user; + private String username; + + private NameChangeListener listener; + + private ManagedForm managedForm; + + // public void init(IEditorSite site, IEditorInput input) throws + // PartInitException { + @PostConstruct + public void init(Composite parent) { + this.userAdmin = userAdminWrapper.getUserAdmin(); + username = mPart.getPersistedState().get(LdapAttrs.uid.name()); + user = (User) userAdmin.getRole(username); + + listener = new NameChangeListener(Display.getCurrent()); + userAdminWrapper.addListener(listener); + updateEditorTitle(null); + + managedForm = new ManagedForm(parent) { + + @Override + public void staleStateChanged() { + refresh(); + } + }; + ScrolledComposite scrolled = managedForm.getForm(); + Composite body = new Composite(scrolled, SWT.NONE); + scrolled.setContent(body); + createUi(body); + managedForm.refresh(); + } + + abstract void createUi(Composite parent); + + /** + * returns the list of all authorizations for the given user or of the current + * displayed user if parameter is null + */ + protected List getFlatGroups(User aUser) { + Authorization currAuth; + if (aUser == null) + currAuth = userAdmin.getAuthorization(this.user); + else + currAuth = userAdmin.getAuthorization(aUser); + + String[] roles = currAuth.getRoles(); + + List groups = new ArrayList(); + for (String roleStr : roles) { + User currRole = (User) userAdmin.getRole(roleStr); + if (currRole != null && !groups.contains(currRole)) + groups.add(currRole); + } + return groups; + } + + protected IManagedForm getManagedForm() { + return managedForm; + } + + /** Exposes the user (or group) that is displayed by the current editor */ + protected User getDisplayedUser() { + return user; + } + + private void setDisplayedUser(User user) { + this.user = user; + } + + void updateEditorTitle(String title) { + if (title == null) { + String commonName = UserAdminUtils.getProperty(user, LdapAttrs.cn.name()); + title = "".equals(commonName) ? user.getName() : commonName; + } + setPartName(title); + } + + protected void setPartName(String name) { + mPart.setLabel(name); + } + + // protected void addPages() { + // try { + // if (user.getType() == Role.GROUP) + // addPage(new GroupMainPage(this, userAdminWrapper, repository, nodeInstance)); + // else + // addPage(new UserMainPage(this, userAdminWrapper)); + // } catch (Exception e) { + // throw new CmsException("Cannot add pages", e); + // } + // } + + @Persist + public void doSave(IProgressMonitor monitor) { + userAdminWrapper.beginTransactionIfNeeded(); + commitPages(true); + userAdminWrapper.commitOrNotifyTransactionStateChange(); + // firePropertyChange(PROP_DIRTY); + userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_REMOVED, user)); + } + + protected void commitPages(boolean b) { + managedForm.commit(b); + } + + @PreDestroy + public void dispose() { + userAdminWrapper.removeListener(listener); + managedForm.dispose(); + } + + // CONTROLERS FOR THIS EDITOR AND ITS PAGES + + class NameChangeListener extends UiUserAdminListener { + public NameChangeListener(Display display) { + super(display); + } + + @Override + public void roleChangedToUiThread(UserAdminEvent event) { + Role changedRole = event.getRole(); + if (changedRole == null || changedRole.equals(user)) { + updateEditorTitle(null); + User reloadedUser = (User) userAdminWrapper.getUserAdmin().getRole(user.getName()); + setDisplayedUser(reloadedUser); + } + } + } + + class MainInfoListener extends UiUserAdminListener { + private final AbstractFormPart part; + + public MainInfoListener(Display display, AbstractFormPart part) { + super(display); + this.part = part; + } + + @Override + public void roleChangedToUiThread(UserAdminEvent event) { + // Rollback + if (event.getRole() == null) + part.markStale(); + } + } + + class GroupChangeListener extends UiUserAdminListener { + private final AbstractFormPart part; + + public GroupChangeListener(Display display, AbstractFormPart part) { + super(display); + this.part = part; + } + + @Override + public void roleChangedToUiThread(UserAdminEvent event) { + // always mark as stale + part.markStale(); + } + } + + /** Registers a listener that will notify this part */ + class FormPartML implements ModifyListener { + private static final long serialVersionUID = 6299808129505381333L; + private AbstractFormPart formPart; + + public FormPartML(AbstractFormPart generalPart) { + this.formPart = generalPart; + } + + public void modifyText(ModifyEvent e) { + // Discard event when the control does not have the focus, typically + // to avoid all editors being marked as dirty during a Rollback + if (((Control) e.widget).isFocusControl()) + formPart.markDirty(); + } + } + + /* DEPENDENCY INJECTION */ + public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { + this.userAdminWrapper = userAdminWrapper; + } + + /** Creates label and multiline text. */ + Text createLMT(Composite parent, String label, String value) { + Label lbl = new Label(parent, SWT.NONE); + lbl.setText(label); + lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false)); + Text text = new Text(parent, SWT.NONE); + text.setText(value); + text.setLayoutData(new GridData(SWT.LEAD, SWT.FILL, true, true)); + return text; + } + + /** Creates label and password. */ + Text createLP(Composite parent, String label, String value) { + Label lbl = new Label(parent, SWT.NONE); + lbl.setText(label); + lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false)); + Text text = new Text(parent, SWT.PASSWORD | SWT.BORDER); + text.setText(value); + text.setLayoutData(new GridData(SWT.LEAD, SWT.FILL, true, false)); + return text; + } + + /** Creates label and text. */ + Text createLT(Composite parent, String label, String value) { + Label lbl = new Label(parent, SWT.NONE); + lbl.setText(label); + lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false)); + lbl.setFont(EclipseUiUtils.getBoldFont(parent)); + Text text = new Text(parent, SWT.BORDER); + text.setText(value); + text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + // CmsUiUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT); + return text; + } + + Text createReadOnlyLT(Composite parent, String label, String value) { + Label lbl = new Label(parent, SWT.NONE); + lbl.setText(label); + lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false)); + lbl.setFont(EclipseUiUtils.getBoldFont(parent)); + Text text = new Text(parent, SWT.NONE); + text.setText(value); + text.setLayoutData(new GridData(SWT.LEAD, SWT.FILL, true, false)); + text.setEditable(false); + // CmsUiUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT); + return text; + } + +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/CmsWorkbenchStyles.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/CmsWorkbenchStyles.java new file mode 100644 index 0000000..07df312 --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/CmsWorkbenchStyles.java @@ -0,0 +1,8 @@ +package org.argeo.cms.e4.users; + +/** Centralize the declaration of Workbench specific CSS Styles */ +interface CmsWorkbenchStyles { + + // Specific People layouting + String WORKBENCH_FORM_TEXT = "workbench_form_text"; +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/GroupEditor.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/GroupEditor.java new file mode 100644 index 0000000..d54f8bc --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/GroupEditor.java @@ -0,0 +1,566 @@ +package org.argeo.cms.e4.users; + +import static org.argeo.api.cms.CmsContext.WORKGROUP; +import static org.argeo.cms.auth.UserAdminUtils.setProperty; +import static org.argeo.util.naming.LdapAttrs.businessCategory; +import static org.argeo.util.naming.LdapAttrs.description; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.annotation.PreDestroy; +import javax.inject.Inject; +import javax.jcr.Node; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.api.cms.CmsContext; +import org.argeo.cms.auth.UserAdminUtils; +import org.argeo.cms.e4.users.providers.CommonNameLP; +import org.argeo.cms.e4.users.providers.MailLP; +import org.argeo.cms.e4.users.providers.RoleIconLP; +import org.argeo.cms.e4.users.providers.UserFilter; +import org.argeo.cms.jcr.CmsJcrUtils; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ui.eclipse.forms.AbstractFormPart; +import org.argeo.cms.ui.eclipse.forms.IManagedForm; +import org.argeo.eclipse.ui.ColumnDefinition; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.parts.LdifUsersTable; +import org.argeo.jcr.JcrException; +import org.argeo.jcr.JcrUtils; +import org.argeo.util.naming.LdapAttrs; +import org.argeo.util.transaction.WorkTransaction; +import org.eclipse.e4.ui.workbench.modeling.EPartService; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.ToolBarManager; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.ViewerDropAdapter; +import org.eclipse.swt.SWT; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.DropTargetEvent; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.dnd.TransferData; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Link; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.ToolBar; +import org.osgi.service.useradmin.Group; +import org.osgi.service.useradmin.Role; +//import org.eclipse.ui.forms.AbstractFormPart; +//import org.eclipse.ui.forms.IManagedForm; +//import org.eclipse.ui.forms.SectionPart; +//import org.eclipse.ui.forms.editor.FormEditor; +//import org.eclipse.ui.forms.editor.FormPage; +//import org.eclipse.ui.forms.widgets.FormToolkit; +//import org.eclipse.ui.forms.widgets.ScrolledForm; +//import org.eclipse.ui.forms.widgets.Section; +import org.osgi.service.useradmin.User; +import org.osgi.service.useradmin.UserAdmin; +import org.osgi.service.useradmin.UserAdminEvent; + +/** Display/edit main properties of a given group */ +public class GroupEditor extends AbstractRoleEditor { + // final static String ID = "GroupEditor.mainPage"; + + @Inject + private EPartService partService; + + // private final UserEditor editor; + @Inject + private Repository repository; + @Inject + private CmsContext nodeInstance; + // private final UserAdminWrapper userAdminWrapper; + private Session groupsSession; + + // public GroupMainPage(FormEditor editor, UserAdminWrapper userAdminWrapper, + // Repository repository, + // NodeInstance nodeInstance) { + // super(editor, ID, "Main"); + // try { + // session = repository.login(); + // } catch (RepositoryException e) { + // throw new CmsException("Cannot retrieve session of in MainGroupPage + // constructor", e); + // } + // this.editor = (UserEditor) editor; + // this.userAdminWrapper = userAdminWrapper; + // this.nodeInstance = nodeInstance; + // } + + // protected void createFormContent(final IManagedForm mf) { + // ScrolledForm form = mf.getForm(); + // Composite body = form.getBody(); + // GridLayout mainLayout = new GridLayout(); + // body.setLayout(mainLayout); + // Group group = (Group) editor.getDisplayedUser(); + // appendOverviewPart(body, group); + // appendMembersPart(body, group); + // } + + @Override + protected void createUi(Composite parent) { + try { + groupsSession = repository.login(CmsConstants.SRV_WORKSPACE); + } catch (RepositoryException e) { + throw new JcrException("Cannot retrieve session", e); + } + // ScrolledForm form = mf.getForm(); + // Composite body = form.getBody(); + // Composite body = new Composite(parent, SWT.NONE); + Composite body = parent; + GridLayout mainLayout = new GridLayout(); + body.setLayout(mainLayout); + Group group = (Group) getDisplayedUser(); + appendOverviewPart(body, group); + appendMembersPart(body, group); + } + + @PreDestroy + public void dispose() { + JcrUtils.logoutQuietly(groupsSession); + super.dispose(); + } + + /** Creates the general section */ + protected void appendOverviewPart(final Composite parent, final Group group) { + Composite body = new Composite(parent, SWT.NONE); + // GridLayout layout = new GridLayout(5, false); + GridLayout layout = new GridLayout(2, false); + body.setLayout(layout); + body.setLayoutData(CmsSwtUtils.fillWidth()); + + String cn = UserAdminUtils.getProperty(group, LdapAttrs.cn.name()); + createReadOnlyLT(body, "Name", cn); + createReadOnlyLT(body, "DN", group.getName()); + createReadOnlyLT(body, "Domain", UserAdminUtils.getDomainName(group)); + + // Description + Label descLbl = new Label(body, SWT.LEAD); + descLbl.setFont(EclipseUiUtils.getBoldFont(body)); + descLbl.setText("Description"); + descLbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, true, false, 2, 1)); + final Text descTxt = new Text(body, SWT.LEAD | SWT.MULTI | SWT.WRAP | SWT.BORDER); + GridData gd = EclipseUiUtils.fillWidth(); + gd.heightHint = 50; + gd.horizontalSpan = 2; + descTxt.setLayoutData(gd); + + // Mark as workgroup + Link markAsWorkgroupLk = new Link(body, SWT.NONE); + markAsWorkgroupLk.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1)); + + // create form part (controller) + final AbstractFormPart part = new AbstractFormPart() { + + private MainInfoListener listener; + + @Override + public void initialize(IManagedForm form) { + super.initialize(form); + listener = new MainInfoListener(parent.getDisplay(), this); + userAdminWrapper.addListener(listener); + } + + @Override + public void dispose() { + userAdminWrapper.removeListener(listener); + super.dispose(); + } + + public void commit(boolean onSave) { + // group.getProperties().put(LdapAttrs.description.name(), descTxt.getText()); + setProperty(group, description, descTxt.getText()); + super.commit(onSave); + } + + @Override + public void refresh() { + // dnTxt.setText(group.getName()); + // cnTxt.setText(UserAdminUtils.getProperty(group, LdapAttrs.cn.name())); + descTxt.setText(UserAdminUtils.getProperty(group, LdapAttrs.description.name())); + Node workgroupHome = CmsJcrUtils.getGroupHome(groupsSession, cn); + if (workgroupHome == null) + markAsWorkgroupLk.setText("Mark as workgroup"); + else + markAsWorkgroupLk.setText("Configured as workgroup"); + parent.layout(true, true); + super.refresh(); + } + }; + + markAsWorkgroupLk.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = -6439340898096365078L; + + @Override + public void widgetSelected(SelectionEvent e) { + + boolean confirmed = MessageDialog.openConfirm(parent.getShell(), "Mark as workgroup", + "Are you sure you want to mark " + cn + " as being a workgroup? "); + if (confirmed) { + Node workgroupHome = CmsJcrUtils.getGroupHome(groupsSession, cn); + if (workgroupHome != null) + return; // already marked as workgroup, do nothing + else { + // improve transaction management + userAdminWrapper.beginTransactionIfNeeded(); + nodeInstance.createWorkgroup(group.getName()); + setProperty(group, businessCategory, WORKGROUP); + userAdminWrapper.commitOrNotifyTransactionStateChange(); + userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, group)); + part.refresh(); + } + } + } + }); + + ModifyListener defaultListener = new FormPartML(part); + descTxt.addModifyListener(defaultListener); + getManagedForm().addPart(part); + } + + /** Filtered table with members. Has drag and drop ability */ + protected void appendMembersPart(Composite parent, Group group) { + // Section section = tk.createSection(parent, Section.TITLE_BAR); + // section.setText("Members"); + // section.setLayoutData(EclipseUiUtils.fillAll()); + + Composite body = new Composite(parent, SWT.BORDER); + body.setLayout(new GridLayout()); + // section.setClient(body); + body.setLayoutData(EclipseUiUtils.fillAll()); + + // Define the displayed columns + List columnDefs = new ArrayList(); + columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 0, 24)); + columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Name", 150)); + columnDefs.add(new ColumnDefinition(new MailLP(), "Mail", 150)); + // columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", + // 240)); + + // Create and configure the table + LdifUsersTable userViewerCmp = new MyUserTableViewer(body, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL, + userAdminWrapper.getUserAdmin()); + + userViewerCmp.setColumnDefinitions(columnDefs); + userViewerCmp.populate(true, false); + userViewerCmp.setLayoutData(EclipseUiUtils.fillAll()); + + // Controllers + TableViewer userViewer = userViewerCmp.getTableViewer(); + userViewer.addDoubleClickListener(new UserTableDefaultDClickListener(partService)); + int operations = DND.DROP_COPY | DND.DROP_MOVE; + Transfer[] tt = new Transfer[] { TextTransfer.getInstance() }; + userViewer.addDropSupport(operations, tt, + new GroupDropListener(userAdminWrapper, userViewerCmp, (Group) getDisplayedUser())); + + AbstractFormPart part = new GroupMembersPart(userViewerCmp); + getManagedForm().addPart(part); + + // remove button + // addRemoveAbility(toolBarManager, userViewerCmp.getTableViewer(), group); + Action action = new RemoveMembershipAction(userViewer, group, "Remove selected items from this group", + SecurityAdminImages.ICON_REMOVE_DESC); + + ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT); + ToolBar toolBar = toolBarManager.createControl(body); + toolBar.setLayoutData(CmsSwtUtils.fillWidth()); + + toolBarManager.add(action); + toolBarManager.update(true); + + } + + // private LdifUsersTable createMemberPart(Composite parent, Group group) { + // + // // Define the displayed columns + // List columnDefs = new ArrayList(); + // columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 0, 24)); + // columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Name", 150)); + // columnDefs.add(new ColumnDefinition(new MailLP(), "Mail", 150)); + // // columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished + // Name", + // // 240)); + // + // // Create and configure the table + // LdifUsersTable userViewerCmp = new MyUserTableViewer(parent, SWT.MULTI | + // SWT.H_SCROLL | SWT.V_SCROLL, + // userAdminWrapper.getUserAdmin()); + // + // userViewerCmp.setColumnDefinitions(columnDefs); + // userViewerCmp.populate(true, false); + // userViewerCmp.setLayoutData(EclipseUiUtils.fillAll()); + // + // // Controllers + // TableViewer userViewer = userViewerCmp.getTableViewer(); + // userViewer.addDoubleClickListener(new + // UserTableDefaultDClickListener(partService)); + // int operations = DND.DROP_COPY | DND.DROP_MOVE; + // Transfer[] tt = new Transfer[] { TextTransfer.getInstance() }; + // userViewer.addDropSupport(operations, tt, + // new GroupDropListener(userAdminWrapper, userViewerCmp, (Group) + // getDisplayedUser())); + // + // // userViewerCmp.refresh(); + // return userViewerCmp; + // } + + // Local viewers + private class MyUserTableViewer extends LdifUsersTable { + private static final long serialVersionUID = 8467999509931900367L; + + private final UserFilter userFilter; + + public MyUserTableViewer(Composite parent, int style, UserAdmin userAdmin) { + super(parent, style, true); + userFilter = new UserFilter(); + + } + + @Override + protected List listFilteredElements(String filter) { + // reload user and set it in the editor + Group group = (Group) getDisplayedUser(); + Role[] roles = group.getMembers(); + List users = new ArrayList(); + userFilter.setSearchText(filter); + // userFilter.setShowSystemRole(true); + for (Role role : roles) + // if (role.getType() == Role.GROUP) + if (userFilter.select(null, null, role)) + users.add((User) role); + return users; + } + } + + // private void addRemoveAbility(ToolBarManager toolBarManager, TableViewer + // userViewer, Group group) { + // // Section section = sectionPart.getSection(); + // // ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT); + // // ToolBar toolbar = toolBarManager.createControl(parent); + // // ToolBar toolbar = toolBarManager.getControl(); + // // final Cursor handCursor = new Cursor(toolbar.getDisplay(), + // SWT.CURSOR_HAND); + // // toolbar.setCursor(handCursor); + // // toolbar.addDisposeListener(new DisposeListener() { + // // private static final long serialVersionUID = 3882131405820522925L; + // // + // // public void widgetDisposed(DisposeEvent e) { + // // if ((handCursor != null) && (handCursor.isDisposed() == false)) { + // // handCursor.dispose(); + // // } + // // } + // // }); + // + // Action action = new RemoveMembershipAction(userViewer, group, "Remove + // selected items from this group", + // SecurityAdminImages.ICON_REMOVE_DESC); + // toolBarManager.add(action); + // toolBarManager.update(true); + // // section.setTextClient(toolbar); + // } + + private class RemoveMembershipAction extends Action { + private static final long serialVersionUID = -1337713097184522588L; + + private final TableViewer userViewer; + private final Group group; + + RemoveMembershipAction(TableViewer userViewer, Group group, String name, ImageDescriptor img) { + super(name, img); + this.userViewer = userViewer; + this.group = group; + } + + @Override + public void run() { + ISelection selection = userViewer.getSelection(); + if (selection.isEmpty()) + return; + + @SuppressWarnings("unchecked") + Iterator it = ((IStructuredSelection) selection).iterator(); + List users = new ArrayList(); + while (it.hasNext()) { + User currUser = it.next(); + users.add(currUser); + } + + userAdminWrapper.beginTransactionIfNeeded(); + for (User user : users) { + group.removeMember(user); + } + userAdminWrapper.commitOrNotifyTransactionStateChange(); + userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, group)); + } + } + + // LOCAL CONTROLLERS + private class GroupMembersPart extends AbstractFormPart { + private final LdifUsersTable userViewer; + // private final Group group; + + private GroupChangeListener listener; + + public GroupMembersPart(LdifUsersTable userViewer) { + // super(section); + this.userViewer = userViewer; + // this.group = group; + } + + @Override + public void initialize(IManagedForm form) { + super.initialize(form); + listener = new GroupChangeListener(userViewer.getDisplay(), GroupMembersPart.this); + userAdminWrapper.addListener(listener); + } + + @Override + public void dispose() { + userAdminWrapper.removeListener(listener); + super.dispose(); + } + + @Override + public void refresh() { + userViewer.refresh(); + super.refresh(); + } + } + + /** + * Defines this table as being a potential target to add group membership + * (roles) to this group + */ + private class GroupDropListener extends ViewerDropAdapter { + private static final long serialVersionUID = 2893468717831451621L; + + private final UserAdminWrapper userAdminWrapper; + // private final LdifUsersTable myUserViewerCmp; + private final Group myGroup; + + public GroupDropListener(UserAdminWrapper userAdminWrapper, LdifUsersTable userTableViewerCmp, Group group) { + super(userTableViewerCmp.getTableViewer()); + this.userAdminWrapper = userAdminWrapper; + this.myGroup = group; + // this.myUserViewerCmp = userTableViewerCmp; + } + + @Override + public boolean validateDrop(Object target, int operation, TransferData transferType) { + // Target is always OK in a list only view + // TODO check if not a string + boolean validDrop = true; + return validDrop; + } + + @Override + public void drop(DropTargetEvent event) { + // TODO Is there an opportunity to perform the check before? + String newUserName = (String) event.data; + UserAdmin myUserAdmin = userAdminWrapper.getUserAdmin(); + Role role = myUserAdmin.getRole(newUserName); + if (role.getType() == Role.GROUP) { + Group newGroup = (Group) role; + Shell shell = getViewer().getControl().getShell(); + // Sanity checks + if (myGroup == newGroup) { // Equality + MessageDialog.openError(shell, "Forbidden addition ", "A group cannot be a member of itself."); + return; + } + + // Cycle + String myName = myGroup.getName(); + List myMemberships = getFlatGroups(myGroup); + if (myMemberships.contains(newGroup)) { + MessageDialog.openError(shell, "Forbidden addition: cycle", + "Cannot add " + newUserName + " to group " + myName + ". This would create a cycle"); + return; + } + + // Already member + List newGroupMemberships = getFlatGroups(newGroup); + if (newGroupMemberships.contains(myGroup)) { + MessageDialog.openError(shell, "Forbidden addition", + "Cannot add " + newUserName + " to group " + myName + ", this membership already exists"); + return; + } + userAdminWrapper.beginTransactionIfNeeded(); + myGroup.addMember(newGroup); + userAdminWrapper.commitOrNotifyTransactionStateChange(); + userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, myGroup)); + } else if (role.getType() == Role.USER) { + // TODO check if the group is already member of this group + WorkTransaction transaction = userAdminWrapper.beginTransactionIfNeeded(); + User user = (User) role; + myGroup.addMember(user); + if (UserAdminWrapper.COMMIT_ON_SAVE) + try { + transaction.commit(); + } catch (Exception e) { + throw new IllegalStateException( + "Cannot commit transaction " + "after user group membership update", e); + } + userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, myGroup)); + } + super.drop(event); + } + + @Override + public boolean performDrop(Object data) { + // myUserViewerCmp.refresh(); + return true; + } + } + + // LOCAL HELPERS + // private Composite addSection(FormToolkit tk, Composite parent) { + // Section section = tk.createSection(parent, SWT.NO_FOCUS); + // section.setLayoutData(EclipseUiUtils.fillWidth()); + // Composite body = tk.createComposite(section, SWT.WRAP); + // body.setLayoutData(EclipseUiUtils.fillAll()); + // section.setClient(body); + // return body; + // } + + /** Creates label and text. */ + // private Text createLT(Composite parent, String label, String value) { + // FormToolkit toolkit = getManagedForm().getToolkit(); + // Label lbl = toolkit.createLabel(parent, label); + // lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false)); + // lbl.setFont(EclipseUiUtils.getBoldFont(parent)); + // Text text = toolkit.createText(parent, value, SWT.BORDER); + // text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + // CmsUiUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT); + // return text; + // } + // + // Text createReadOnlyLT(Composite parent, String label, String value) { + // FormToolkit toolkit = getManagedForm().getToolkit(); + // Label lbl = toolkit.createLabel(parent, label); + // lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false)); + // lbl.setFont(EclipseUiUtils.getBoldFont(parent)); + // Text text = toolkit.createText(parent, value, SWT.NONE); + // text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + // text.setEditable(false); + // CmsUiUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT); + // return text; + // } + +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/GroupsView.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/GroupsView.java new file mode 100644 index 0000000..73e4f5d --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/GroupsView.java @@ -0,0 +1,251 @@ +package org.argeo.cms.e4.users; + +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.inject.Inject; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.api.cms.CmsLog; +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.e4.users.providers.CommonNameLP; +import org.argeo.cms.e4.users.providers.DomainNameLP; +import org.argeo.cms.e4.users.providers.RoleIconLP; +import org.argeo.cms.e4.users.providers.UserDragListener; +import org.argeo.cms.swt.CmsException; +//import org.argeo.cms.ui.workbench.WorkbenchUiPlugin; +//import org.argeo.cms.ui.workbench.internal.useradmin.UiUserAdminListener; +//import org.argeo.cms.ui.workbench.internal.useradmin.UserAdminWrapper; +//import org.argeo.cms.ui.workbench.internal.useradmin.providers.CommonNameLP; +//import org.argeo.cms.ui.workbench.internal.useradmin.providers.DomainNameLP; +//import org.argeo.cms.ui.workbench.internal.useradmin.providers.RoleIconLP; +//import org.argeo.cms.ui.workbench.internal.useradmin.providers.UserDragListener; +//import org.argeo.cms.ui.workbench.internal.useradmin.providers.UserTableDefaultDClickListener; +import org.argeo.eclipse.ui.ColumnDefinition; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.parts.LdifUsersTable; +import org.argeo.util.naming.LdapAttrs; +import org.argeo.util.naming.LdapObjs; +import org.eclipse.e4.ui.di.Focus; +import org.eclipse.e4.ui.workbench.modeling.EPartService; +import org.eclipse.e4.ui.workbench.modeling.ESelectionService; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +//import org.eclipse.ui.part.ViewPart; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.User; +import org.osgi.service.useradmin.UserAdminEvent; +import org.osgi.service.useradmin.UserAdminListener; + +/** List all groups with filter */ +public class GroupsView { + private final static CmsLog log = CmsLog.getLog(GroupsView.class); + // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".groupsView"; + + @Inject + private EPartService partService; + @Inject + private UserAdminWrapper userAdminWrapper; + + // UI Objects + private LdifUsersTable groupTableViewerCmp; + private TableViewer userViewer; + private List columnDefs = new ArrayList(); + + private UserAdminListener listener; + + @PostConstruct + public void createPartControl(Composite parent, ESelectionService selectionService) { + parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); + + // boolean isAdmin = CurrentUser.isInRole(NodeConstants.ROLE_ADMIN); + + // Define the displayed columns + columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 19)); + columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Name", 150)); + columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 100)); + // Only show technical DN to admin + // if (isAdmin) + // columnDefs.add(new ColumnDefinition(new UserNameLP(), + // "Distinguished Name", 300)); + + // Create and configure the table + groupTableViewerCmp = new MyUserTableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); + + groupTableViewerCmp.setColumnDefinitions(columnDefs); + // if (isAdmin) + // groupTableViewerCmp.populateWithStaticFilters(false, false); + // else + groupTableViewerCmp.populate(true, false); + + groupTableViewerCmp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + // Links + userViewer = groupTableViewerCmp.getTableViewer(); + userViewer.addDoubleClickListener(new UserTableDefaultDClickListener(partService)); + // getViewSite().setSelectionProvider(userViewer); + userViewer.addSelectionChangedListener(new ISelectionChangedListener() { + + @Override + public void selectionChanged(SelectionChangedEvent event) { + IStructuredSelection selection = (IStructuredSelection) event.getSelection(); + selectionService.setSelection(selection.toList()); + } + }); + + // Really? + groupTableViewerCmp.refresh(); + + // Drag and drop + int operations = DND.DROP_COPY | DND.DROP_MOVE; + Transfer[] tt = new Transfer[] { TextTransfer.getInstance() }; + userViewer.addDragSupport(operations, tt, new UserDragListener(userViewer)); + + // // Register a useradmin listener + // listener = new UserAdminListener() { + // @Override + // public void roleChanged(UserAdminEvent event) { + // if (userViewer != null && !userViewer.getTable().isDisposed()) + // refresh(); + // } + // }; + // userAdminWrapper.addListener(listener); + // } + + // Register a useradmin listener + listener = new MyUiUAListener(parent.getDisplay()); + userAdminWrapper.addListener(listener); + } + + private class MyUiUAListener extends UiUserAdminListener { + public MyUiUAListener(Display display) { + super(display); + } + + @Override + public void roleChangedToUiThread(UserAdminEvent event) { + if (userViewer != null && !userViewer.getTable().isDisposed()) + refresh(); + } + } + + private class MyUserTableViewer extends LdifUsersTable { + private static final long serialVersionUID = 8467999509931900367L; + + private boolean showSystemRoles = true; + + private final String[] knownProps = { LdapAttrs.uid.name(), LdapAttrs.cn.name(), LdapAttrs.DN }; + + public MyUserTableViewer(Composite parent, int style) { + super(parent, style); + showSystemRoles = CurrentUser.isInRole(CmsConstants.ROLE_ADMIN); + } + + protected void populateStaticFilters(Composite staticFilterCmp) { + staticFilterCmp.setLayout(new GridLayout()); + final Button showSystemRoleBtn = new Button(staticFilterCmp, SWT.CHECK); + showSystemRoleBtn.setText("Show system roles"); + showSystemRoles = CurrentUser.isInRole(CmsConstants.ROLE_ADMIN); + showSystemRoleBtn.setSelection(showSystemRoles); + + showSystemRoleBtn.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = -7033424592697691676L; + + @Override + public void widgetSelected(SelectionEvent e) { + showSystemRoles = showSystemRoleBtn.getSelection(); + refresh(); + } + + }); + } + + @Override + protected List listFilteredElements(String filter) { + Role[] roles; + try { + StringBuilder builder = new StringBuilder(); + StringBuilder tmpBuilder = new StringBuilder(); + if (EclipseUiUtils.notEmpty(filter)) + for (String prop : knownProps) { + tmpBuilder.append("("); + tmpBuilder.append(prop); + tmpBuilder.append("=*"); + tmpBuilder.append(filter); + tmpBuilder.append("*)"); + } + if (tmpBuilder.length() > 1) { + builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=") + .append(LdapObjs.groupOfNames.name()).append(")"); + // hide tokens + builder.append("(!(").append(LdapAttrs.DN).append("=*").append(CmsConstants.TOKENS_BASEDN) + .append("))"); + + if (!showSystemRoles) + builder.append("(!(").append(LdapAttrs.DN).append("=*").append(CmsConstants.SYSTEM_ROLES_BASEDN) + .append("))"); + builder.append("(|"); + builder.append(tmpBuilder.toString()); + builder.append("))"); + } else { + if (!showSystemRoles) + builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=") + .append(LdapObjs.groupOfNames.name()).append(")(!(").append(LdapAttrs.DN).append("=*") + .append(CmsConstants.SYSTEM_ROLES_BASEDN).append("))(!(").append(LdapAttrs.DN).append("=*") + .append(CmsConstants.TOKENS_BASEDN).append(")))"); + else + builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=") + .append(LdapObjs.groupOfNames.name()).append(")(!(").append(LdapAttrs.DN).append("=*") + .append(CmsConstants.TOKENS_BASEDN).append(")))"); + + } + roles = userAdminWrapper.getUserAdmin().getRoles(builder.toString()); + } catch (InvalidSyntaxException e) { + throw new CmsException("Unable to get roles with filter: " + filter, e); + } + List users = new ArrayList(); + for (Role role : roles) + if (!users.contains(role)) + users.add((User) role); + else + log.warn("Duplicated role: " + role); + + return users; + } + } + + public void refresh() { + groupTableViewerCmp.refresh(); + } + + @PreDestroy + public void dispose() { + userAdminWrapper.removeListener(listener); + } + + @Focus + public void setFocus() { + groupTableViewerCmp.setFocus(); + } + + /* DEPENDENCY INJECTION */ + public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { + this.userAdminWrapper = userAdminWrapper; + } +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/SecurityAdminImages.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/SecurityAdminImages.java new file mode 100644 index 0000000..7bbe3c7 --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/SecurityAdminImages.java @@ -0,0 +1,19 @@ +package org.argeo.cms.e4.users; + +import org.argeo.cms.ui.theme.CmsImages; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.graphics.Image; + +/** Shared icons that must be declared programmatically . */ +public class SecurityAdminImages extends CmsImages { + private final static String PREFIX = "icons/"; + + public final static ImageDescriptor ICON_REMOVE_DESC = createDesc(PREFIX + "delete.png"); + public final static ImageDescriptor ICON_USER_DESC = createDesc(PREFIX + "person.png"); + + public final static Image ICON_USER = ICON_USER_DESC.createImage(); + public final static Image ICON_GROUP = createImg(PREFIX + "group.png"); + public final static Image ICON_WORKGROUP = createImg(PREFIX + "workgroup.png"); + public final static Image ICON_ROLE = createImg(PREFIX + "role.gif"); + +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UiAdminUtils.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UiAdminUtils.java new file mode 100644 index 0000000..fb48a47 --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UiAdminUtils.java @@ -0,0 +1,34 @@ +package org.argeo.cms.e4.users; + +import org.argeo.util.transaction.WorkTransaction; + +/** First effort to centralize back end methods used by the user admin UI */ +public class UiAdminUtils { + /* + * INTERNAL METHODS: Below methods are meant to stay here and are not part + * of a potential generic backend to manage the useradmin + */ + /** Easily notify the ActiveWindow that the transaction had a state change */ + public final static void notifyTransactionStateChange( + WorkTransaction userTransaction) { +// try { +// IWorkbenchWindow aww = PlatformUI.getWorkbench() +// .getActiveWorkbenchWindow(); +// ISourceProviderService sourceProviderService = (ISourceProviderService) aww +// .getService(ISourceProviderService.class); +// UserTransactionProvider esp = (UserTransactionProvider) sourceProviderService +// .getSourceProvider(UserTransactionProvider.TRANSACTION_STATE); +// esp.fireTransactionStateChange(); +// } catch (Exception e) { +// throw new CmsException("Unable to begin transaction", e); +// } + } + + /** + * Email addresses must match this regexp pattern ({@value #EMAIL_PATTERN}. + * Thanks to this tip. + */ + public final static String EMAIL_PATTERN = "^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$"; +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UiUserAdminListener.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UiUserAdminListener.java new file mode 100644 index 0000000..eb64aba --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UiUserAdminListener.java @@ -0,0 +1,27 @@ +package org.argeo.cms.e4.users; + +import org.eclipse.swt.widgets.Display; +import org.osgi.service.useradmin.UserAdminEvent; +import org.osgi.service.useradmin.UserAdminListener; + +/** Convenience class to insure the call to refresh is done in the UI thread */ +public abstract class UiUserAdminListener implements UserAdminListener { + + private final Display display; + + public UiUserAdminListener(Display display) { + this.display = display; + } + + @Override + public void roleChanged(final UserAdminEvent event) { + display.asyncExec(new Runnable() { + @Override + public void run() { + roleChangedToUiThread(event); + } + }); + } + + public abstract void roleChangedToUiThread(UserAdminEvent event); +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java new file mode 100644 index 0000000..dbb629c --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java @@ -0,0 +1,153 @@ +package org.argeo.cms.e4.users; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.cms.swt.CmsException; +import org.argeo.osgi.useradmin.UserDirectory; +import org.argeo.util.directory.DirectoryConf; +import org.argeo.util.transaction.WorkTransaction; +import org.osgi.service.useradmin.UserAdmin; +import org.osgi.service.useradmin.UserAdminEvent; +import org.osgi.service.useradmin.UserAdminListener; + +/** Centralise interaction with the UserAdmin in this bundle */ +public class UserAdminWrapper { + + private UserAdmin userAdmin; + // private ServiceReference userAdminServiceReference; +// private Set uris; + private Map> userDirectories = Collections + .synchronizedMap(new LinkedHashMap<>()); + private WorkTransaction userTransaction; + + // First effort to simplify UX while managing users and groups + public final static boolean COMMIT_ON_SAVE = true; + + // Registered listeners + List listeners = new ArrayList(); + + /** + * Starts a transaction if necessary. Should always been called together with + * {@link UserAdminWrapper#commitOrNotifyTransactionStateChange()} once the + * security model changes have been performed. + */ + public WorkTransaction beginTransactionIfNeeded() { + try { + // UserTransaction userTransaction = getUserTransaction(); + if (userTransaction.isNoTransactionStatus()) { + userTransaction.begin(); + // UiAdminUtils.notifyTransactionStateChange(userTransaction); + } + return userTransaction; + } catch (Exception e) { + throw new CmsException("Unable to begin transaction", e); + } + } + + /** + * Depending on the current application configuration, it will either commit the + * current transaction or throw a notification that the transaction state has + * changed (In the later case, it must be called from the UI thread). + */ + public void commitOrNotifyTransactionStateChange() { + try { + // UserTransaction userTransaction = getUserTransaction(); + if (userTransaction.isNoTransactionStatus()) + return; + + if (UserAdminWrapper.COMMIT_ON_SAVE) + userTransaction.commit(); + else + UiAdminUtils.notifyTransactionStateChange(userTransaction); + } catch (Exception e) { + throw new CmsException("Unable to clean transaction", e); + } + } + + // TODO implement safer mechanism + public void addListener(UserAdminListener userAdminListener) { + if (!listeners.contains(userAdminListener)) + listeners.add(userAdminListener); + } + + public void removeListener(UserAdminListener userAdminListener) { + if (listeners.contains(userAdminListener)) + listeners.remove(userAdminListener); + } + + public void notifyListeners(UserAdminEvent event) { + for (UserAdminListener listener : listeners) + listener.roleChanged(event); + } + + public Map getKnownBaseDns(boolean onlyWritable) { + Map dns = new HashMap(); + for (UserDirectory userDirectory : userDirectories.keySet()) { + Boolean readOnly = userDirectory.isReadOnly(); + String baseDn = userDirectory.getBase(); + + if (onlyWritable && readOnly) + continue; + if (baseDn.equalsIgnoreCase(CmsConstants.SYSTEM_ROLES_BASEDN)) + continue; + if (baseDn.equalsIgnoreCase(CmsConstants.TOKENS_BASEDN)) + continue; + dns.put(baseDn, DirectoryConf.propertiesAsUri(userDirectories.get(userDirectory)).toString()); + + } +// for (String uri : uris) { +// if (!uri.startsWith("/")) +// continue; +// Dictionary props = UserAdminConf.uriAsProperties(uri); +// String readOnly = UserAdminConf.readOnly.getValue(props); +// String baseDn = UserAdminConf.baseDn.getValue(props); +// +// if (onlyWritable && "true".equals(readOnly)) +// continue; +// if (baseDn.equalsIgnoreCase(NodeConstants.ROLES_BASEDN)) +// continue; +// if (baseDn.equalsIgnoreCase(NodeConstants.TOKENS_BASEDN)) +// continue; +// dns.put(baseDn, uri); +// } + return dns; + } + + public UserAdmin getUserAdmin() { + return userAdmin; + } + + public WorkTransaction getUserTransaction() { + return userTransaction; + } + + /* DEPENDENCY INJECTION */ + public void setUserAdmin(UserAdmin userAdmin, Map properties) { + this.userAdmin = userAdmin; +// this.uris = Collections.unmodifiableSortedSet(new TreeSet<>(properties.keySet())); + } + + public void setUserTransaction(WorkTransaction userTransaction) { + this.userTransaction = userTransaction; + } + + public void addUserDirectory(UserDirectory userDirectory, Map properties) { + userDirectories.put(userDirectory, new Hashtable<>(properties)); + } + + public void removeUserDirectory(UserDirectory userDirectory, Map properties) { + userDirectories.remove(userDirectory); + } + + // public void setUserAdminServiceReference( + // ServiceReference userAdminServiceReference) { + // this.userAdminServiceReference = userAdminServiceReference; + // } +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UserBatchUpdateWizard.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UserBatchUpdateWizard.java new file mode 100644 index 0000000..4fc59d3 --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UserBatchUpdateWizard.java @@ -0,0 +1,606 @@ +package org.argeo.cms.e4.users; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.api.cms.CmsLog; +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.auth.UserAdminUtils; +import org.argeo.cms.e4.users.providers.CommonNameLP; +import org.argeo.cms.e4.users.providers.DomainNameLP; +import org.argeo.cms.e4.users.providers.MailLP; +import org.argeo.cms.e4.users.providers.UserNameLP; +import org.argeo.cms.swt.CmsException; +import org.argeo.eclipse.ui.ColumnDefinition; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.parts.LdifUsersTable; +import org.argeo.util.naming.LdapAttrs; +import org.argeo.util.naming.LdapObjs; +import org.argeo.util.transaction.WorkTransaction; +import org.eclipse.jface.dialogs.IPageChangeProvider; +import org.eclipse.jface.dialogs.IPageChangedListener; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.dialogs.PageChangedEvent; +import org.eclipse.jface.wizard.IWizardContainer; +import org.eclipse.jface.wizard.Wizard; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Text; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.User; +import org.osgi.service.useradmin.UserAdminEvent; + +/** Wizard to update users */ +public class UserBatchUpdateWizard extends Wizard { + + private final static CmsLog log = CmsLog.getLog(UserBatchUpdateWizard.class); + private UserAdminWrapper userAdminWrapper; + + // pages + private ChooseCommandWizardPage chooseCommandPage; + private ChooseUsersWizardPage userListPage; + private ValidateAndLaunchWizardPage validatePage; + + // Various implemented commands keys + private final static String CMD_UPDATE_PASSWORD = "resetPassword"; + private final static String CMD_UPDATE_EMAIL = "resetEmail"; + private final static String CMD_GROUP_MEMBERSHIP = "groupMembership"; + + private final Map commands = new HashMap() { + private static final long serialVersionUID = 1L; + { + put("Reset password(s)", CMD_UPDATE_PASSWORD); + put("Reset email(s)", CMD_UPDATE_EMAIL); + // TODO implement role / group management + // put("Add/Remove from group", CMD_GROUP_MEMBERSHIP); + } + }; + + public UserBatchUpdateWizard(UserAdminWrapper userAdminWrapper) { + this.userAdminWrapper = userAdminWrapper; + } + + @Override + public void addPages() { + chooseCommandPage = new ChooseCommandWizardPage(); + addPage(chooseCommandPage); + userListPage = new ChooseUsersWizardPage(); + addPage(userListPage); + validatePage = new ValidateAndLaunchWizardPage(); + addPage(validatePage); + } + + @Override + public boolean performFinish() { + if (!canFinish()) + return false; + WorkTransaction ut = userAdminWrapper.getUserTransaction(); + if (!ut.isNoTransactionStatus() && !MessageDialog.openConfirm(getShell(), "Existing Transaction", + "A user transaction is already existing, " + "are you sure you want to proceed ?")) + return false; + + // We cannot use jobs, user modifications are still meant to be done in + // the UIThread + // UpdateJob job = null; + // if (job != null) + // job.schedule(); + + if (CMD_UPDATE_PASSWORD.equals(chooseCommandPage.getCommand())) { + char[] newValue = chooseCommandPage.getPwdValue(); + if (newValue == null) + throw new CmsException("Password cannot be null or an empty string"); + ResetPassword job = new ResetPassword(userAdminWrapper, userListPage.getSelectedUsers(), newValue); + job.doUpdate(); + } else if (CMD_UPDATE_EMAIL.equals(chooseCommandPage.getCommand())) { + String newValue = chooseCommandPage.getEmailValue(); + if (newValue == null) + throw new CmsException("Password cannot be null or an empty string"); + ResetEmail job = new ResetEmail(userAdminWrapper, userListPage.getSelectedUsers(), newValue); + job.doUpdate(); + } + return true; + } + + public boolean canFinish() { + if (this.getContainer().getCurrentPage() == validatePage) + return true; + return false; + } + + private class ResetPassword { + private char[] newPwd; + private UserAdminWrapper userAdminWrapper; + private List usersToUpdate; + + public ResetPassword(UserAdminWrapper userAdminWrapper, List usersToUpdate, char[] newPwd) { + this.newPwd = newPwd; + this.usersToUpdate = usersToUpdate; + this.userAdminWrapper = userAdminWrapper; + } + + @SuppressWarnings("unchecked") + protected void doUpdate() { + userAdminWrapper.beginTransactionIfNeeded(); + try { + for (User user : usersToUpdate) { + // the char array is emptied after being used. + user.getCredentials().put(null, newPwd.clone()); + } + userAdminWrapper.commitOrNotifyTransactionStateChange(); + } catch (Exception e) { + throw new CmsException("Cannot perform batch update on users", e); + } finally { + WorkTransaction ut = userAdminWrapper.getUserTransaction(); + if (!ut.isNoTransactionStatus()) + ut.rollback(); + } + } + } + + private class ResetEmail { + private String newEmail; + private UserAdminWrapper userAdminWrapper; + private List usersToUpdate; + + public ResetEmail(UserAdminWrapper userAdminWrapper, List usersToUpdate, String newEmail) { + this.newEmail = newEmail; + this.usersToUpdate = usersToUpdate; + this.userAdminWrapper = userAdminWrapper; + } + + @SuppressWarnings("unchecked") + protected void doUpdate() { + userAdminWrapper.beginTransactionIfNeeded(); + try { + for (User user : usersToUpdate) { + // the char array is emptied after being used. + user.getProperties().put(LdapAttrs.mail.name(), newEmail); + } + + userAdminWrapper.commitOrNotifyTransactionStateChange(); + if (!usersToUpdate.isEmpty()) + userAdminWrapper.notifyListeners( + new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, usersToUpdate.get(0))); + } catch (Exception e) { + throw new CmsException("Cannot perform batch update on users", e); + } finally { + WorkTransaction ut = userAdminWrapper.getUserTransaction(); + if (!ut.isNoTransactionStatus()) + ut.rollback(); + } + } + } + + // @SuppressWarnings("unused") + // private class AddToGroup extends UpdateJob { + // private String groupID; + // private Session session; + // + // public AddToGroup(Session session, List nodesToUpdate, + // String groupID) { + // super(session, nodesToUpdate); + // this.session = session; + // this.groupID = groupID; + // } + // + // protected void doUpdate(Node node) { + // log.info("Add/Remove to group actions are not yet implemented"); + // // TODO implement this + // // try { + // // throw new CmsException("Not yet implemented"); + // // } catch (RepositoryException re) { + // // throw new CmsException( + // // "Unable to update boolean value for node " + node, re); + // // } + // } + // } + + // /** + // * Base privileged job that will be run asynchronously to perform the + // batch + // * update + // */ + // private abstract class UpdateJob extends PrivilegedJob { + // + // private final UserAdminWrapper userAdminWrapper; + // private final List usersToUpdate; + // + // protected abstract void doUpdate(User user); + // + // public UpdateJob(UserAdminWrapper userAdminWrapper, + // List usersToUpdate) { + // super("Perform update"); + // this.usersToUpdate = usersToUpdate; + // this.userAdminWrapper = userAdminWrapper; + // } + // + // @Override + // protected IStatus doRun(IProgressMonitor progressMonitor) { + // try { + // JcrMonitor monitor = new EclipseJcrMonitor(progressMonitor); + // int total = usersToUpdate.size(); + // monitor.beginTask("Performing change", total); + // userAdminWrapper.beginTransactionIfNeeded(); + // for (User user : usersToUpdate) { + // doUpdate(user); + // monitor.worked(1); + // } + // userAdminWrapper.getUserTransaction().commit(); + // } catch (Exception e) { + // throw new CmsException( + // "Cannot perform batch update on users", e); + // } finally { + // UserTransaction ut = userAdminWrapper.getUserTransaction(); + // try { + // if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION) + // ut.rollback(); + // } catch (IllegalStateException | SecurityException + // | SystemException e) { + // log.error("Unable to rollback session in 'finally', " + // + "the system might be in a dirty state"); + // e.printStackTrace(); + // } + // } + // return Status.OK_STATUS; + // } + // } + + // PAGES + /** + * Displays a combo box that enables user to choose which action to perform + */ + private class ChooseCommandWizardPage extends WizardPage { + private static final long serialVersionUID = -8069434295293996633L; + private Combo chooseCommandCmb; + private Button trueChk; + private Text valueTxt; + private Text pwdTxt; + private Text pwd2Txt; + + public ChooseCommandWizardPage() { + super("Choose a command to run."); + setTitle("Choose a command to run."); + } + + @Override + public void createControl(Composite parent) { + GridLayout gl = new GridLayout(); + Composite container = new Composite(parent, SWT.NO_FOCUS); + container.setLayout(gl); + + chooseCommandCmb = new Combo(container, SWT.READ_ONLY); + chooseCommandCmb.setLayoutData(EclipseUiUtils.fillWidth()); + String[] values = commands.keySet().toArray(new String[0]); + chooseCommandCmb.setItems(values); + + final Composite bottomPart = new Composite(container, SWT.NO_FOCUS); + bottomPart.setLayoutData(EclipseUiUtils.fillAll()); + bottomPart.setLayout(EclipseUiUtils.noSpaceGridLayout()); + + chooseCommandCmb.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = 1L; + + @Override + public void widgetSelected(SelectionEvent e) { + if (getCommand().equals(CMD_UPDATE_PASSWORD)) + populatePasswordCmp(bottomPart); + else if (getCommand().equals(CMD_UPDATE_EMAIL)) + populateEmailCmp(bottomPart); + else if (getCommand().equals(CMD_GROUP_MEMBERSHIP)) + populateGroupCmp(bottomPart); + else + populateBooleanFlagCmp(bottomPart); + checkPageComplete(); + bottomPart.layout(true, true); + } + }); + setControl(container); + } + + private void populateBooleanFlagCmp(Composite parent) { + EclipseUiUtils.clear(parent); + trueChk = new Button(parent, SWT.CHECK); + trueChk.setText("Do it. (It will to the contrary if unchecked)"); + trueChk.setSelection(true); + trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false)); + } + + private void populatePasswordCmp(Composite parent) { + EclipseUiUtils.clear(parent); + Composite body = new Composite(parent, SWT.NO_FOCUS); + + ModifyListener ml = new ModifyListener() { + private static final long serialVersionUID = -1558726363536729634L; + + @Override + public void modifyText(ModifyEvent event) { + checkPageComplete(); + } + }; + + body.setLayout(new GridLayout(2, false)); + body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + pwdTxt = EclipseUiUtils.createGridLP(body, "New password", ml); + pwd2Txt = EclipseUiUtils.createGridLP(body, "Repeat password", ml); + } + + private void populateEmailCmp(Composite parent) { + EclipseUiUtils.clear(parent); + Composite body = new Composite(parent, SWT.NO_FOCUS); + + ModifyListener ml = new ModifyListener() { + private static final long serialVersionUID = 2147704227294268317L; + + @Override + public void modifyText(ModifyEvent event) { + checkPageComplete(); + } + }; + + body.setLayout(new GridLayout(2, false)); + body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + valueTxt = EclipseUiUtils.createGridLT(body, "New e-mail", ml); + } + + private void checkPageComplete() { + String errorMsg = null; + if (chooseCommandCmb.getSelectionIndex() < 0) + errorMsg = "Please select an action"; + else if (CMD_UPDATE_EMAIL.equals(getCommand())) { + if (!valueTxt.getText().matches(UiAdminUtils.EMAIL_PATTERN)) + errorMsg = "Not a valid e-mail address"; + } else if (CMD_UPDATE_PASSWORD.equals(getCommand())) { + if (EclipseUiUtils.isEmpty(pwdTxt.getText()) || pwdTxt.getText().length() < 4) + errorMsg = "Please enter a password that is at least 4 character long"; + else if (!pwdTxt.getText().equals(pwd2Txt.getText())) + errorMsg = "Passwords are different"; + } + if (EclipseUiUtils.notEmpty(errorMsg)) { + setMessage(errorMsg, WizardPage.ERROR); + setPageComplete(false); + } else { + setMessage("Page complete, you can proceed to user choice", WizardPage.INFORMATION); + setPageComplete(true); + } + + getContainer().updateButtons(); + } + + private void populateGroupCmp(Composite parent) { + EclipseUiUtils.clear(parent); + trueChk = new Button(parent, SWT.CHECK); + trueChk.setText("Add to group. (It will remove user(s) from the " + "corresponding group if unchecked)"); + trueChk.setSelection(true); + trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false)); + } + + protected String getCommand() { + return commands.get(chooseCommandCmb.getItem(chooseCommandCmb.getSelectionIndex())); + } + + protected String getCommandLbl() { + return chooseCommandCmb.getItem(chooseCommandCmb.getSelectionIndex()); + } + + @SuppressWarnings("unused") + protected boolean getBoleanValue() { + // FIXME this is not consistent and will lead to errors. + if ("argeo:enabled".equals(getCommand())) + return trueChk.getSelection(); + else + return !trueChk.getSelection(); + } + + @SuppressWarnings("unused") + protected String getStringValue() { + String value = null; + if (valueTxt != null) { + value = valueTxt.getText(); + if ("".equals(value.trim())) + value = null; + } + return value; + } + + protected char[] getPwdValue() { + // We do not directly reset the password text fields: There is no + // need to over secure this process: setting a pwd to multi users + // at the same time is anyhow a bad practice and should be used only + // in test environment or for temporary access + if (pwdTxt == null || pwdTxt.isDisposed()) + return null; + else + return pwdTxt.getText().toCharArray(); + } + + protected String getEmailValue() { + // We do not directly reset the password text fields: There is no + // need to over secure this process: setting a pwd to multi users + // at the same time is anyhow a bad practice and should be used only + // in test environment or for temporary access + if (valueTxt == null || valueTxt.isDisposed()) + return null; + else + return valueTxt.getText(); + } + } + + /** + * Displays a list of users with a check box to be able to choose some of them + */ + private class ChooseUsersWizardPage extends WizardPage implements IPageChangedListener { + private static final long serialVersionUID = 7651807402211214274L; + private ChooseUserTableViewer userTableCmp; + + public ChooseUsersWizardPage() { + super("Choose Users"); + setTitle("Select users who will be impacted"); + } + + @Override + public void createControl(Composite parent) { + Composite pageCmp = new Composite(parent, SWT.NONE); + pageCmp.setLayout(EclipseUiUtils.noSpaceGridLayout()); + + // Define the displayed columns + List columnDefs = new ArrayList(); + columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name", 150)); + columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150)); + columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200)); + + // Only show technical DN to admin + if (CurrentUser.isInRole(CmsConstants.ROLE_ADMIN)) + columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", 300)); + + userTableCmp = new ChooseUserTableViewer(pageCmp, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); + userTableCmp.setLayoutData(EclipseUiUtils.fillAll()); + userTableCmp.setColumnDefinitions(columnDefs); + userTableCmp.populate(true, true); + userTableCmp.refresh(); + + setControl(pageCmp); + + // Add listener to update message when shown + final IWizardContainer wContainer = this.getContainer(); + if (wContainer instanceof IPageChangeProvider) { + ((IPageChangeProvider) wContainer).addPageChangedListener(this); + } + + } + + @Override + public void pageChanged(PageChangedEvent event) { + if (event.getSelectedPage() == this) { + String msg = "Chosen batch action: " + chooseCommandPage.getCommandLbl(); + ((WizardPage) event.getSelectedPage()).setMessage(msg); + } + } + + protected List getSelectedUsers() { + return userTableCmp.getSelectedUsers(); + } + + private class ChooseUserTableViewer extends LdifUsersTable { + private static final long serialVersionUID = 5080437561015853124L; + private final String[] knownProps = { LdapAttrs.uid.name(), LdapAttrs.DN, LdapAttrs.cn.name(), + LdapAttrs.givenName.name(), LdapAttrs.sn.name(), LdapAttrs.mail.name() }; + + public ChooseUserTableViewer(Composite parent, int style) { + super(parent, style); + } + + @Override + protected List listFilteredElements(String filter) { + Role[] roles; + + try { + StringBuilder builder = new StringBuilder(); + + StringBuilder tmpBuilder = new StringBuilder(); + if (EclipseUiUtils.notEmpty(filter)) + for (String prop : knownProps) { + tmpBuilder.append("("); + tmpBuilder.append(prop); + tmpBuilder.append("=*"); + tmpBuilder.append(filter); + tmpBuilder.append("*)"); + } + if (tmpBuilder.length() > 1) { + builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=") + .append(LdapObjs.inetOrgPerson.name()).append(")(|"); + builder.append(tmpBuilder.toString()); + builder.append("))"); + } else + builder.append("(").append(LdapAttrs.objectClass.name()).append("=") + .append(LdapObjs.inetOrgPerson.name()).append(")"); + roles = userAdminWrapper.getUserAdmin().getRoles(builder.toString()); + } catch (InvalidSyntaxException e) { + throw new CmsException("Unable to get roles with filter: " + filter, e); + } + List users = new ArrayList(); + for (Role role : roles) + // Prevent current logged in user to perform batch on + // himself + if (!UserAdminUtils.isCurrentUser((User) role)) + users.add((User) role); + return users; + } + } + } + + /** Summary of input data before launching the process */ + private class ValidateAndLaunchWizardPage extends WizardPage implements IPageChangedListener { + private static final long serialVersionUID = 7098918351451743853L; + private ChosenUsersTableViewer userTableCmp; + + public ValidateAndLaunchWizardPage() { + super("Validate and launch"); + setTitle("Validate and launch"); + } + + @Override + public void createControl(Composite parent) { + Composite pageCmp = new Composite(parent, SWT.NO_FOCUS); + pageCmp.setLayout(EclipseUiUtils.noSpaceGridLayout()); + + List columnDefs = new ArrayList(); + columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name", 150)); + columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150)); + columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200)); + // Only show technical DN to admin + if (CurrentUser.isInRole(CmsConstants.ROLE_ADMIN)) + columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", 300)); + userTableCmp = new ChosenUsersTableViewer(pageCmp, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); + userTableCmp.setLayoutData(EclipseUiUtils.fillAll()); + userTableCmp.setColumnDefinitions(columnDefs); + userTableCmp.populate(false, false); + userTableCmp.refresh(); + setControl(pageCmp); + // Add listener to update message when shown + final IWizardContainer wContainer = this.getContainer(); + if (wContainer instanceof IPageChangeProvider) { + ((IPageChangeProvider) wContainer).addPageChangedListener(this); + } + } + + @Override + public void pageChanged(PageChangedEvent event) { + if (event.getSelectedPage() == this) { + @SuppressWarnings({ "unchecked", "rawtypes" }) + Object[] values = ((ArrayList) userListPage.getSelectedUsers()) + .toArray(new Object[userListPage.getSelectedUsers().size()]); + userTableCmp.getTableViewer().setInput(values); + String msg = "Following batch action: [" + chooseCommandPage.getCommandLbl() + + "] will be perfomed on the users listed below.\n"; + // + "Are you sure you want to proceed?"; + setMessage(msg); + } + } + + private class ChosenUsersTableViewer extends LdifUsersTable { + private static final long serialVersionUID = 7814764735794270541L; + + public ChosenUsersTableViewer(Composite parent, int style) { + super(parent, style); + } + + @Override + protected List listFilteredElements(String filter) { + return userListPage.getSelectedUsers(); + } + } + } +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UserEditor.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UserEditor.java new file mode 100644 index 0000000..66f4420 --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UserEditor.java @@ -0,0 +1,535 @@ +package org.argeo.cms.e4.users; + +import static org.argeo.cms.auth.UserAdminUtils.getProperty; +import static org.argeo.util.naming.LdapAttrs.cn; +import static org.argeo.util.naming.LdapAttrs.givenName; +import static org.argeo.util.naming.LdapAttrs.mail; +import static org.argeo.util.naming.LdapAttrs.sn; +import static org.argeo.util.naming.LdapAttrs.uid; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.inject.Inject; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.auth.UserAdminUtils; +import org.argeo.cms.e4.users.providers.CommonNameLP; +import org.argeo.cms.e4.users.providers.DomainNameLP; +import org.argeo.cms.e4.users.providers.RoleIconLP; +import org.argeo.cms.e4.users.providers.UserFilter; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ui.eclipse.forms.AbstractFormPart; +//import org.argeo.cms.ui.eclipse.forms.FormToolkit; +import org.argeo.cms.ui.eclipse.forms.IManagedForm; +import org.argeo.eclipse.ui.ColumnDefinition; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.parts.LdifUsersTable; +import org.argeo.util.naming.LdapAttrs; +import org.eclipse.e4.ui.workbench.modeling.EPartService; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.ToolBarManager; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.dialogs.TrayDialog; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerDropAdapter; +import org.eclipse.swt.SWT; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.DropTargetEvent; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.dnd.TransferData; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Link; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.ToolBar; +import org.osgi.service.useradmin.Group; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.User; +import org.osgi.service.useradmin.UserAdmin; +import org.osgi.service.useradmin.UserAdminEvent; + +/** Display/edit the properties of a given user */ +public class UserEditor extends AbstractRoleEditor { + // final static String ID = "UserEditor.mainPage"; + + @Inject + private EPartService partService; + + // private final UserEditor editor; + // private UserAdminWrapper userAdminWrapper; + + // Local configuration + // private final int PRE_TITLE_INDENT = 10; + + // public UserMainPage(FormEditor editor, UserAdminWrapper userAdminWrapper) { + // super(editor, ID, "Main"); + // this.editor = (UserEditor) editor; + // this.userAdminWrapper = userAdminWrapper; + // } + + // protected void createFormContent(final IManagedForm mf) { + // ScrolledForm form = mf.getForm(); + // Composite body = form.getBody(); + // GridLayout mainLayout = new GridLayout(); + // // mainLayout.marginRight = 10; + // body.setLayout(mainLayout); + // User user = editor.getDisplayedUser(); + // appendOverviewPart(body, user); + // // Remove to ability to force the password for his own user. The user + // // must then use the change pwd feature + // appendMemberOfPart(body, user); + // } + + @Override + protected void createUi(Composite body) { + // Composite body = new Composite(parent, SWT.BORDER); + GridLayout mainLayout = new GridLayout(); + // mainLayout.marginRight = 10; + body.setLayout(mainLayout); + // body.getParent().setLayout(new GridLayout()); + // body.setLayoutData(CmsUiUtils.fillAll()); + User user = getDisplayedUser(); + appendOverviewPart(body, user); + // Remove to ability to force the password for his own user. The user + // must then use the change pwd feature + appendMemberOfPart(body, user); + } + + /** Creates the general section */ + private void appendOverviewPart(final Composite parent, final User user) { + // FormToolkit tk = getManagedForm().getToolkit(); + + // Section section = tk.createSection(parent, SWT.NO_FOCUS); + // GridData gd = EclipseUiUtils.fillWidth(); + // // gd.verticalAlignment = PRE_TITLE_INDENT; + // section.setLayoutData(gd); + Composite body = new Composite(parent, SWT.NONE); + body.setLayoutData(EclipseUiUtils.fillWidth()); + // section.setClient(body); + // body.setLayout(new GridLayout(6, false)); + body.setLayout(new GridLayout(2, false)); + + Text commonName = createReadOnlyLT(body, "Name", getProperty(user, cn)); + Text distinguishedName = createReadOnlyLT(body, "Login", getProperty(user, uid)); + Text firstName = createLT(body, "First name", getProperty(user, givenName)); + Text lastName = createLT(body, "Last name", getProperty(user, sn)); + Text email = createLT(body, "Email", getProperty(user, mail)); + + Link resetPwdLk = new Link(body, SWT.NONE); + if (!UserAdminUtils.isCurrentUser(user)) { + resetPwdLk.setText("Reset password"); + } + resetPwdLk.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1)); + + // create form part (controller) + AbstractFormPart part = new AbstractFormPart() { + private MainInfoListener listener; + + @Override + public void initialize(IManagedForm form) { + super.initialize(form); + listener = new MainInfoListener(parent.getDisplay(), this); + userAdminWrapper.addListener(listener); + } + + @Override + public void dispose() { + userAdminWrapper.removeListener(listener); + super.dispose(); + } + + @SuppressWarnings("unchecked") + public void commit(boolean onSave) { + // TODO Sanity checks (mail validity...) + user.getProperties().put(LdapAttrs.givenName.name(), firstName.getText()); + user.getProperties().put(LdapAttrs.sn.name(), lastName.getText()); + user.getProperties().put(LdapAttrs.cn.name(), commonName.getText()); + user.getProperties().put(LdapAttrs.mail.name(), email.getText()); + super.commit(onSave); + } + + @Override + public void refresh() { + distinguishedName.setText(UserAdminUtils.getProperty(user, LdapAttrs.uid.name())); + commonName.setText(UserAdminUtils.getProperty(user, LdapAttrs.cn.name())); + firstName.setText(UserAdminUtils.getProperty(user, LdapAttrs.givenName.name())); + lastName.setText(UserAdminUtils.getProperty(user, LdapAttrs.sn.name())); + email.setText(UserAdminUtils.getProperty(user, LdapAttrs.mail.name())); + refreshFormTitle(user); + super.refresh(); + } + }; + + // Improve this: automatically generate CN when first or last name + // changes + ModifyListener cnML = new ModifyListener() { + private static final long serialVersionUID = 4298649222869835486L; + + @Override + public void modifyText(ModifyEvent event) { + String first = firstName.getText(); + String last = lastName.getText(); + String cn = first.trim() + " " + last.trim() + " "; + cn = cn.trim(); + commonName.setText(cn); + // getManagedForm().getForm().setText(cn); + updateEditorTitle(cn); + } + }; + firstName.addModifyListener(cnML); + lastName.addModifyListener(cnML); + + ModifyListener defaultListener = new FormPartML(part); + firstName.addModifyListener(defaultListener); + lastName.addModifyListener(defaultListener); + email.addModifyListener(defaultListener); + + if (!UserAdminUtils.isCurrentUser(user)) + resetPwdLk.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = 5881800534589073787L; + + @Override + public void widgetSelected(SelectionEvent e) { + new ChangePasswordDialog(user, "Reset password").open(); + } + }); + + getManagedForm().addPart(part); + } + + private class ChangePasswordDialog extends TrayDialog { + private static final long serialVersionUID = 2843538207460082349L; + + private User user; + private Text password1; + private Text password2; + private String title; + // private FormToolkit tk; + + public ChangePasswordDialog(User user, String title) { + super(Display.getDefault().getActiveShell()); + // this.tk = tk; + this.user = user; + this.title = title; + } + + protected Control createDialogArea(Composite parent) { + Composite dialogarea = (Composite) super.createDialogArea(parent); + dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + Composite body = new Composite(dialogarea, SWT.NO_FOCUS); + body.setLayoutData(EclipseUiUtils.fillAll()); + GridLayout layout = new GridLayout(2, false); + body.setLayout(layout); + + password1 = createLP(body, "New password", ""); + password2 = createLP(body, "Repeat password", ""); + parent.pack(); + return body; + } + + @SuppressWarnings("unchecked") + @Override + protected void okPressed() { + String msg = null; + + if (password1.getText().equals("")) + msg = "Password cannot be empty"; + else if (password1.getText().equals(password2.getText())) { + char[] newPassword = password1.getText().toCharArray(); + // userAdminWrapper.beginTransactionIfNeeded(); + userAdminWrapper.beginTransactionIfNeeded(); + user.getCredentials().put(null, newPassword); + userAdminWrapper.commitOrNotifyTransactionStateChange(); + super.okPressed(); + } else { + msg = "Passwords are not equals"; + } + + if (EclipseUiUtils.notEmpty(msg)) + MessageDialog.openError(getParentShell(), "Cannot reset pasword", msg); + } + + protected void configureShell(Shell shell) { + super.configureShell(shell); + shell.setText(title); + } + } + + private LdifUsersTable appendMemberOfPart(final Composite parent, User user) { + // Section section = addSection(tk, parent, "Roles"); + // Composite body = (Composite) section.getClient(); + // Composite body= parent; + Composite body = new Composite(parent, SWT.BORDER); + body.setLayout(new GridLayout()); + body.setLayoutData(CmsSwtUtils.fillAll()); + + // boolean isAdmin = CurrentUser.isInRole(NodeConstants.ROLE_ADMIN); + + // Displayed columns + List columnDefs = new ArrayList(); + columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 0, 24)); + columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Name", 150)); + columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 100)); + // Only show technical DN to administrators + // if (isAdmin) + // columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", + // 300)); + + // Create and configure the table + final LdifUsersTable userViewerCmp = new MyUserTableViewer(body, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL, user); + + userViewerCmp.setColumnDefinitions(columnDefs); + // if (isAdmin) + // userViewerCmp.populateWithStaticFilters(false, false); + // else + userViewerCmp.populate(true, false); + GridData gd = EclipseUiUtils.fillAll(); + gd.heightHint = 500; + userViewerCmp.setLayoutData(gd); + + // Controllers + TableViewer userViewer = userViewerCmp.getTableViewer(); + userViewer.addDoubleClickListener(new UserTableDefaultDClickListener(partService)); + int operations = DND.DROP_COPY | DND.DROP_MOVE; + Transfer[] tt = new Transfer[] { TextTransfer.getInstance() }; + GroupDropListener dropL = new GroupDropListener(userAdminWrapper, userViewer, user); + userViewer.addDropSupport(operations, tt, dropL); + + AbstractFormPart part = new AbstractFormPart() { + + private GroupChangeListener listener; + + @Override + public void initialize(IManagedForm form) { + super.initialize(form); + listener = new GroupChangeListener(parent.getDisplay(), this); + userAdminWrapper.addListener(listener); + } + + public void commit(boolean onSave) { + super.commit(onSave); + } + + @Override + public void dispose() { + userAdminWrapper.removeListener(listener); + super.dispose(); + } + + @Override + public void refresh() { + userViewerCmp.refresh(); + super.refresh(); + } + }; + getManagedForm().addPart(part); + // addRemoveAbitily(body, userViewer, user); + // userViewerCmp.refresh(); + String tooltip = "Remove " + UserAdminUtils.getUserLocalId(user.getName()) + " from the below selected groups"; + Action action = new RemoveMembershipAction(userViewer, user, tooltip, SecurityAdminImages.ICON_REMOVE_DESC); + ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT); + ToolBar toolBar = toolBarManager.createControl(body); + toolBar.setLayoutData(CmsSwtUtils.fillWidth()); + toolBarManager.add(action); + toolBarManager.update(true); + return userViewerCmp; + } + + private class MyUserTableViewer extends LdifUsersTable { + private static final long serialVersionUID = 2653790051461237329L; + + private Button showSystemRoleBtn; + + private final User user; + private final UserFilter userFilter; + + public MyUserTableViewer(Composite parent, int style, User user) { + super(parent, style, true); + this.user = user; + userFilter = new UserFilter(); + } + + protected void populateStaticFilters(Composite staticFilterCmp) { + staticFilterCmp.setLayout(new GridLayout()); + showSystemRoleBtn = new Button(staticFilterCmp, SWT.CHECK); + showSystemRoleBtn.setText("Show system roles"); + boolean showSysRole = CurrentUser.isInRole(CmsConstants.ROLE_ADMIN); + showSystemRoleBtn.setSelection(showSysRole); + userFilter.setShowSystemRole(showSysRole); + showSystemRoleBtn.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = -7033424592697691676L; + + @Override + public void widgetSelected(SelectionEvent e) { + userFilter.setShowSystemRole(showSystemRoleBtn.getSelection()); + refresh(); + } + }); + } + + @Override + protected List listFilteredElements(String filter) { + List users = (List) getFlatGroups(null); + List filteredUsers = new ArrayList(); + if (users.contains(user)) + users.remove(user); + userFilter.setSearchText(filter); + for (User user : users) + if (userFilter.select(null, null, user)) + filteredUsers.add(user); + return filteredUsers; + } + } + + // private void addRemoveAbility(Composite parent, TableViewer userViewer, User + // user) { + // // Section section = sectionPart.getSection(); + // ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT); + // ToolBar toolbar = toolBarManager.createControl(parent); + // final Cursor handCursor = new Cursor(Display.getCurrent(), SWT.CURSOR_HAND); + // toolbar.setCursor(handCursor); + // toolbar.addDisposeListener(new DisposeListener() { + // private static final long serialVersionUID = 3882131405820522925L; + // + // public void widgetDisposed(DisposeEvent e) { + // if ((handCursor != null) && (handCursor.isDisposed() == false)) { + // handCursor.dispose(); + // } + // } + // }); + // + // String tooltip = "Remove " + UserAdminUtils.getUserLocalId(user.getName()) + + // " from the below selected groups"; + // Action action = new RemoveMembershipAction(userViewer, user, tooltip, + // SecurityAdminImages.ICON_REMOVE_DESC); + // toolBarManager.add(action); + // toolBarManager.update(true); + // // section.setTextClient(toolbar); + // } + + private class RemoveMembershipAction extends Action { + private static final long serialVersionUID = -1337713097184522588L; + + private final TableViewer userViewer; + private final User user; + + RemoveMembershipAction(TableViewer userViewer, User user, String name, ImageDescriptor img) { + super(name, img); + this.userViewer = userViewer; + this.user = user; + } + + @Override + public void run() { + ISelection selection = userViewer.getSelection(); + if (selection.isEmpty()) + return; + + @SuppressWarnings("unchecked") + Iterator it = ((IStructuredSelection) selection).iterator(); + List groups = new ArrayList(); + while (it.hasNext()) { + Group currGroup = it.next(); + groups.add(currGroup); + } + + userAdminWrapper.beginTransactionIfNeeded(); + for (Group group : groups) { + group.removeMember(user); + } + userAdminWrapper.commitOrNotifyTransactionStateChange(); + for (Group group : groups) { + userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, group)); + } + } + } + + /** + * Defines the table as being a potential target to add group memberships + * (roles) to this user + */ + private class GroupDropListener extends ViewerDropAdapter { + private static final long serialVersionUID = 2893468717831451621L; + + private final UserAdminWrapper myUserAdminWrapper; + private final User myUser; + + public GroupDropListener(UserAdminWrapper userAdminWrapper, Viewer userViewer, User user) { + super(userViewer); + this.myUserAdminWrapper = userAdminWrapper; + this.myUser = user; + } + + @Override + public boolean validateDrop(Object target, int operation, TransferData transferType) { + // Target is always OK in a list only view + // TODO check if not a string + boolean validDrop = true; + return validDrop; + } + + @Override + public void drop(DropTargetEvent event) { + String name = (String) event.data; + UserAdmin myUserAdmin = myUserAdminWrapper.getUserAdmin(); + Role role = myUserAdmin.getRole(name); + // TODO this check should be done before. + if (role.getType() == Role.GROUP) { + // TODO check if the user is already member of this group + + myUserAdminWrapper.beginTransactionIfNeeded(); + Group group = (Group) role; + group.addMember(myUser); + userAdminWrapper.commitOrNotifyTransactionStateChange(); + myUserAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, group)); + } + super.drop(event); + } + + @Override + public boolean performDrop(Object data) { + // userTableViewerCmp.refresh(); + return true; + } + } + + // LOCAL HELPERS + private void refreshFormTitle(User group) { + // getManagedForm().getForm().setText(UserAdminUtils.getProperty(group, + // LdapAttrs.cn.name())); + } + + /** Appends a section with a title */ + // private Section addSection(FormToolkit tk, Composite parent, String title) { + // Section section = tk.createSection(parent, Section.TITLE_BAR); + // GridData gd = EclipseUiUtils.fillWidth(); + // gd.verticalAlignment = PRE_TITLE_INDENT; + // section.setLayoutData(gd); + // section.setText(title); + // // section.getMenu().setVisible(true); + // + // Composite body = tk.createComposite(section, SWT.WRAP); + // body.setLayoutData(EclipseUiUtils.fillAll()); + // section.setClient(body); + // + // return section; + // } + +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UserTableDefaultDClickListener.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UserTableDefaultDClickListener.java new file mode 100644 index 0000000..c6d024e --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UserTableDefaultDClickListener.java @@ -0,0 +1,39 @@ +package org.argeo.cms.e4.users; + +import org.argeo.cms.e4.CmsE4Utils; +import org.argeo.util.naming.LdapAttrs; +import org.eclipse.e4.ui.workbench.modeling.EPartService; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.osgi.service.useradmin.Group; +import org.osgi.service.useradmin.User; + +/** + * Default double click listener for the various user tables, will open the + * clicked item in the editor + */ +public class UserTableDefaultDClickListener implements IDoubleClickListener { + private final EPartService partService; + + public UserTableDefaultDClickListener(EPartService partService) { + this.partService = partService; + } + + public void doubleClick(DoubleClickEvent evt) { + if (evt.getSelection().isEmpty()) + return; + Object obj = ((IStructuredSelection) evt.getSelection()).getFirstElement(); + User user = (User) obj; + + String editorId = getEditorId(user); + CmsE4Utils.openEditor(partService, editorId, LdapAttrs.uid.name(), user.getName()); + } + + protected String getEditorId(User user) { + if (user instanceof Group) + return "org.argeo.cms.e4.partdescriptor.groupEditor"; + else + return "org.argeo.cms.e4.partdescriptor.userEditor"; + } +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UsersView.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UsersView.java new file mode 100644 index 0000000..720945c --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/UsersView.java @@ -0,0 +1,182 @@ +package org.argeo.cms.e4.users; + +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.inject.Inject; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.e4.users.providers.CommonNameLP; +import org.argeo.cms.e4.users.providers.DomainNameLP; +import org.argeo.cms.e4.users.providers.MailLP; +import org.argeo.cms.e4.users.providers.UserDragListener; +import org.argeo.cms.e4.users.providers.UserNameLP; +import org.argeo.cms.swt.CmsException; +import org.argeo.eclipse.ui.ColumnDefinition; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.parts.LdifUsersTable; +import org.argeo.util.naming.LdapAttrs; +import org.argeo.util.naming.LdapObjs; +import org.eclipse.e4.ui.di.Focus; +import org.eclipse.e4.ui.workbench.modeling.EPartService; +import org.eclipse.e4.ui.workbench.modeling.ESelectionService; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.User; +import org.osgi.service.useradmin.UserAdminEvent; +import org.osgi.service.useradmin.UserAdminListener; + +/** List all users with filter - based on Ldif userAdmin */ +public class UsersView { + // private final static Log log = LogFactory.getLog(UsersView.class); + + // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".usersView"; + + @Inject + private UserAdminWrapper userAdminWrapper; + @Inject + private EPartService partService; + + // UI Objects + private LdifUsersTable userTableViewerCmp; + private TableViewer userViewer; + private List columnDefs = new ArrayList(); + + private UserAdminListener listener; + + @PostConstruct + public void createPartControl(Composite parent, ESelectionService selectionService) { + + parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); + // Define the displayed columns + columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name", 150)); + columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150)); + columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200)); + // Only show technical DN to admin + if (CurrentUser.isInRole(CmsConstants.ROLE_ADMIN)) + columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", 300)); + + // Create and configure the table + userTableViewerCmp = new MyUserTableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); + userTableViewerCmp.setLayoutData(EclipseUiUtils.fillAll()); + userTableViewerCmp.setColumnDefinitions(columnDefs); + userTableViewerCmp.populate(true, false); + + // Links + userViewer = userTableViewerCmp.getTableViewer(); + userViewer.addDoubleClickListener(new UserTableDefaultDClickListener(partService)); + userViewer.addSelectionChangedListener(new ISelectionChangedListener() { + + @Override + public void selectionChanged(SelectionChangedEvent event) { + IStructuredSelection selection = (IStructuredSelection) event.getSelection(); + selectionService.setSelection(selection.toList()); + } + }); + // getViewSite().setSelectionProvider(userViewer); + + // Really? + userTableViewerCmp.refresh(); + + // Drag and drop + int operations = DND.DROP_COPY | DND.DROP_MOVE; + Transfer[] tt = new Transfer[] { TextTransfer.getInstance() }; + userViewer.addDragSupport(operations, tt, new UserDragListener(userViewer)); + + // Register a useradmin listener + listener = new MyUiUAListener(parent.getDisplay()); + userAdminWrapper.addListener(listener); + } + + private class MyUiUAListener extends UiUserAdminListener { + public MyUiUAListener(Display display) { + super(display); + } + + @Override + public void roleChangedToUiThread(UserAdminEvent event) { + if (userViewer != null && !userViewer.getTable().isDisposed()) + refresh(); + } + } + + private class MyUserTableViewer extends LdifUsersTable { + private static final long serialVersionUID = 8467999509931900367L; + + private final String[] knownProps = { LdapAttrs.DN, LdapAttrs.uid.name(), LdapAttrs.cn.name(), + LdapAttrs.givenName.name(), LdapAttrs.sn.name(), LdapAttrs.mail.name() }; + + public MyUserTableViewer(Composite parent, int style) { + super(parent, style); + } + + @Override + protected List listFilteredElements(String filter) { + Role[] roles; + + try { + StringBuilder builder = new StringBuilder(); + + StringBuilder tmpBuilder = new StringBuilder(); + if (EclipseUiUtils.notEmpty(filter)) + for (String prop : knownProps) { + tmpBuilder.append("("); + tmpBuilder.append(prop); + tmpBuilder.append("=*"); + tmpBuilder.append(filter); + tmpBuilder.append("*)"); + } + if (tmpBuilder.length() > 1) { + builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=") + .append(LdapObjs.inetOrgPerson.name()).append(")(|"); + builder.append(tmpBuilder.toString()); + builder.append("))"); + } else + builder.append("(").append(LdapAttrs.objectClass.name()).append("=") + .append(LdapObjs.inetOrgPerson.name()).append(")"); + roles = userAdminWrapper.getUserAdmin().getRoles(builder.toString()); + } catch (InvalidSyntaxException e) { + throw new CmsException("Unable to get roles with filter: " + filter, e); + } + List users = new ArrayList(); + for (Role role : roles) + // if (role.getType() == Role.USER && role.getType() != + // Role.GROUP) + users.add((User) role); + return users; + } + } + + public void refresh() { + userTableViewerCmp.refresh(); + } + + // Override generic view methods + @PreDestroy + public void dispose() { + userAdminWrapper.removeListener(listener); + } + + @Focus + public void setFocus() { + userTableViewerCmp.setFocus(); + } + + /* DEPENDENCY INJECTION */ + public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { + this.userAdminWrapper = userAdminWrapper; + } +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/DeleteGroups.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/DeleteGroups.java new file mode 100644 index 0000000..742bc3f --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/DeleteGroups.java @@ -0,0 +1,95 @@ +package org.argeo.cms.e4.users.handlers; + +import java.util.List; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.argeo.cms.auth.UserAdminUtils; +import org.argeo.cms.e4.users.GroupsView; +import org.argeo.cms.e4.users.UserAdminWrapper; +import org.eclipse.e4.core.di.annotations.CanExecute; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.services.IServiceConstants; +import org.eclipse.e4.ui.workbench.modeling.ESelectionService; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.swt.widgets.Display; +import org.osgi.service.useradmin.Group; +import org.osgi.service.useradmin.UserAdmin; +import org.osgi.service.useradmin.UserAdminEvent; + +/** Delete the selected groups */ +public class DeleteGroups { + // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + + // ".deleteGroups"; + + /* DEPENDENCY INJECTION */ + @Inject + private UserAdminWrapper userAdminWrapper; + + @Inject + ESelectionService selectionService; + + @SuppressWarnings("unchecked") + @Execute + public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) { + // ISelection selection = null;// HandlerUtil.getCurrentSelection(event); + // if (selection.isEmpty()) + // return null; + // + // List groups = new ArrayList(); + // Iterator it = ((IStructuredSelection) selection).iterator(); + + List selection = (List) selectionService.getSelection(); + if (selection == null) + return; + + StringBuilder builder = new StringBuilder(); + for (Group group : selection) { + Group currGroup = group; + String groupName = UserAdminUtils.getUserLocalId(currGroup.getName()); + // TODO add checks + builder.append(groupName).append("; "); + // groups.add(currGroup); + } + + if (!MessageDialog.openQuestion(Display.getCurrent().getActiveShell(), "Delete Groups", "Are you sure that you " + + "want to delete these groups?\n" + builder.substring(0, builder.length() - 2))) + return; + + userAdminWrapper.beginTransactionIfNeeded(); + UserAdmin userAdmin = userAdminWrapper.getUserAdmin(); + // IWorkbenchPage iwp = + // HandlerUtil.getActiveWorkbenchWindow(event).getActivePage(); + for (Group group : selection) { + String groupName = group.getName(); + // TODO find a way to close the editor cleanly if opened. Cannot be + // done through the UserAdminListeners, it causes a + // java.util.ConcurrentModificationException because disposing the + // editor unregisters and disposes the listener + // IEditorPart part = iwp.findEditor(new UserEditorInput(groupName)); + // if (part != null) + // iwp.closeEditor(part, false); + userAdmin.removeRole(groupName); + } + userAdminWrapper.commitOrNotifyTransactionStateChange(); + + // Update the view + for (Group group : selection) { + userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_REMOVED, group)); + } + + // return null; + } + + @CanExecute + public boolean canExecute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) { + return part.getObject() instanceof GroupsView && selectionService.getSelection() != null; + } + + /* DEPENDENCY INJECTION */ + // public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { + // this.userAdminWrapper = userAdminWrapper; + // } +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/DeleteUsers.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/DeleteUsers.java new file mode 100644 index 0000000..d1afd22 --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/DeleteUsers.java @@ -0,0 +1,88 @@ +package org.argeo.cms.e4.users.handlers; + +import java.util.List; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.argeo.cms.auth.UserAdminUtils; +import org.argeo.cms.e4.users.UserAdminWrapper; +import org.argeo.cms.e4.users.UsersView; +import org.eclipse.e4.core.di.annotations.CanExecute; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.services.IServiceConstants; +import org.eclipse.e4.ui.workbench.modeling.ESelectionService; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.swt.widgets.Display; +import org.osgi.service.useradmin.User; +import org.osgi.service.useradmin.UserAdmin; +import org.osgi.service.useradmin.UserAdminEvent; + +/** Delete the selected users */ +public class DeleteUsers { + // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".deleteUsers"; + + /* DEPENDENCY INJECTION */ + @Inject + private UserAdminWrapper userAdminWrapper; + + @SuppressWarnings("unchecked") + @Execute + public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) { + // ISelection selection = null;// HandlerUtil.getCurrentSelection(event); + // if (selection.isEmpty()) + // return null; + List selection = (List) selectionService.getSelection(); + if (selection == null) + return; + +// Iterator it = ((IStructuredSelection) selection).iterator(); +// List users = new ArrayList(); + StringBuilder builder = new StringBuilder(); + + for(User user:selection) { + User currUser = user; +// User currUser = it.next(); + String userName = UserAdminUtils.getUserLocalId(currUser.getName()); + if (UserAdminUtils.isCurrentUser(currUser)) { + MessageDialog.openError(Display.getCurrent().getActiveShell(), "Deletion forbidden", + "You cannot delete your own user this way."); + return; + } + builder.append(userName).append("; "); +// users.add(currUser); + } + + if (!MessageDialog.openQuestion(Display.getCurrent().getActiveShell(), "Delete Users", + "Are you sure that you want to delete these users?\n" + builder.substring(0, builder.length() - 2))) + return; + + userAdminWrapper.beginTransactionIfNeeded(); + UserAdmin userAdmin = userAdminWrapper.getUserAdmin(); + // IWorkbenchPage iwp = + // HandlerUtil.getActiveWorkbenchWindow(event).getActivePage(); + + for (User user : selection) { + String userName = user.getName(); + // TODO find a way to close the editor cleanly if opened. Cannot be + // done through the UserAdminListeners, it causes a + // java.util.ConcurrentModificationException because disposing the + // editor unregisters and disposes the listener + // IEditorPart part = iwp.findEditor(new UserEditorInput(userName)); + // if (part != null) + // iwp.closeEditor(part, false); + userAdmin.removeRole(userName); + } + userAdminWrapper.commitOrNotifyTransactionStateChange(); + + for (User user : selection) { + userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_REMOVED, user)); + } + } + + @CanExecute + public boolean canExecute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) { + return part.getObject() instanceof UsersView && selectionService.getSelection() != null; + } +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/NewGroup.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/NewGroup.java new file mode 100644 index 0000000..41e14e0 --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/NewGroup.java @@ -0,0 +1,212 @@ +package org.argeo.cms.e4.users.handlers; + +import java.util.Dictionary; +import java.util.Map; + +import javax.inject.Inject; + +import org.argeo.cms.e4.users.UserAdminWrapper; +import org.argeo.cms.swt.CmsException; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.dialogs.ErrorFeedback; +import org.argeo.util.directory.DirectoryConf; +import org.argeo.util.naming.LdapAttrs; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.jface.wizard.Wizard; +import org.eclipse.jface.wizard.WizardDialog; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.osgi.service.useradmin.Group; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.UserAdminEvent; + +/** Create a new group */ +public class NewGroup { + // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".newGroup"; + + /* DEPENDENCY INJECTION */ + @Inject + private UserAdminWrapper userAdminWrapper; + + @Execute + public Object execute() { + NewGroupWizard newGroupWizard = new NewGroupWizard(); + newGroupWizard.setWindowTitle("Group creation"); + WizardDialog dialog = new WizardDialog(Display.getCurrent().getActiveShell(), newGroupWizard); + dialog.open(); + return null; + } + + private class NewGroupWizard extends Wizard { + + // Pages + private MainGroupInfoWizardPage mainGroupInfo; + + // UI fields + private Text dNameTxt, commonNameTxt, descriptionTxt; + private Combo baseDnCmb; + + public NewGroupWizard() { + } + + @Override + public void addPages() { + mainGroupInfo = new MainGroupInfoWizardPage(); + addPage(mainGroupInfo); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public boolean performFinish() { + if (!canFinish()) + return false; + String commonName = commonNameTxt.getText(); + try { + userAdminWrapper.beginTransactionIfNeeded(); + String dn = getDn(commonName); + Group group = (Group) userAdminWrapper.getUserAdmin().createRole(dn, Role.GROUP); + Dictionary props = group.getProperties(); + String descStr = descriptionTxt.getText(); + if (EclipseUiUtils.notEmpty(descStr)) + props.put(LdapAttrs.description.name(), descStr); + userAdminWrapper.commitOrNotifyTransactionStateChange(); + userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CREATED, group)); + return true; + } catch (Exception e) { + ErrorFeedback.show("Cannot create new group " + commonName, e); + return false; + } + } + + private class MainGroupInfoWizardPage extends WizardPage implements FocusListener { + private static final long serialVersionUID = -3150193365151601807L; + + public MainGroupInfoWizardPage() { + super("Main"); + setTitle("General information"); + setMessage("Please choose a domain, provide a common name " + "and a free description"); + } + + @Override + public void createControl(Composite parent) { + Composite bodyCmp = new Composite(parent, SWT.NONE); + setControl(bodyCmp); + bodyCmp.setLayout(new GridLayout(2, false)); + + dNameTxt = EclipseUiUtils.createGridLT(bodyCmp, "Distinguished name"); + dNameTxt.setEnabled(false); + + baseDnCmb = createGridLC(bodyCmp, "Base DN"); + // Initialise before adding the listener to avoid NPE + initialiseDnCmb(baseDnCmb); + baseDnCmb.addFocusListener(this); + + commonNameTxt = EclipseUiUtils.createGridLT(bodyCmp, "Common name"); + commonNameTxt.addFocusListener(this); + + Label descLbl = new Label(bodyCmp, SWT.LEAD); + descLbl.setText("Description"); + descLbl.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false)); + descriptionTxt = new Text(bodyCmp, SWT.LEAD | SWT.MULTI | SWT.WRAP | SWT.BORDER); + descriptionTxt.setLayoutData(EclipseUiUtils.fillAll()); + descriptionTxt.addFocusListener(this); + + // Initialize buttons + setPageComplete(false); + getContainer().updateButtons(); + } + + @Override + public void focusLost(FocusEvent event) { + String name = commonNameTxt.getText(); + if (EclipseUiUtils.isEmpty(name)) + dNameTxt.setText(""); + else + dNameTxt.setText(getDn(name)); + + String message = checkComplete(); + if (message != null) { + setMessage(message, WizardPage.ERROR); + setPageComplete(false); + } else { + setMessage("Complete", WizardPage.INFORMATION); + setPageComplete(true); + } + getContainer().updateButtons(); + } + + @Override + public void focusGained(FocusEvent event) { + } + + /** @return the error message or null if complete */ + protected String checkComplete() { + String name = commonNameTxt.getText(); + + if (name.trim().equals("")) + return "Common name must not be empty"; + Role role = userAdminWrapper.getUserAdmin().getRole(getDn(name)); + if (role != null) + return "Group " + name + " already exists"; + return null; + } + + @Override + public void setVisible(boolean visible) { + super.setVisible(visible); + if (visible) + if (baseDnCmb.getSelectionIndex() == -1) + baseDnCmb.setFocus(); + else + commonNameTxt.setFocus(); + } + } + + private Map getDns() { + return userAdminWrapper.getKnownBaseDns(true); + } + + private String getDn(String cn) { + Map dns = getDns(); + String bdn = baseDnCmb.getText(); + if (EclipseUiUtils.notEmpty(bdn)) { + Dictionary props = DirectoryConf.uriAsProperties(dns.get(bdn)); + String dn = LdapAttrs.cn.name() + "=" + cn + "," + DirectoryConf.groupBase.getValue(props) + "," + bdn; + return dn; + } + return null; + } + + private void initialiseDnCmb(Combo combo) { + Map dns = userAdminWrapper.getKnownBaseDns(true); + if (dns.isEmpty()) + throw new CmsException("No writable base dn found. Cannot create group"); + combo.setItems(dns.keySet().toArray(new String[0])); + if (dns.size() == 1) + combo.select(0); + } + } + + private Combo createGridLC(Composite parent, String label) { + Label lbl = new Label(parent, SWT.LEAD); + lbl.setText(label); + lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); + Combo combo = new Combo(parent, SWT.LEAD | SWT.BORDER | SWT.READ_ONLY); + combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + return combo; + } + + /* DEPENDENCY INJECTION */ + public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { + this.userAdminWrapper = userAdminWrapper; + } +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/NewUser.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/NewUser.java new file mode 100644 index 0000000..40a4460 --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/NewUser.java @@ -0,0 +1,287 @@ +package org.argeo.cms.e4.users.handlers; + +import java.util.Dictionary; +import java.util.List; +import java.util.Map; + +import javax.inject.Inject; +import javax.naming.InvalidNameException; +import javax.naming.ldap.LdapName; +import javax.naming.ldap.Rdn; + +import org.argeo.cms.auth.UserAdminUtils; +import org.argeo.cms.e4.users.UiAdminUtils; +import org.argeo.cms.e4.users.UserAdminWrapper; +import org.argeo.cms.swt.CmsException; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.dialogs.ErrorFeedback; +import org.argeo.util.directory.DirectoryConf; +import org.argeo.util.naming.LdapAttrs; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.jface.wizard.Wizard; +import org.eclipse.jface.wizard.WizardDialog; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.User; +import org.osgi.service.useradmin.UserAdminEvent; + +/** Open a wizard that enables creation of a new user. */ +public class NewUser { + // private final static Log log = LogFactory.getLog(NewUser.class); + // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".newUser"; + + /* DEPENDENCY INJECTION */ + @Inject + private UserAdminWrapper userAdminWrapper; + + @Execute + public Object execute() { + NewUserWizard newUserWizard = new NewUserWizard(); + newUserWizard.setWindowTitle("User creation"); + WizardDialog dialog = new WizardDialog(Display.getCurrent().getActiveShell(), newUserWizard); + dialog.open(); + return null; + } + + private class NewUserWizard extends Wizard { + + // pages + private MainUserInfoWizardPage mainUserInfo; + + // End user fields + private Text dNameTxt, usernameTxt, firstNameTxt, lastNameTxt, primaryMailTxt, pwd1Txt, pwd2Txt; + private Combo baseDnCmb; + + public NewUserWizard() { + + } + + @Override + public void addPages() { + mainUserInfo = new MainUserInfoWizardPage(); + addPage(mainUserInfo); + String message = "Default wizard that also eases user creation tests:\n " + + "Mail and last name are automatically " + + "generated form the uid. Password are defauted to 'demo'."; + mainUserInfo.setMessage(message, WizardPage.WARNING); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public boolean performFinish() { + if (!canFinish()) + return false; + String username = mainUserInfo.getUsername(); + userAdminWrapper.beginTransactionIfNeeded(); + try { + User user = (User) userAdminWrapper.getUserAdmin().createRole(getDn(username), Role.USER); + + Dictionary props = user.getProperties(); + + String lastNameStr = lastNameTxt.getText(); + if (EclipseUiUtils.notEmpty(lastNameStr)) + props.put(LdapAttrs.sn.name(), lastNameStr); + + String firstNameStr = firstNameTxt.getText(); + if (EclipseUiUtils.notEmpty(firstNameStr)) + props.put(LdapAttrs.givenName.name(), firstNameStr); + + String cn = UserAdminUtils.buildDefaultCn(firstNameStr, lastNameStr); + if (EclipseUiUtils.notEmpty(cn)) + props.put(LdapAttrs.cn.name(), cn); + + String mailStr = primaryMailTxt.getText(); + if (EclipseUiUtils.notEmpty(mailStr)) + props.put(LdapAttrs.mail.name(), mailStr); + + char[] password = mainUserInfo.getPassword(); + user.getCredentials().put(null, password); + userAdminWrapper.commitOrNotifyTransactionStateChange(); + userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CREATED, user)); + return true; + } catch (Exception e) { + ErrorFeedback.show("Cannot create new user " + username, e); + return false; + } + } + + private class MainUserInfoWizardPage extends WizardPage implements ModifyListener { + private static final long serialVersionUID = -3150193365151601807L; + + public MainUserInfoWizardPage() { + super("Main"); + setTitle("Required Information"); + } + + @Override + public void createControl(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + composite.setLayout(new GridLayout(2, false)); + dNameTxt = EclipseUiUtils.createGridLT(composite, "Distinguished name", this); + dNameTxt.setEnabled(false); + + baseDnCmb = createGridLC(composite, "Base DN"); + initialiseDnCmb(baseDnCmb); + baseDnCmb.addModifyListener(this); + baseDnCmb.addModifyListener(new ModifyListener() { + private static final long serialVersionUID = -1435351236582736843L; + + @Override + public void modifyText(ModifyEvent event) { + String name = usernameTxt.getText(); + dNameTxt.setText(getDn(name)); + } + }); + + usernameTxt = EclipseUiUtils.createGridLT(composite, "Local ID", this); + usernameTxt.addModifyListener(new ModifyListener() { + private static final long serialVersionUID = -1435351236582736843L; + + @Override + public void modifyText(ModifyEvent event) { + String name = usernameTxt.getText(); + if (name.trim().equals("")) { + dNameTxt.setText(""); + lastNameTxt.setText(""); + primaryMailTxt.setText(""); + pwd1Txt.setText(""); + pwd2Txt.setText(""); + } else { + dNameTxt.setText(getDn(name)); + lastNameTxt.setText(name.toUpperCase()); + primaryMailTxt.setText(getMail(name)); + pwd1Txt.setText("demo"); + pwd2Txt.setText("demo"); + } + } + }); + + primaryMailTxt = EclipseUiUtils.createGridLT(composite, "Email", this); + firstNameTxt = EclipseUiUtils.createGridLT(composite, "First name", this); + lastNameTxt = EclipseUiUtils.createGridLT(composite, "Last name", this); + pwd1Txt = EclipseUiUtils.createGridLP(composite, "Password", this); + pwd2Txt = EclipseUiUtils.createGridLP(composite, "Repeat password", this); + setControl(composite); + + // Initialize buttons + setPageComplete(false); + getContainer().updateButtons(); + } + + @Override + public void modifyText(ModifyEvent event) { + String message = checkComplete(); + if (message != null) { + setMessage(message, WizardPage.ERROR); + setPageComplete(false); + } else { + setMessage("Complete", WizardPage.INFORMATION); + setPageComplete(true); + } + getContainer().updateButtons(); + } + + /** @return error message or null if complete */ + protected String checkComplete() { + String name = usernameTxt.getText(); + + if (name.trim().equals("")) + return "User name must not be empty"; + Role role = userAdminWrapper.getUserAdmin().getRole(getDn(name)); + if (role != null) + return "User " + name + " already exists"; + if (!primaryMailTxt.getText().matches(UiAdminUtils.EMAIL_PATTERN)) + return "Not a valid email address"; + if (lastNameTxt.getText().trim().equals("")) + return "Specify a last name"; + if (pwd1Txt.getText().trim().equals("")) + return "Specify a password"; + if (pwd2Txt.getText().trim().equals("")) + return "Repeat the password"; + if (!pwd2Txt.getText().equals(pwd1Txt.getText())) + return "Passwords are different"; + return null; + } + + @Override + public void setVisible(boolean visible) { + super.setVisible(visible); + if (visible) + if (baseDnCmb.getSelectionIndex() == -1) + baseDnCmb.setFocus(); + else + usernameTxt.setFocus(); + } + + public String getUsername() { + return usernameTxt.getText(); + } + + public char[] getPassword() { + return pwd1Txt.getTextChars(); + } + + } + + private Map getDns() { + return userAdminWrapper.getKnownBaseDns(true); + } + + private String getDn(String uid) { + Map dns = getDns(); + String bdn = baseDnCmb.getText(); + if (EclipseUiUtils.notEmpty(bdn)) { + Dictionary props = DirectoryConf.uriAsProperties(dns.get(bdn)); + String dn = LdapAttrs.uid.name() + "=" + uid + "," + DirectoryConf.userBase.getValue(props) + "," + bdn; + return dn; + } + return null; + } + + private void initialiseDnCmb(Combo combo) { + Map dns = userAdminWrapper.getKnownBaseDns(true); + if (dns.isEmpty()) + throw new CmsException("No writable base dn found. Cannot create user"); + combo.setItems(dns.keySet().toArray(new String[0])); + if (dns.size() == 1) + combo.select(0); + } + + private String getMail(String username) { + if (baseDnCmb.getSelectionIndex() == -1) + return null; + String baseDn = baseDnCmb.getText(); + try { + LdapName name = new LdapName(baseDn); + List rdns = name.getRdns(); + return username + "@" + (String) rdns.get(1).getValue() + '.' + (String) rdns.get(0).getValue(); + } catch (InvalidNameException e) { + throw new CmsException("Unable to generate mail for " + username + " with base dn " + baseDn, e); + } + } + } + + private Combo createGridLC(Composite parent, String label) { + Label lbl = new Label(parent, SWT.LEAD); + lbl.setText(label); + lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); + Combo combo = new Combo(parent, SWT.LEAD | SWT.BORDER | SWT.READ_ONLY); + combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + return combo; + } + + /* DEPENDENCY INJECTION */ + public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { + this.userAdminWrapper = userAdminWrapper; + } +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/package-info.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/package-info.java new file mode 100644 index 0000000..cf3db1d --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/handlers/package-info.java @@ -0,0 +1,2 @@ +/** Users management handlers. */ +package org.argeo.cms.e4.users.handlers; \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/package-info.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/package-info.java new file mode 100644 index 0000000..c6f14b0 --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/package-info.java @@ -0,0 +1,2 @@ +/** Users management perspective. */ +package org.argeo.cms.e4.users; \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/CommonNameLP.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/CommonNameLP.java new file mode 100644 index 0000000..2d8db67 --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/CommonNameLP.java @@ -0,0 +1,21 @@ +package org.argeo.cms.e4.users.providers; + +import org.argeo.cms.auth.UserAdminUtils; +import org.argeo.util.naming.LdapAttrs; +import org.osgi.service.useradmin.User; + +/** Simply declare a label provider that returns the common name of a user */ +public class CommonNameLP extends UserAdminAbstractLP { + private static final long serialVersionUID = 5256703081044911941L; + + @Override + public String getText(User user) { + return UserAdminUtils.getProperty(user, LdapAttrs.cn.name()); + } + + @Override + public String getToolTipText(Object element) { + return UserAdminUtils.getProperty((User) element, LdapAttrs.DN); + } + +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/DomainNameLP.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/DomainNameLP.java new file mode 100644 index 0000000..e23729d --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/DomainNameLP.java @@ -0,0 +1,14 @@ +package org.argeo.cms.e4.users.providers; + +import org.argeo.cms.auth.UserAdminUtils; +import org.osgi.service.useradmin.User; + +/** The human friendly domain name for the corresponding user. */ +public class DomainNameLP extends UserAdminAbstractLP { + private static final long serialVersionUID = 5256703081044911941L; + + @Override + public String getText(User user) { + return UserAdminUtils.getDomainName(user); + } +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/MailLP.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/MailLP.java new file mode 100644 index 0000000..52d3b85 --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/MailLP.java @@ -0,0 +1,15 @@ +package org.argeo.cms.e4.users.providers; + +import org.argeo.cms.auth.UserAdminUtils; +import org.argeo.util.naming.LdapAttrs; +import org.osgi.service.useradmin.User; + +/** Simply declare a label provider that returns the Primary Mail of a user */ +public class MailLP extends UserAdminAbstractLP { + private static final long serialVersionUID = 8329764452141982707L; + + @Override + public String getText(User user) { + return UserAdminUtils.getProperty(user, LdapAttrs.mail.name()); + } +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/RoleIconLP.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/RoleIconLP.java new file mode 100644 index 0000000..8e12eed --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/RoleIconLP.java @@ -0,0 +1,35 @@ +package org.argeo.cms.e4.users.providers; + +import org.argeo.api.cms.CmsContext; +import org.argeo.api.cms.CmsConstants; +import org.argeo.cms.auth.UserAdminUtils; +import org.argeo.cms.e4.users.SecurityAdminImages; +import org.argeo.util.naming.LdapAttrs; +import org.eclipse.swt.graphics.Image; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.User; + +/** Provide a bundle specific image depending on the current user type */ +public class RoleIconLP extends UserAdminAbstractLP { + private static final long serialVersionUID = 6550449442061090388L; + + @Override + public String getText(User user) { + return ""; + } + + @Override + public Image getImage(Object element) { + User user = (User) element; + String dn = user.getName(); + if (dn.endsWith(CmsConstants.SYSTEM_ROLES_BASEDN)) + return SecurityAdminImages.ICON_ROLE; + else if (user.getType() == Role.GROUP) { + String businessCategory = UserAdminUtils.getProperty(user, LdapAttrs.businessCategory); + if (businessCategory != null && businessCategory.equals(CmsContext.WORKGROUP)) + return SecurityAdminImages.ICON_WORKGROUP; + return SecurityAdminImages.ICON_GROUP; + } else + return SecurityAdminImages.ICON_USER; + } +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/UserAdminAbstractLP.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/UserAdminAbstractLP.java new file mode 100644 index 0000000..29873db --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/UserAdminAbstractLP.java @@ -0,0 +1,66 @@ +package org.argeo.cms.e4.users.providers; + +import javax.naming.InvalidNameException; +import javax.naming.ldap.LdapName; + +import org.argeo.cms.auth.UserAdminUtils; +import org.argeo.cms.swt.CmsException; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.widgets.Display; +import org.osgi.service.useradmin.User; + +/** + * Utility class that add font modifications to a column label provider + * depending on the given user properties + */ +public abstract class UserAdminAbstractLP extends ColumnLabelProvider { + private static final long serialVersionUID = 137336765024922368L; + + // private Font italic; + private Font bold; + + @Override + public Font getFont(Object element) { + // Self as bold + try { + LdapName selfUserName = UserAdminUtils.getCurrentUserLdapName(); + String userName = ((User) element).getName(); + LdapName userLdapName = new LdapName(userName); + if (userLdapName.equals(selfUserName)) { + if (bold == null) + bold = JFaceResources.getFontRegistry() + .defaultFontDescriptor().setStyle(SWT.BOLD) + .createFont(Display.getCurrent()); + return bold; + } + } catch (InvalidNameException e) { + throw new CmsException("cannot parse dn for " + element, e); + } + + // Disabled as Italic + // Node userProfile = (Node) elem; + // if (!userProfile.getProperty(ARGEO_ENABLED).getBoolean()) + // return italic; + + return null; + // return super.getFont(element); + } + + @Override + public String getText(Object element) { + User user = (User) element; + return getText(user); + } + + public void setDisplay(Display display) { + // italic = JFaceResources.getFontRegistry().defaultFontDescriptor() + // .setStyle(SWT.ITALIC).createFont(display); + bold = JFaceResources.getFontRegistry().defaultFontDescriptor() + .setStyle(SWT.BOLD).createFont(Display.getCurrent()); + } + + public abstract String getText(User user); +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/UserDragListener.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/UserDragListener.java new file mode 100644 index 0000000..56a2624 --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/UserDragListener.java @@ -0,0 +1,40 @@ +package org.argeo.cms.e4.users.providers; + +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.dnd.DragSourceEvent; +import org.eclipse.swt.dnd.DragSourceListener; +import org.osgi.service.useradmin.User; + +/** Default drag listener to modify group and users via the UI */ +public class UserDragListener implements DragSourceListener { + private static final long serialVersionUID = -2074337775033781454L; + private final Viewer viewer; + + public UserDragListener(Viewer viewer) { + this.viewer = viewer; + } + + public void dragStart(DragSourceEvent event) { + // TODO implement finer checks + IStructuredSelection selection = (IStructuredSelection) viewer + .getSelection(); + if (selection.isEmpty() || selection.size() > 1) + event.doit = false; + else + event.doit = true; + } + + public void dragSetData(DragSourceEvent event) { + // TODO Support multiple selection + Object obj = ((IStructuredSelection) viewer.getSelection()) + .getFirstElement(); + if (obj != null) { + User user = (User) obj; + event.data = user.getName(); + } + } + + public void dragFinished(DragSourceEvent event) { + } +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/UserFilter.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/UserFilter.java new file mode 100644 index 0000000..7a7bfbf --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/UserFilter.java @@ -0,0 +1,58 @@ +package org.argeo.cms.e4.users.providers; + +import static org.argeo.eclipse.ui.EclipseUiUtils.notEmpty; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.cms.auth.UserAdminUtils; +import org.argeo.util.naming.LdapAttrs; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerFilter; +import org.osgi.service.useradmin.User; + +/** + * Filter user list using JFace mechanism on the client (yet on the server) side + * rather than having the UserAdmin to process the search + */ +public class UserFilter extends ViewerFilter { + private static final long serialVersionUID = 5082509381672880568L; + + private String searchString; + private boolean showSystemRole = true; + + private final String[] knownProps = { LdapAttrs.DN, LdapAttrs.cn.name(), LdapAttrs.givenName.name(), + LdapAttrs.sn.name(), LdapAttrs.uid.name(), LdapAttrs.description.name(), LdapAttrs.mail.name() }; + + public void setSearchText(String s) { + // ensure that the value can be used for matching + if (notEmpty(s)) + searchString = ".*" + s.toLowerCase() + ".*"; + else + searchString = ".*"; + } + + public void setShowSystemRole(boolean showSystemRole) { + this.showSystemRole = showSystemRole; + } + + @Override + public boolean select(Viewer viewer, Object parentElement, Object element) { + User user = (User) element; + if (!showSystemRole && user.getName().matches(".*(" + CmsConstants.SYSTEM_ROLES_BASEDN + ")")) + // UserAdminUtils.getProperty(user, LdifName.dn.name()) + // .toLowerCase().endsWith(AuthConstants.ROLES_BASEDN)) + return false; + + if (searchString == null || searchString.length() == 0) + return true; + + if (user.getName().matches(searchString)) + return true; + + for (String key : knownProps) { + String currVal = UserAdminUtils.getProperty(user, key); + if (notEmpty(currVal) && currVal.toLowerCase().matches(searchString)) + return true; + } + return false; + } +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/UserNameLP.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/UserNameLP.java new file mode 100644 index 0000000..3cd00eb --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/UserNameLP.java @@ -0,0 +1,13 @@ +package org.argeo.cms.e4.users.providers; + +import org.osgi.service.useradmin.User; + +/** Simply declare a label provider that returns the username of a user */ +public class UserNameLP extends UserAdminAbstractLP { + private static final long serialVersionUID = 6550449442061090388L; + + @Override + public String getText(User user) { + return user.getName(); + } +} diff --git a/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/package-info.java b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/package-info.java new file mode 100644 index 0000000..33bef8d --- /dev/null +++ b/swt/org.argeo.cms.jcr.e4/src/org/argeo/cms/e4/users/providers/package-info.java @@ -0,0 +1,2 @@ +/** Users management content providers. */ +package org.argeo.cms.e4.users.providers; \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.ui/.classpath b/swt/org.argeo.cms.jcr.ui/.classpath new file mode 100644 index 0000000..81fe078 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/swt/org.argeo.cms.jcr.ui/.project b/swt/org.argeo.cms.jcr.ui/.project new file mode 100644 index 0000000..645296b --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/.project @@ -0,0 +1,28 @@ + + + org.argeo.cms.jcr.ui + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/swt/org.argeo.cms.jcr.ui/META-INF/.gitignore b/swt/org.argeo.cms.jcr.ui/META-INF/.gitignore new file mode 100644 index 0000000..4854a41 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/META-INF/.gitignore @@ -0,0 +1 @@ +/MANIFEST.MF diff --git a/swt/org.argeo.cms.jcr.ui/bnd.bnd b/swt/org.argeo.cms.jcr.ui/bnd.bnd new file mode 100644 index 0000000..c3c609c --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/bnd.bnd @@ -0,0 +1,23 @@ +Bundle-Activator: org.argeo.cms.ui.internal.Activator +Bundle-ActivationPolicy: lazy + +Import-Package: org.eclipse.swt,\ +org.eclipse.jface.window,\ +org.eclipse.core.commands,\ +javax.jcr.security,\ +org.eclipse.rap.fileupload;version="[2.1,4)",\ +org.eclipse.rap.rwt;version="[2.1,4)",\ +org.eclipse.rap.rwt.application;version="[2.1,4)",\ +org.eclipse.rap.rwt.client;version="[2.1,4)",\ +org.eclipse.rap.rwt.client.service;version="[2.1,4)",\ +org.eclipse.rap.rwt.service;version="[2.1,4)",\ +org.eclipse.rap.rwt.widgets;version="[2.1,4)",\ +org.osgi.*;version=0.0.0,\ +javax.servlet.*;version="[3,5)",\ +* + +## TODO: in order to enable single sourcing, we have introduced dummy RAP packages +# in the RCP specific bundle org.argeo.eclipse.ui.rap. +# this was working with RAP 2.x but since we upgrade Rap to 3.1.x, +# there is a version range issue cause org.argeo.eclipse.ui.rap bundle is still in 2.x +# We enable large RAP version range as a workaround that must be fixed \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.ui/build.properties b/swt/org.argeo.cms.jcr.ui/build.properties new file mode 100644 index 0000000..c6baffa --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/build.properties @@ -0,0 +1,5 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + icons/ diff --git a/swt/org.argeo.cms.jcr.ui/icons/loading.gif b/swt/org.argeo.cms.jcr.ui/icons/loading.gif new file mode 100644 index 0000000..3288d10 Binary files /dev/null and b/swt/org.argeo.cms.jcr.ui/icons/loading.gif differ diff --git a/swt/org.argeo.cms.jcr.ui/icons/noPic-goldenRatio-640px.png b/swt/org.argeo.cms.jcr.ui/icons/noPic-goldenRatio-640px.png new file mode 100644 index 0000000..0396506 Binary files /dev/null and b/swt/org.argeo.cms.jcr.ui/icons/noPic-goldenRatio-640px.png differ diff --git a/swt/org.argeo.cms.jcr.ui/icons/noPic-square-640px.png b/swt/org.argeo.cms.jcr.ui/icons/noPic-square-640px.png new file mode 100644 index 0000000..8e3abb5 Binary files /dev/null and b/swt/org.argeo.cms.jcr.ui/icons/noPic-square-640px.png differ diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/AbstractOsgiComposite.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/AbstractOsgiComposite.java new file mode 100644 index 0000000..4fd1d68 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/AbstractOsgiComposite.java @@ -0,0 +1,41 @@ +package org.argeo.cms.e4.maintenance; + +import java.util.Collection; + +import org.argeo.api.cms.CmsLog; +import org.argeo.cms.swt.CmsSwtUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; + +abstract class AbstractOsgiComposite extends Composite { + private static final long serialVersionUID = -4097415973477517137L; + protected final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext(); + protected final CmsLog log = CmsLog.getLog(getClass()); + + public AbstractOsgiComposite(Composite parent, int style) { + super(parent, style); + parent.setLayout(CmsSwtUtils.noSpaceGridLayout()); + setLayout(CmsSwtUtils.noSpaceGridLayout()); + setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); + initUi(style); + } + + protected abstract void initUi(int style); + + protected T getService(Class clazz) { + return bc.getService(bc.getServiceReference(clazz)); + } + + protected Collection> getServiceReferences(Class clazz, String filter) { + try { + return bc.getServiceReferences(clazz, filter); + } catch (InvalidSyntaxException e) { + throw new IllegalArgumentException("Filter " + filter + " is invalid", e); + } + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/Browse.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/Browse.java new file mode 100644 index 0000000..a536da0 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/Browse.java @@ -0,0 +1,576 @@ +package org.argeo.cms.e4.maintenance; + +import static org.eclipse.swt.SWT.RIGHT; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.LinkedHashMap; + +import javax.jcr.ItemNotFoundException; +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.Property; +import javax.jcr.PropertyIterator; +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; +import javax.jcr.Value; + +import org.argeo.api.cms.ux.Cms2DSize; +import org.argeo.cms.swt.CmsException; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ui.CmsUiProvider; +import org.argeo.cms.ui.util.CmsLink; +import org.argeo.cms.ui.widgets.EditableImage; +import org.argeo.cms.ui.widgets.Img; +import org.argeo.jcr.JcrUtils; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.jface.viewers.ILazyContentProvider; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TableViewerColumn; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.events.ControlAdapter; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.swt.widgets.Text; + +public class Browse implements CmsUiProvider { + + // Some local constants to experiment. should be cleaned + private final static String BROWSE_PREFIX = "browse#"; + private final static int THUMBNAIL_WIDTH = 400; + private final static int COLUMN_WIDTH = 160; + private DateFormat timeFormatter = new SimpleDateFormat("dd-MM-yyyy', 'HH:mm"); + + // keep a cache of the opened nodes + // Key is the path + private LinkedHashMap browserCols = new LinkedHashMap(); + private Composite nodeDisplayParent; + private Composite colViewer; + private ScrolledComposite scrolledCmp; + private Text parentPathTxt; + private Text filterTxt; + private Node currEdited; + + private String initialPath; + + @Override + public Control createUi(Composite parent, Node context) throws RepositoryException { + if (context == null) + // return null; + throw new CmsException("Context cannot be null"); + GridLayout layout = CmsSwtUtils.noSpaceGridLayout(); + layout.numColumns = 2; + parent.setLayout(layout); + + // Left + Composite leftCmp = new Composite(parent, SWT.NO_FOCUS); + leftCmp.setLayoutData(CmsSwtUtils.fillAll()); + createBrowserPart(leftCmp, context); + + // Right + nodeDisplayParent = new Composite(parent, SWT.NO_FOCUS | SWT.BORDER); + GridData gd = new GridData(SWT.RIGHT, SWT.FILL, false, true); + gd.widthHint = THUMBNAIL_WIDTH; + nodeDisplayParent.setLayoutData(gd); + createNodeView(nodeDisplayParent, context); + + // INIT + setEdited(context); + initialPath = context.getPath(); + + // Workaround we don't yet manage the delete to display parent of the + // initial context node + + return null; + } + + private void createBrowserPart(Composite parent, Node context) throws RepositoryException { + GridLayout layout = CmsSwtUtils.noSpaceGridLayout(); + parent.setLayout(layout); + Composite filterCmp = new Composite(parent, SWT.NO_FOCUS); + filterCmp.setLayoutData(CmsSwtUtils.fillWidth()); + + // top filter + addFilterPanel(filterCmp); + + // scrolled composite + scrolledCmp = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.BORDER | SWT.NO_FOCUS); + scrolledCmp.setLayoutData(CmsSwtUtils.fillAll()); + scrolledCmp.setExpandVertical(true); + scrolledCmp.setExpandHorizontal(true); + scrolledCmp.setShowFocusedControl(true); + + colViewer = new Composite(scrolledCmp, SWT.NO_FOCUS); + scrolledCmp.setContent(colViewer); + scrolledCmp.addControlListener(new ControlAdapter() { + private static final long serialVersionUID = 6589392045145698201L; + + @Override + public void controlResized(ControlEvent e) { + Rectangle r = scrolledCmp.getClientArea(); + scrolledCmp.setMinSize(colViewer.computeSize(SWT.DEFAULT, r.height)); + } + }); + initExplorer(colViewer, context); + } + + private Control initExplorer(Composite parent, Node context) throws RepositoryException { + parent.setLayout(CmsSwtUtils.noSpaceGridLayout()); + createBrowserColumn(parent, context); + return null; + } + + private Control createBrowserColumn(Composite parent, Node context) throws RepositoryException { + // TODO style is not correctly managed. + FilterEntitiesVirtualTable table = new FilterEntitiesVirtualTable(parent, SWT.BORDER | SWT.NO_FOCUS, context); + // CmsUiUtils.style(table, ArgeoOrgStyle.browserColumn.style()); + table.filterList("*"); + table.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, false, true)); + browserCols.put(context.getPath(), table); + return null; + } + + public void addFilterPanel(Composite parent) { + + parent.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(2, false))); + + // Text Area for the filter + parentPathTxt = new Text(parent, SWT.NO_FOCUS); + parentPathTxt.setEditable(false); + filterTxt = new Text(parent, SWT.SEARCH | SWT.ICON_CANCEL); + filterTxt.setMessage("Filter current list"); + filterTxt.setLayoutData(CmsSwtUtils.fillWidth()); + filterTxt.addModifyListener(new ModifyListener() { + private static final long serialVersionUID = 7709303319740056286L; + + public void modifyText(ModifyEvent event) { + modifyFilter(false); + } + }); + + filterTxt.addKeyListener(new KeyListener() { + private static final long serialVersionUID = -4523394262771183968L; + + @Override + public void keyReleased(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + boolean shiftPressed = (e.stateMask & SWT.SHIFT) != 0; + // boolean altPressed = (e.stateMask & SWT.ALT) != 0; + FilterEntitiesVirtualTable currTable = null; + if (currEdited != null) { + FilterEntitiesVirtualTable table = browserCols.get(getPath(currEdited)); + if (table != null && !table.isDisposed()) + currTable = table; + } + + try { + if (e.keyCode == SWT.ARROW_DOWN) + currTable.setFocus(); + else if (e.keyCode == SWT.BS) { + if (filterTxt.getText().equals("") + && !(getPath(currEdited).equals("/") || getPath(currEdited).equals(initialPath))) { + setEdited(currEdited.getParent()); + e.doit = false; + filterTxt.setFocus(); + } + } else if (e.keyCode == SWT.TAB && !shiftPressed) { + if (currEdited.getNodes(filterTxt.getText() + "*").getSize() == 1) { + setEdited(currEdited.getNodes(filterTxt.getText() + "*").nextNode()); + } + filterTxt.setFocus(); + e.doit = false; + } + } catch (RepositoryException e1) { + throw new CmsException("Unexpected error in key management for " + currEdited + "with filter " + + filterTxt.getText(), e1); + } + + } + }); + } + + private void setEdited(Node node) { + try { + currEdited = node; + CmsSwtUtils.clear(nodeDisplayParent); + createNodeView(nodeDisplayParent, currEdited); + nodeDisplayParent.layout(); + refreshFilters(node); + refreshBrowser(node); + } catch (RepositoryException re) { + throw new CmsException("Unable to update browser for " + node, re); + } + } + + private void refreshFilters(Node node) throws RepositoryException { + String currNodePath = node.getPath(); + parentPathTxt.setText(currNodePath); + filterTxt.setText(""); + filterTxt.getParent().layout(); + } + + private void refreshBrowser(Node node) throws RepositoryException { + + // Retrieve + String currNodePath = node.getPath(); + String currParPath = ""; + if (!"/".equals(currNodePath)) + currParPath = JcrUtils.parentPath(currNodePath); + if ("".equals(currParPath)) + currParPath = "/"; + + Object[][] colMatrix = new Object[browserCols.size()][2]; + + int i = 0, j = -1, k = -1; + for (String path : browserCols.keySet()) { + colMatrix[i][0] = path; + colMatrix[i][1] = browserCols.get(path); + if (j >= 0 && k < 0 && !currNodePath.equals("/")) { + boolean leaveOpened = path.startsWith(currNodePath); + + // workaround for same name siblings + // fix me weird side effect when we go left or click on anb + // already selected, unfocused node + if (leaveOpened && (path.lastIndexOf("/") == 0 && currNodePath.lastIndexOf("/") == 0 + || JcrUtils.parentPath(path).equals(JcrUtils.parentPath(currNodePath)))) + leaveOpened = JcrUtils.lastPathElement(path).equals(JcrUtils.lastPathElement(currNodePath)); + + if (!leaveOpened) + k = i; + } + if (currParPath.equals(path)) + j = i; + i++; + } + + if (j >= 0 && k >= 0) + // remove useless cols + for (int l = i - 1; l >= k; l--) { + browserCols.remove(colMatrix[l][0]); + ((FilterEntitiesVirtualTable) colMatrix[l][1]).dispose(); + } + + // Remove disposed columns + // TODO investigate and fix the mechanism that leave them there after + // disposal + if (browserCols.containsKey(currNodePath)) { + FilterEntitiesVirtualTable currCol = browserCols.get(currNodePath); + if (currCol.isDisposed()) + browserCols.remove(currNodePath); + } + + if (!browserCols.containsKey(currNodePath)) + createBrowserColumn(colViewer, node); + + colViewer.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(browserCols.size(), false))); + // colViewer.pack(); + colViewer.layout(); + // also resize the scrolled composite + scrolledCmp.layout(); + scrolledCmp.getShowFocusedControl(); + // colViewer.getParent().layout(); + // if (JcrUtils.parentPath(currNodePath).equals(currBrowserKey)) { + // } else { + // } + } + + private void modifyFilter(boolean fromOutside) { + if (!fromOutside) + if (currEdited != null) { + String filter = filterTxt.getText() + "*"; + FilterEntitiesVirtualTable table = browserCols.get(getPath(currEdited)); + if (table != null && !table.isDisposed()) + table.filterList(filter); + } + + } + + private String getPath(Node node) { + try { + return node.getPath(); + } catch (RepositoryException e) { + throw new CmsException("Unable to get path for node " + node, e); + } + } + + private Cms2DSize imageWidth = new Cms2DSize(250, 0); + + /** + * Recreates the content of the box that displays information about the current + * selected node. + */ + private Control createNodeView(Composite parent, Node context) throws RepositoryException { + + parent.setLayout(new GridLayout(2, false)); + + if (isImg(context)) { + EditableImage image = new Img(parent, RIGHT, context, imageWidth); + image.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false, 2, 1)); + } + + // Name and primary type + Label contextL = new Label(parent, SWT.NONE); + CmsSwtUtils.markup(contextL); + contextL.setText("" + context.getName() + ""); + new Label(parent, SWT.NONE).setText(context.getPrimaryNodeType().getName()); + + // Children + for (NodeIterator nIt = context.getNodes(); nIt.hasNext();) { + Node child = nIt.nextNode(); + new CmsLink(child.getName(), BROWSE_PREFIX + child.getPath()).createUi(parent, context); + new Label(parent, SWT.NONE).setText(child.getPrimaryNodeType().getName()); + } + + // Properties + for (PropertyIterator pIt = context.getProperties(); pIt.hasNext();) { + Property property = pIt.nextProperty(); + Label label = new Label(parent, SWT.NONE); + label.setText(property.getName()); + label.setToolTipText(JcrUtils.getPropertyDefinitionAsString(property)); + new Label(parent, SWT.NONE).setText(getPropAsString(property)); + } + + return null; + } + + private boolean isImg(Node node) throws RepositoryException { + // TODO support images + return false; +// return node.hasNode(JCR_CONTENT) && node.isNodeType(CmsTypes.CMS_IMAGE); + } + + private String getPropAsString(Property property) throws RepositoryException { + String result = ""; + if (property.isMultiple()) { + result = getMultiAsString(property, ", "); + } else { + Value value = property.getValue(); + if (value.getType() == PropertyType.BINARY) + result = ""; + else if (value.getType() == PropertyType.DATE) + result = timeFormatter.format(value.getDate().getTime()); + else + result = value.getString(); + } + return result; + } + + private String getMultiAsString(Property property, String separator) throws RepositoryException { + if (separator == null) + separator = "; "; + Value[] values = property.getValues(); + StringBuilder builder = new StringBuilder(); + for (Value val : values) { + String currStr = val.getString(); + if (!"".equals(currStr.trim())) + builder.append(currStr).append(separator); + } + if (builder.lastIndexOf(separator) >= 0) + return builder.substring(0, builder.length() - separator.length()); + else + return builder.toString(); + } + + /** Almost canonical implementation of a table that display entities */ + private class FilterEntitiesVirtualTable extends Composite { + private static final long serialVersionUID = 8798147431706283824L; + + // Context + private Node context; + + // UI Objects + private TableViewer entityViewer; + + // enable management of multiple columns + Node getNode() { + return context; + } + + @Override + public boolean setFocus() { + if (entityViewer.getTable().isDisposed()) + return false; + if (entityViewer.getSelection().isEmpty()) { + Object first = entityViewer.getElementAt(0); + if (first != null) { + entityViewer.setSelection(new StructuredSelection(first), true); + } + } + return entityViewer.getTable().setFocus(); + } + + void filterList(String filter) { + try { + NodeIterator nit = context.getNodes(filter); + refreshFilteredList(nit); + } catch (RepositoryException e) { + throw new CmsException("Unable to filter " + getNode() + " children with filter " + filter, e); + } + + } + + public FilterEntitiesVirtualTable(Composite parent, int style, Node context) { + super(parent, SWT.NO_FOCUS); + this.context = context; + populate(); + } + + protected void populate() { + Composite parent = this; + GridLayout layout = CmsSwtUtils.noSpaceGridLayout(); + + this.setLayout(layout); + createTableViewer(parent); + } + + private void createTableViewer(final Composite parent) { + // the list + // We must limit the size of the table otherwise the full list is + // loaded + // before the layout happens + Composite listCmp = new Composite(parent, SWT.NO_FOCUS); + GridData gd = new GridData(SWT.LEFT, SWT.FILL, false, true); + gd.widthHint = COLUMN_WIDTH; + listCmp.setLayoutData(gd); + listCmp.setLayout(CmsSwtUtils.noSpaceGridLayout()); + + entityViewer = new TableViewer(listCmp, SWT.VIRTUAL | SWT.SINGLE); + Table table = entityViewer.getTable(); + + table.setLayoutData(CmsSwtUtils.fillAll()); + table.setLinesVisible(true); + table.setHeaderVisible(false); + CmsSwtUtils.markup(table); + + CmsSwtUtils.style(table, MaintenanceStyles.BROWSER_COLUMN); + + // first column + TableViewerColumn column = new TableViewerColumn(entityViewer, SWT.NONE); + TableColumn tcol = column.getColumn(); + tcol.setWidth(COLUMN_WIDTH); + tcol.setResizable(true); + column.setLabelProvider(new SimpleNameLP()); + + entityViewer.setContentProvider(new MyLazyCP(entityViewer)); + entityViewer.addSelectionChangedListener(new ISelectionChangedListener() { + + @Override + public void selectionChanged(SelectionChangedEvent event) { + IStructuredSelection selection = (IStructuredSelection) entityViewer.getSelection(); + if (selection.isEmpty()) + return; + else + setEdited((Node) selection.getFirstElement()); + + } + }); + + table.addKeyListener(new KeyListener() { + private static final long serialVersionUID = -330694313896036230L; + + @Override + public void keyReleased(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + + IStructuredSelection selection = (IStructuredSelection) entityViewer.getSelection(); + Node selected = null; + if (!selection.isEmpty()) + selected = ((Node) selection.getFirstElement()); + try { + if (e.keyCode == SWT.ARROW_RIGHT) { + if (selected != null) { + setEdited(selected); + browserCols.get(selected.getPath()).setFocus(); + } + } else if (e.keyCode == SWT.ARROW_LEFT) { + try { + selected = getNode().getParent(); + String newPath = selected.getPath(); // getNode().getParent() + setEdited(selected); + if (browserCols.containsKey(newPath)) + browserCols.get(newPath).setFocus(); + } catch (ItemNotFoundException ie) { + // root silent + } + } + } catch (RepositoryException ie) { + throw new CmsException("Error while managing arrow " + "events in the browser for " + selected, + ie); + } + } + }); + } + + private class MyLazyCP implements ILazyContentProvider { + private static final long serialVersionUID = 1L; + private TableViewer viewer; + private Object[] elements; + + public MyLazyCP(TableViewer viewer) { + this.viewer = viewer; + } + + public void dispose() { + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // IMPORTANT: don't forget this: an exception will be thrown if + // a selected object is not part of the results anymore. + viewer.setSelection(null); + this.elements = (Object[]) newInput; + } + + public void updateElement(int index) { + viewer.replace(elements[index], index); + } + } + + protected void refreshFilteredList(NodeIterator children) { + Object[] rows = JcrUtils.nodeIteratorToList(children).toArray(); + entityViewer.setInput(rows); + entityViewer.setItemCount(rows.length); + entityViewer.refresh(); + } + + public class SimpleNameLP extends ColumnLabelProvider { + private static final long serialVersionUID = 2465059387875338553L; + + @Override + public String getText(Object element) { + if (element instanceof Node) { + Node curr = ((Node) element); + try { + return curr.getName(); + } catch (RepositoryException e) { + throw new CmsException("Unable to get name for" + curr); + } + } + return super.getText(element); + } + } + } +} \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/ConnectivityDeploymentUi.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/ConnectivityDeploymentUi.java new file mode 100644 index 0000000..97f3e67 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/ConnectivityDeploymentUi.java @@ -0,0 +1,48 @@ +package org.argeo.cms.e4.maintenance; + +import org.argeo.cms.swt.CmsSwtUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.osgi.framework.ServiceReference; +import org.osgi.service.http.HttpService; +import org.osgi.service.useradmin.UserAdmin; + +class ConnectivityDeploymentUi extends AbstractOsgiComposite { + private static final long serialVersionUID = 590221539553514693L; + + public ConnectivityDeploymentUi(Composite parent, int style) { + super(parent, style); + } + + @Override + protected void initUi(int style) { + StringBuffer text = new StringBuffer(); + text.append("Provided Servers
"); + + ServiceReference userAdminRef = bc.getServiceReference(HttpService.class); + if (userAdminRef != null) { + // FIXME use constants + Object httpPort = userAdminRef.getProperty("http.port"); + Object httpsPort = userAdminRef.getProperty("https.port"); + if (httpPort != null) + text.append("http ").append(httpPort).append("
"); + if (httpsPort != null) + text.append("https ").append(httpsPort).append("
"); + + } + + text.append("
"); + text.append("Referenced Servers
"); + + Label label = new Label(this, SWT.NONE); + label.setData(new GridData(SWT.FILL, SWT.FILL, false, false)); + CmsSwtUtils.markup(label); + label.setText(text.toString()); + } + + protected boolean isDeployed() { + return bc.getServiceReference(UserAdmin.class) != null; + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/DataDeploymentUi.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/DataDeploymentUi.java new file mode 100644 index 0000000..ef95bde --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/DataDeploymentUi.java @@ -0,0 +1,139 @@ +package org.argeo.cms.e4.maintenance; + +import java.io.File; +import java.io.IOException; +import java.nio.file.FileStore; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collection; + +import org.apache.jackrabbit.core.RepositoryContext; +import org.apache.jackrabbit.core.config.RepositoryConfig; +import org.argeo.api.cms.CmsConstants; +import org.argeo.cms.swt.CmsSwtUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.osgi.framework.ServiceReference; + +class DataDeploymentUi extends AbstractOsgiComposite { + private static final long serialVersionUID = 590221539553514693L; + + public DataDeploymentUi(Composite parent, int style) { + super(parent, style); + } + + @Override + protected void initUi(int style) { + if (isDeployed()) { + initCurrentUi(this); + } else { + initNewUi(this); + } + } + + private void initNewUi(Composite parent) { +// try { +// ConfigurationAdmin confAdmin = bc.getService(bc.getServiceReference(ConfigurationAdmin.class)); +// Configuration[] confs = confAdmin.listConfigurations( +// "(" + ConfigurationAdmin.SERVICE_FACTORYPID + "=" + NodeConstants.NODE_REPOS_FACTORY_PID + ")"); +// if (confs == null || confs.length == 0) { +// Group buttonGroup = new Group(parent, SWT.NONE); +// buttonGroup.setText("Repository Type"); +// buttonGroup.setLayout(new GridLayout(2, true)); +// buttonGroup.setLayoutData(new GridData(GridData.FILL_VERTICAL)); +// +// SelectionListener selectionListener = new SelectionAdapter() { +// private static final long serialVersionUID = 6247064348421088092L; +// +// public void widgetSelected(SelectionEvent event) { +// Button radio = (Button) event.widget; +// if (!radio.getSelection()) +// return; +// log.debug(event); +// JackrabbitType nodeType = (JackrabbitType) radio.getData(); +// if (log.isDebugEnabled()) +// log.debug(" selected = " + nodeType.name()); +// }; +// }; +// +// for (JackrabbitType nodeType : JackrabbitType.values()) { +// Button radio = new Button(buttonGroup, SWT.RADIO); +// radio.setText(nodeType.name()); +// radio.setData(nodeType); +// if (nodeType.equals(JackrabbitType.localfs)) +// radio.setSelection(true); +// radio.addSelectionListener(selectionListener); +// } +// +// } else if (confs.length == 1) { +// +// } else { +// throw new CmsException("Multiple repos not yet supported"); +// } +// } catch (Exception e) { +// throw new CmsException("Cannot initialize UI", e); +// } + + } + + private void initCurrentUi(Composite parent) { + parent.setLayout(new GridLayout()); + Collection> contexts = getServiceReferences(RepositoryContext.class, + "(" + CmsConstants.CN + "=*)"); + StringBuffer text = new StringBuffer(); + text.append("Jackrabbit Repositories
"); + for (ServiceReference sr : contexts) { + RepositoryContext repositoryContext = bc.getService(sr); + String alias = sr.getProperty(CmsConstants.CN).toString(); + String rootNodeId = repositoryContext.getRootNodeId().toString(); + RepositoryConfig repositoryConfig = repositoryContext.getRepositoryConfig(); + Path repoHomePath = new File(repositoryConfig.getHomeDir()).toPath().toAbsolutePath(); + // TODO check data store + + text.append("" + alias + "
"); + text.append("rootNodeId: " + rootNodeId + "
"); + try { + FileStore fileStore = Files.getFileStore(repoHomePath); + text.append("partition: " + fileStore.toString() + "
"); + text.append( + percentUsed(fileStore) + " used (" + humanReadable(fileStore.getUsableSpace()) + " free)
"); + } catch (IOException e) { + log.error("Cannot check fileStore for " + repoHomePath, e); + } + } + Label label = new Label(parent, SWT.NONE); + label.setData(new GridData(SWT.FILL, SWT.FILL, false, false)); + CmsSwtUtils.markup(label); + label.setText("" + text.toString() + ""); + } + + private String humanReadable(long bytes) { + long mb = bytes / (1024 * 1024); + return mb >= 2048 ? Long.toString(mb / 1024) + " GB" : Long.toString(mb) + " MB"; + } + + private String percentUsed(FileStore fs) throws IOException { + long used = fs.getTotalSpace() - fs.getUnallocatedSpace(); + long percent = used * 100 / fs.getTotalSpace(); + if (log.isTraceEnabled()) { + // output identical to `df -B 1`) + log.trace(fs.getTotalSpace() + "," + used + "," + fs.getUsableSpace()); + } + String span; + if (percent < 80) + span = ""; + else if (percent < 95) + span = ""; + else + span = ""; + return span + percent + "%"; + } + + protected boolean isDeployed() { + return bc.getServiceReference(RepositoryContext.class) != null; + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/DeploymentEntryPoint.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/DeploymentEntryPoint.java new file mode 100644 index 0000000..0a28dc5 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/DeploymentEntryPoint.java @@ -0,0 +1,95 @@ +package org.argeo.cms.e4.maintenance; + +import java.util.GregorianCalendar; +import java.util.TimeZone; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.api.cms.CmsContext; +import org.argeo.api.cms.CmsDeployment; +import org.argeo.api.cms.CmsState; +import org.argeo.cms.swt.CmsSwtUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceReference; + +class DeploymentEntryPoint { + private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext(); + + protected void createContents(Composite parent) { + // FIXME manage authentication if needed + // if (!CurrentUser.roles().contains(AuthConstants.ROLE_ADMIN)) + // return; + + // parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + if (isDesktop()) { + parent.setLayout(new GridLayout(2, true)); + } else { + // TODO add scrolling + parent.setLayout(new GridLayout(1, true)); + } + + initHighLevelSummary(parent); + + Group securityGroup = createHighLevelGroup(parent, "Security"); + securityGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); + new SecurityDeploymentUi(securityGroup, SWT.NONE); + + Group dataGroup = createHighLevelGroup(parent, "Data"); + dataGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); + new DataDeploymentUi(dataGroup, SWT.NONE); + + Group logGroup = createHighLevelGroup(parent, "Notifications"); + logGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true)); + new LogDeploymentUi(logGroup, SWT.NONE); + + Group connectivityGroup = createHighLevelGroup(parent, "Connectivity"); + new ConnectivityDeploymentUi(connectivityGroup, SWT.NONE); + connectivityGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true)); + + } + + private void initHighLevelSummary(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false); + if (isDesktop()) + gridData.horizontalSpan = 3; + composite.setLayoutData(gridData); + composite.setLayout(new FillLayout()); + + ServiceReference nodeStateRef = bc.getServiceReference(CmsState.class); + if (nodeStateRef == null) + throw new IllegalStateException("No CMS state available"); + CmsState nodeState = bc.getService(nodeStateRef); + ServiceReference nodeDeploymentRef = bc.getServiceReference(CmsContext.class); + Label label = new Label(composite, SWT.WRAP); + CmsSwtUtils.markup(label); + if (nodeDeploymentRef == null) { + label.setText("Not yet deployed on, please configure below."); + } else { + Object stateUuid = nodeStateRef.getProperty(CmsConstants.CN); + CmsContext nodeDeployment = bc.getService(nodeDeploymentRef); + GregorianCalendar calendar = new GregorianCalendar(); + calendar.setTimeInMillis(nodeDeployment.getAvailableSince()); + calendar.setTimeZone(TimeZone.getDefault()); + label.setText("Deployment state " + stateUuid + ", available since " + calendar.getTime() + ""); + } + } + + private static Group createHighLevelGroup(Composite parent, String text) { + Group group = new Group(parent, SWT.NONE); + group.setText(text); + CmsSwtUtils.markup(group); + return group; + } + + private boolean isDesktop() { + return true; + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/LogDeploymentUi.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/LogDeploymentUi.java new file mode 100644 index 0000000..fa5d3da --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/LogDeploymentUi.java @@ -0,0 +1,73 @@ +package org.argeo.cms.e4.maintenance; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Enumeration; +import java.util.GregorianCalendar; +import java.util.TimeZone; + +import org.argeo.cms.swt.CmsSwtUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Text; +import org.osgi.service.log.LogEntry; +import org.osgi.service.log.LogListener; +import org.osgi.service.log.LogReaderService; + +class LogDeploymentUi extends AbstractOsgiComposite implements LogListener { + private static final long serialVersionUID = 590221539553514693L; + + private DateFormat dateFormat = new SimpleDateFormat("MMdd HH:mm"); + + private Display display; + private Text logDisplay; + + public LogDeploymentUi(Composite parent, int style) { + super(parent, style); + } + + @Override + protected void initUi(int style) { + LogReaderService logReader = getService(LogReaderService.class); + // FIXME use server push + // logReader.addLogListener(this); + this.display = getDisplay(); + this.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + logDisplay = new Text(this, SWT.WRAP | SWT.MULTI | SWT.READ_ONLY); + logDisplay.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + CmsSwtUtils.markup(logDisplay); + Enumeration logEntries = (Enumeration) logReader.getLog(); + while (logEntries.hasMoreElements()) + logDisplay.append(printEntry(logEntries.nextElement())); + } + + private String printEntry(LogEntry entry) { + StringBuilder sb = new StringBuilder(); + GregorianCalendar calendar = new GregorianCalendar(TimeZone.getDefault()); + calendar.setTimeInMillis(entry.getTime()); + sb.append(dateFormat.format(calendar.getTime())).append(' '); + sb.append(entry.getMessage()); + sb.append('\n'); + return sb.toString(); + } + + @Override + public void logged(LogEntry entry) { + if (display.isDisposed()) + return; + display.asyncExec(() -> { + if (logDisplay.isDisposed()) + return; + logDisplay.append(printEntry(entry)); + }); + display.wake(); + } + + // @Override + // public void dispose() { + // super.dispose(); + // getService(LogReaderService.class).removeLogListener(this); + // } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/MaintenanceStyles.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/MaintenanceStyles.java new file mode 100644 index 0000000..df1be51 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/MaintenanceStyles.java @@ -0,0 +1,10 @@ +package org.argeo.cms.e4.maintenance; + +/** Specific styles used by the various maintenance pages . */ +public interface MaintenanceStyles { + // General + public final static String PREFIX = "maintenance_"; + + // Browser + public final static String BROWSER_COLUMN = "browser_column"; + } diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/NonAdminPage.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/NonAdminPage.java new file mode 100644 index 0000000..cb38ce8 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/NonAdminPage.java @@ -0,0 +1,30 @@ +package org.argeo.cms.e4.maintenance; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ui.CmsUiProvider; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; + +public class NonAdminPage implements CmsUiProvider{ + + @Override + public Control createUi(Composite parent, Node context) + throws RepositoryException { + Composite body = new Composite(parent, SWT.NO_FOCUS); + body.setLayoutData(CmsSwtUtils.fillAll()); + body.setLayout(new GridLayout()); + Label label = new Label(body, SWT.NONE); + label.setText("You should be an admin to perform maintenance operations. " + + "Are you sure you are logged in?"); + label.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true)); + return null; + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/SecurityDeploymentUi.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/SecurityDeploymentUi.java new file mode 100644 index 0000000..3492c54 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/SecurityDeploymentUi.java @@ -0,0 +1,85 @@ +package org.argeo.cms.e4.maintenance; + +import java.net.URI; + +import org.argeo.cms.swt.CmsSwtUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.UserAdmin; + +class SecurityDeploymentUi extends AbstractOsgiComposite { + private static final long serialVersionUID = 590221539553514693L; + + public SecurityDeploymentUi(Composite parent, int style) { + super(parent, style); + } + + @Override + protected void initUi(int style) { + if (isDeployed()) { + initCurrentUi(this); + } else { + initNewUi(this); + } + } + + private void initNewUi(Composite parent) { + new Label(parent, SWT.NONE).setText("Security is not configured"); + } + + private void initCurrentUi(Composite parent) { + ServiceReference userAdminRef = bc.getServiceReference(UserAdmin.class); + UserAdmin userAdmin = bc.getService(userAdminRef); + StringBuffer text = new StringBuffer(); + text.append("Domains
"); + domains: for (String key : userAdminRef.getPropertyKeys()) { + if (!key.startsWith("/")) + continue domains; + URI uri; + try { + uri = new URI(key); + } catch (Exception e) { + // ignore non URI keys + continue domains; + } + + String rootDn = uri.getPath().substring(1, uri.getPath().length()); + // FIXME make reading query options more robust, using utils + boolean readOnly = uri.getQuery().equals("readOnly=true"); + if (readOnly) + text.append(""); + else + text.append(""); + + text.append(rootDn); + text.append("
"); + try { + Role[] roles = userAdmin.getRoles("(dn=*," + rootDn + ")"); + long userCount = 0; + long groupCount = 0; + for (Role role : roles) { + if (role.getType() == Role.USER) + userCount++; + else + groupCount++; + } + text.append(" " + userCount + " users, " + groupCount +" groups.
"); + } catch (InvalidSyntaxException e) { + log.error("Invalid syntax", e); + } + } + Label label = new Label(parent, SWT.NONE); + label.setData(new GridData(SWT.FILL, SWT.FILL, false, false)); + CmsSwtUtils.markup(label); + label.setText(text.toString()); + } + + protected boolean isDeployed() { + return bc.getServiceReference(UserAdmin.class) != null; + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/package-info.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/package-info.java new file mode 100644 index 0000000..e4d2ad4 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/e4/maintenance/package-info.java @@ -0,0 +1,2 @@ +/** Maintenance perspective. */ +package org.argeo.cms.e4.maintenance; \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/CmsUiConstants.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/CmsUiConstants.java new file mode 100644 index 0000000..fd1dda5 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/CmsUiConstants.java @@ -0,0 +1,23 @@ +package org.argeo.cms.ui; + +/** Commons constants */ +@Deprecated +public interface CmsUiConstants { + // DATAKEYS +// public final static String STYLE = EclipseUiConstants.CSS_CLASS; +// public final static String MARKUP = EclipseUiConstants.MARKUP_SUPPORT; + @Deprecated + /* RWT.CUSTOM_ITEM_HEIGHT */ + public final static String ITEM_HEIGHT = "org.eclipse.rap.rwt.customItemHeight"; + + // EVENT DETAILS + @Deprecated + /* RWT.HYPERLINK */ + public final static int HYPERLINK = 1 << 26; + + // STANDARD RESOURCES + public final static String LOADING_IMAGE = "icons/loading.gif"; + + // MISCEALLENEOUS + String DATE_TIME_FORMAT = "dd/MM/yyyy, HH:mm"; +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/CmsUiProvider.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/CmsUiProvider.java new file mode 100644 index 0000000..5f2377b --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/CmsUiProvider.java @@ -0,0 +1,51 @@ +package org.argeo.cms.ui; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.api.acr.Content; +import org.argeo.cms.jcr.acr.JcrContent; +import org.argeo.cms.swt.acr.SwtUiProvider; +import org.argeo.jcr.JcrException; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +/** Stateless factory building an SWT user interface given a JCR context. */ +public interface CmsUiProvider extends SwtUiProvider { + /** + * Initialises a user interface. + * + * @param parent the parent composite + * @param context a context node (holding the JCR underlying session), or null + */ + default Control createUi(Composite parent, Node context) throws RepositoryException { + // does nothing by default + return null; + } + + default Control createUiPart(Composite parent, Node context) { + try { + return createUi(parent, context); + } catch (RepositoryException e) { + throw new JcrException("Cannot create UI for context " + context, e); + } + } + + @Override + default Control createUiPart(Composite parent, Content context) { + if (context == null) + return createUiPart(parent, (Node) null); + if (context instanceof JcrContent) { + Node node = ((JcrContent) context).getJcrNode(); + return createUiPart(parent, node); + } else { +// CmsLog.getLog(CmsUiProvider.class) +// .warn("In " + getClass() + ", content " + context + " is not compatible with JCR."); +// return createUiPart(parent, (Node) null); + + throw new IllegalArgumentException( + "In " + getClass() + ", content " + context + " is not compatible with JCR"); + } + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/LifeCycleUiProvider.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/LifeCycleUiProvider.java new file mode 100644 index 0000000..5d77c15 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/LifeCycleUiProvider.java @@ -0,0 +1,11 @@ +package org.argeo.cms.ui; + +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +/** CmsUiProvider notified of initialisation with a system session. */ +public interface LifeCycleUiProvider extends CmsUiProvider { + public void init(Session adminSession) throws RepositoryException; + + public void destroy(); +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/AbstractFormPart.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/AbstractFormPart.java new file mode 100644 index 0000000..4ce4688 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/AbstractFormPart.java @@ -0,0 +1,108 @@ +package org.argeo.cms.ui.eclipse.forms; +/** + * AbstractFormPart implements IFormPart interface and can be used as a + * convenient base class for concrete form parts. If a method contains + * code that must be called, look for instructions to call 'super' + * when overriding. + * + * @see org.eclipse.ui.forms.widgets.Section + * @since 1.0 + */ +public abstract class AbstractFormPart implements IFormPart { + private IManagedForm managedForm; + private boolean dirty = false; + private boolean stale = true; + /** + * @see org.eclipse.ui.forms.IFormPart#initialize(org.eclipse.ui.forms.IManagedForm) + */ + public void initialize(IManagedForm form) { + this.managedForm = form; + } + /** + * Returns the form that manages this part. + * + * @return the managed form + */ + public IManagedForm getManagedForm() { + return managedForm; + } + /** + * Disposes the part. Subclasses should override to release any system + * resources. + */ + public void dispose() { + } + /** + * Commits the part. Subclasses should call 'super' when overriding. + * + * @param onSave + * true if the request to commit has arrived as a + * result of the 'save' action. + */ + public void commit(boolean onSave) { + dirty = false; + } + /** + * Sets the overall form input. Subclases may elect to override the method + * and adjust according to the form input. + * + * @param input + * the form input object + * @return false + */ + public boolean setFormInput(Object input) { + return false; + } + /** + * Instructs the part to grab keyboard focus. + */ + public void setFocus() { + } + /** + * Refreshes the section after becoming stale (falling behind data in the + * model). Subclasses must call 'super' when overriding this method. + */ + public void refresh() { + stale = false; + // since we have refreshed, any changes we had in the + // part are gone and we are not dirty + dirty = false; + } + /** + * Marks the part dirty. Subclasses should call this method as a result of + * user interaction with the widgets in the section. + */ + public void markDirty() { + dirty = true; + managedForm.dirtyStateChanged(); + } + /** + * Tests whether the part is dirty i.e. its widgets have state that is + * newer than the data in the model. + * + * @return true if the part is dirty, false + * otherwise. + */ + public boolean isDirty() { + return dirty; + } + /** + * Tests whether the part is stale i.e. its widgets have state that is + * older than the data in the model. + * + * @return true if the part is stale, false + * otherwise. + */ + public boolean isStale() { + return stale; + } + /** + * Marks the part stale. Subclasses should call this method as a result of + * model notification that indicates that the content of the section is no + * longer in sync with the model. + */ + public void markStale() { + stale = true; + managedForm.staleStateChanged(); + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormColors.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormColors.java new file mode 100644 index 0000000..32b031b --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormColors.java @@ -0,0 +1,730 @@ +package org.argeo.cms.ui.eclipse.forms; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.resource.LocalResourceManager; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.RGB; +//import org.eclipse.swt.internal.graphics.Graphics; +import org.eclipse.swt.widgets.Display; + +/** + * Manages colors that will be applied to forms and form widgets. The colors are + * chosen to make the widgets look correct in the editor area. If a different + * set of colors is needed, subclass this class and override 'initialize' and/or + * 'initializeColors'. + * + * @since 1.0 + */ +public class FormColors { + /** + * Key for the form title foreground color. + * + * @deprecated use IFormColors.TITLE. + */ + public static final String TITLE = IFormColors.TITLE; + + /** + * Key for the tree/table border color. + * + * @deprecated use IFormColors.BORDER + */ + public static final String BORDER = IFormColors.BORDER; + + /** + * Key for the section separator color. + * + * @deprecated use IFormColors.SEPARATOR. + */ + public static final String SEPARATOR = IFormColors.SEPARATOR; + + /** + * Key for the section title bar background. + * + * @deprecated use IFormColors.TB_BG + */ + public static final String TB_BG = IFormColors.TB_BG; + + /** + * Key for the section title bar foreground. + * + * @deprecated use IFormColors.TB_FG + */ + public static final String TB_FG = IFormColors.TB_FG; + + /** + * Key for the section title bar gradient. + * + * @deprecated use IFormColors.TB_GBG + */ + public static final String TB_GBG = IFormColors.TB_GBG; + + /** + * Key for the section title bar border. + * + * @deprecated use IFormColors.TB_BORDER. + */ + public static final String TB_BORDER = IFormColors.TB_BORDER; + + /** + * Key for the section toggle color. Since 3.1, this color is used for all + * section styles. + * + * @deprecated use IFormColors.TB_TOGGLE. + */ + public static final String TB_TOGGLE = IFormColors.TB_TOGGLE; + + /** + * Key for the section toggle hover color. + * + * @deprecated use IFormColors.TB_TOGGLE_HOVER. + */ + public static final String TB_TOGGLE_HOVER = IFormColors.TB_TOGGLE_HOVER; + + protected Map colorRegistry = new HashMap(10); + + private LocalResourceManager resources; + + protected Color background; + + protected Color foreground; + + private boolean shared; + + protected Display display; + + protected Color border; + + /** + * Creates form colors using the provided display. + * + * @param display + * the display to use + */ + public FormColors(Display display) { + this.display = display; + initialize(); + } + + /** + * Returns the display used to create colors. + * + * @return the display + */ + public Display getDisplay() { + return display; + } + + /** + * Initializes the colors. Subclasses can override this method to change the + * way colors are created. Alternatively, only the color table can be + * modified by overriding initializeColorTable(). + * + * @see #initializeColorTable + */ + protected void initialize() { + background = display.getSystemColor(SWT.COLOR_LIST_BACKGROUND); + foreground = display.getSystemColor(SWT.COLOR_LIST_FOREGROUND); + initializeColorTable(); + updateBorderColor(); + } + + /** + * Allocates colors for the following keys: BORDER, SEPARATOR and + * TITLE. Subclasses can override to allocate these colors differently. + */ + protected void initializeColorTable() { + createTitleColor(); + createColor(IFormColors.SEPARATOR, getColor(IFormColors.TITLE).getRGB()); + RGB black = getSystemColor(SWT.COLOR_BLACK); + RGB borderRGB = getSystemColor(SWT.COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT); + createColor(IFormColors.BORDER, blend(borderRGB, black, 80)); + } + + /** + * Allocates colors for the section tool bar (all the keys that start with + * TB). Since these colors are only needed when TITLE_BAR style is used with + * the Section widget, they are not needed all the time and are allocated on + * demand. Consequently, this method will do nothing if the colors have been + * already initialized. Call this method prior to using colors with the TB + * keys to ensure they are available. + */ + public void initializeSectionToolBarColors() { + if (colorRegistry.containsKey(IFormColors.TB_BG)) + return; + createTitleBarGradientColors(); + createTitleBarOutlineColors(); + createTwistieColors(); + } + + /** + * Allocates additional colors for the form header, namely background + * gradients, bottom separator keylines and DND highlights. Since these + * colors are only needed for clients that want to use these particular + * style of header rendering, they are not needed all the time and are + * allocated on demand. Consequently, this method will do nothing if the + * colors have been already initialized. Call this method prior to using + * color keys with the H_ prefix to ensure they are available. + */ + protected void initializeFormHeaderColors() { + if (colorRegistry.containsKey(IFormColors.H_BOTTOM_KEYLINE2)) + return; + createFormHeaderColors(); + } + + /** + * Returns the RGB value of the system color represented by the code + * argument, as defined in SWT class. + * + * @param code + * the system color constant as defined in SWT + * class. + * @return the RGB value of the system color + */ + public RGB getSystemColor(int code) { + return getDisplay().getSystemColor(code).getRGB(); + } + + /** + * Creates the color for the specified key using the provided RGB object. + * The color object will be returned and also put into the registry. When + * the class is disposed, the color will be disposed with it. + * + * @param key + * the unique color key + * @param rgb + * the RGB object + * @return the allocated color object + */ + public Color createColor(String key, RGB rgb) { + // RAP [rh] changes due to missing Color constructor +// Color c = getResourceManager().createColor(rgb); +// Color prevC = (Color) colorRegistry.get(key); +// if (prevC != null && !prevC.isDisposed()) +// getResourceManager().destroyColor(prevC.getRGB()); +// Color c = Graphics.getColor(rgb); + Color c = new Color(display, rgb); + colorRegistry.put(key, c); + return c; + } + + /** + * Creates a color that can be used for areas of the form that is inactive. + * These areas can contain images, links, controls and other content but are + * considered auxilliary to the main content area. + * + *

+ * The color should not be disposed because it is managed by this class. + * + * @return the inactive form color + */ + public Color getInactiveBackground() { + String key = "__ncbg__"; //$NON-NLS-1$ + Color color = getColor(key); + if (color == null) { + RGB sel = getSystemColor(SWT.COLOR_LIST_SELECTION); + // a blend of 95% white and 5% list selection system color + RGB ncbg = blend(sel, getSystemColor(SWT.COLOR_WHITE), 5); + color = createColor(key, ncbg); + } + return color; + } + + /** + * Creates the color for the specified key using the provided RGB values. + * The color object will be returned and also put into the registry. If + * there is already another color object under the same key in the registry, + * the existing object will be disposed. When the class is disposed, the + * color will be disposed with it. + * + * @param key + * the unique color key + * @param r + * red value + * @param g + * green value + * @param b + * blue value + * @return the allocated color object + */ + public Color createColor(String key, int r, int g, int b) { + return createColor(key, new RGB(r,g,b)); + } + + /** + * Computes the border color relative to the background. Allocated border + * color is designed to work well with white. Otherwise, stanard widget + * background color will be used. + */ + protected void updateBorderColor() { + if (isWhiteBackground()) + border = getColor(IFormColors.BORDER); + else { + border = display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND); + Color bg = getImpliedBackground(); + if (border.getRed() == bg.getRed() + && border.getGreen() == bg.getGreen() + && border.getBlue() == bg.getBlue()) + border = display.getSystemColor(SWT.COLOR_WIDGET_DARK_SHADOW); + } + } + + /** + * Sets the background color. All the toolkits that use this class will + * share the same background. + * + * @param bg + * background color + */ + public void setBackground(Color bg) { + this.background = bg; + updateBorderColor(); + updateFormHeaderColors(); + } + + /** + * Sets the foreground color. All the toolkits that use this class will + * share the same foreground. + * + * @param fg + * foreground color + */ + public void setForeground(Color fg) { + this.foreground = fg; + } + + /** + * Returns the current background color. + * + * @return the background color + */ + public Color getBackground() { + return background; + } + + /** + * Returns the current foreground color. + * + * @return the foreground color + */ + public Color getForeground() { + return foreground; + } + + /** + * Returns the computed border color. Border color depends on the background + * and is recomputed whenever the background changes. + * + * @return the current border color + */ + public Color getBorderColor() { + return border; + } + + /** + * Tests if the background is white. White background has RGB value + * 255,255,255. + * + * @return true if background is white, false + * otherwise. + */ + public boolean isWhiteBackground() { + Color bg = getImpliedBackground(); + return bg.getRed() == 255 && bg.getGreen() == 255 + && bg.getBlue() == 255; + } + + /** + * Returns the color object for the provided key or null if + * not in the registry. + * + * @param key + * the color key + * @return color object if found, or null if not. + */ + public Color getColor(String key) { + if (key.startsWith(IFormColors.TB_PREFIX)) + initializeSectionToolBarColors(); + else if (key.startsWith(IFormColors.H_PREFIX)) + initializeFormHeaderColors(); + return (Color) colorRegistry.get(key); + } + + /** + * Disposes all the colors in the registry. + */ + public void dispose() { + if (resources != null) + resources.dispose(); + resources = null; + colorRegistry = null; + } + + /** + * Marks the colors shared. This prevents toolkits that share this object + * from disposing it. + */ + public void markShared() { + this.shared = true; + } + + /** + * Tests if the colors are shared. + * + * @return true if shared, false otherwise. + */ + public boolean isShared() { + return shared; + } + + /** + * Blends c1 and c2 based in the provided ratio. + * + * @param c1 + * first color + * @param c2 + * second color + * @param ratio + * percentage of the first color in the blend (0-100) + * @return the RGB value of the blended color + */ + public static RGB blend(RGB c1, RGB c2, int ratio) { + int r = blend(c1.red, c2.red, ratio); + int g = blend(c1.green, c2.green, ratio); + int b = blend(c1.blue, c2.blue, ratio); + return new RGB(r, g, b); + } + + /** + * Tests the source RGB for range. + * + * @param rgb + * the tested RGB + * @param from + * range start (excluding the value itself) + * @param to + * range end (excluding the value itself) + * @return true if at least one of the primary colors in the + * source RGB are within the provided range, false + * otherwise. + */ + public static boolean testAnyPrimaryColor(RGB rgb, int from, int to) { + if (testPrimaryColor(rgb.red, from, to)) + return true; + if (testPrimaryColor(rgb.green, from, to)) + return true; + if (testPrimaryColor(rgb.blue, from, to)) + return true; + return false; + } + + /** + * Tests the source RGB for range. + * + * @param rgb + * the tested RGB + * @param from + * range start (excluding the value itself) + * @param to + * tange end (excluding the value itself) + * @return true if at least two of the primary colors in the + * source RGB are within the provided range, false + * otherwise. + */ + public static boolean testTwoPrimaryColors(RGB rgb, int from, int to) { + int total = 0; + if (testPrimaryColor(rgb.red, from, to)) + total++; + if (testPrimaryColor(rgb.green, from, to)) + total++; + if (testPrimaryColor(rgb.blue, from, to)) + total++; + return total >= 2; + } + + /** + * Blends two primary color components based on the provided ratio. + * + * @param v1 + * first component + * @param v2 + * second component + * @param ratio + * percentage of the first component in the blend + * @return + */ + private static int blend(int v1, int v2, int ratio) { + int b = (ratio * v1 + (100 - ratio) * v2) / 100; + return Math.min(255, b); + } + + private Color getImpliedBackground() { + if (getBackground() != null) + return getBackground(); + return getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND); + } + + private static boolean testPrimaryColor(int value, int from, int to) { + return value > from && value < to; + } + + private void createTitleColor() { + /* + * RGB rgb = getSystemColor(SWT.COLOR_LIST_SELECTION); // test too light + * if (testTwoPrimaryColors(rgb, 120, 151)) rgb = blend(rgb, BLACK, 80); + * else if (testTwoPrimaryColors(rgb, 150, 256)) rgb = blend(rgb, BLACK, + * 50); createColor(TITLE, rgb); + */ + RGB bg = getImpliedBackground().getRGB(); + RGB listSelection = getSystemColor(SWT.COLOR_LIST_SELECTION); + RGB listForeground = getSystemColor(SWT.COLOR_LIST_FOREGROUND); + RGB rgb = listSelection; + + // Group 1 + // Rule: If at least 2 of the LIST_SELECTION RGB values are equal to or + // between 0 and 120, then use 100% LIST_SELECTION as it is (no + // additions) + // Examples: XP Default, Win Classic Standard, Win High Con White, Win + // Classic Marine + if (testTwoPrimaryColors(listSelection, -1, 121)) + rgb = listSelection; + // Group 2 + // When LIST_BACKGROUND = white (255, 255, 255) or not black, text + // colour = LIST_SELECTION @ 100% Opacity + 50% LIST_FOREGROUND over + // LIST_BACKGROUND + // Rule: If at least 2 of the LIST_SELECTION RGB values are equal to or + // between 121 and 255, then add 50% LIST_FOREGROUND to LIST_SELECTION + // foreground colour + // Examples: Win Vista, XP Silver, XP Olive , Win Classic Plum, OSX + // Aqua, OSX Graphite, Linux GTK + else if (testTwoPrimaryColors(listSelection, 120, 256) + || (bg.red == 0 && bg.green == 0 && bg.blue == 0)) + rgb = blend(listSelection, listForeground, 50); + // Group 3 + // When LIST_BACKGROUND = black (0, 0, 0), text colour = LIST_SELECTION + // @ 100% Opacity + 50% LIST_FOREGROUND over LIST_BACKGROUND + // Rule: If LIST_BACKGROUND = 0, 0, 0, then add 50% LIST_FOREGROUND to + // LIST_SELECTION foreground colour + // Examples: Win High Con Black, Win High Con #1, Win High Con #2 + // (covered in the second part of the OR clause above) + createColor(IFormColors.TITLE, rgb); + } + + private void createTwistieColors() { + RGB rgb = getColor(IFormColors.TITLE).getRGB(); + RGB white = getSystemColor(SWT.COLOR_WHITE); + createColor(TB_TOGGLE, rgb); + rgb = blend(rgb, white, 60); + createColor(TB_TOGGLE_HOVER, rgb); + } + + private void createTitleBarGradientColors() { + RGB tbBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND); + RGB bg = getImpliedBackground().getRGB(); + + // Group 1 + // Rule: If at least 2 of the RGB values are equal to or between 180 and + // 255, then apply specified opacity for Group 1 + // Examples: Vista, XP Silver, Wn High Con #2 + // Gradient Bottom = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND + // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND + if (testTwoPrimaryColors(tbBg, 179, 256)) + tbBg = blend(tbBg, bg, 30); + + // Group 2 + // Rule: If at least 2 of the RGB values are equal to or between 121 and + // 179, then apply specified opacity for Group 2 + // Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black + // Gradient Bottom = TITLE_BACKGROUND @ 20% Opacity over LIST_BACKGROUND + // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND + else if (testTwoPrimaryColors(tbBg, 120, 180)) + tbBg = blend(tbBg, bg, 20); + + // Group 3 + // Rule: Everything else + // Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX + // Aqua, Wn High Con White, Wn High Con #1 + // Gradient Bottom = TITLE_BACKGROUND @ 10% Opacity over LIST_BACKGROUND + // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND + else { + tbBg = blend(tbBg, bg, 10); + } + + createColor(IFormColors.TB_BG, tbBg); + + // for backward compatibility + createColor(TB_GBG, tbBg); + } + + private void createTitleBarOutlineColors() { + // title bar outline - border color + RGB tbBorder = getSystemColor(SWT.COLOR_TITLE_BACKGROUND); + RGB bg = getImpliedBackground().getRGB(); + // Group 1 + // Rule: If at least 2 of the RGB values are equal to or between 180 and + // 255, then apply specified opacity for Group 1 + // Examples: Vista, XP Silver, Wn High Con #2 + // Keyline = TITLE_BACKGROUND @ 70% Opacity over LIST_BACKGROUND + if (testTwoPrimaryColors(tbBorder, 179, 256)) + tbBorder = blend(tbBorder, bg, 70); + + // Group 2 + // Rule: If at least 2 of the RGB values are equal to or between 121 and + // 179, then apply specified opacity for Group 2 + // Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black + + // Keyline = TITLE_BACKGROUND @ 50% Opacity over LIST_BACKGROUND + else if (testTwoPrimaryColors(tbBorder, 120, 180)) + tbBorder = blend(tbBorder, bg, 50); + + // Group 3 + // Rule: Everything else + // Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX + // Aqua, Wn High Con White, Wn High Con #1 + + // Keyline = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND + else { + tbBorder = blend(tbBorder, bg, 30); + } + createColor(FormColors.TB_BORDER, tbBorder); + } + + private void updateFormHeaderColors() { + if (colorRegistry.containsKey(IFormColors.H_GRADIENT_END)) { + disposeIfFound(IFormColors.H_GRADIENT_END); + disposeIfFound(IFormColors.H_GRADIENT_START); + disposeIfFound(IFormColors.H_BOTTOM_KEYLINE1); + disposeIfFound(IFormColors.H_BOTTOM_KEYLINE2); + disposeIfFound(IFormColors.H_HOVER_LIGHT); + disposeIfFound(IFormColors.H_HOVER_FULL); + initializeFormHeaderColors(); + } + } + + private void disposeIfFound(String key) { + Color color = getColor(key); + if (color != null) { + colorRegistry.remove(key); + // RAP [rh] changes due to missing Color#dispose() +// color.dispose(); + } + } + + private void createFormHeaderColors() { + createFormHeaderGradientColors(); + createFormHeaderKeylineColors(); + createFormHeaderDNDColors(); + } + + private void createFormHeaderGradientColors() { + RGB titleBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND); + Color bgColor = getImpliedBackground(); + RGB bg = bgColor.getRGB(); + RGB bottom, top; + // Group 1 + // Rule: If at least 2 of the RGB values are equal to or between 180 and + // 255, then apply specified opacity for Group 1 + // Examples: Vista, XP Silver, Wn High Con #2 + // Gradient Bottom = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND + // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND + if (testTwoPrimaryColors(titleBg, 179, 256)) { + bottom = blend(titleBg, bg, 30); + top = bg; + } + + // Group 2 + // Rule: If at least 2 of the RGB values are equal to or between 121 and + // 179, then apply specified opacity for Group 2 + // Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black + // Gradient Bottom = TITLE_BACKGROUND @ 20% Opacity over LIST_BACKGROUND + // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND + else if (testTwoPrimaryColors(titleBg, 120, 180)) { + bottom = blend(titleBg, bg, 20); + top = bg; + } + + // Group 3 + // Rule: If at least 2 of the RGB values are equal to or between 0 and + // 120, then apply specified opacity for Group 3 + // Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX + // Aqua, Wn High Con White, Wn High Con #1 + // Gradient Bottom = TITLE_BACKGROUND @ 10% Opacity over LIST_BACKGROUND + // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND + else { + bottom = blend(titleBg, bg, 10); + top = bg; + } + createColor(IFormColors.H_GRADIENT_END, top); + createColor(IFormColors.H_GRADIENT_START, bottom); + } + + private void createFormHeaderKeylineColors() { + RGB titleBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND); + Color bgColor = getImpliedBackground(); + RGB bg = bgColor.getRGB(); + RGB keyline2; + // H_BOTTOM_KEYLINE1 + createColor(IFormColors.H_BOTTOM_KEYLINE1, new RGB(255, 255, 255)); + + // H_BOTTOM_KEYLINE2 + // Group 1 + // Rule: If at least 2 of the RGB values are equal to or between 180 and + // 255, then apply specified opacity for Group 1 + // Examples: Vista, XP Silver, Wn High Con #2 + // Keyline = TITLE_BACKGROUND @ 70% Opacity over LIST_BACKGROUND + if (testTwoPrimaryColors(titleBg, 179, 256)) + keyline2 = blend(titleBg, bg, 70); + + // Group 2 + // Rule: If at least 2 of the RGB values are equal to or between 121 and + // 179, then apply specified opacity for Group 2 + // Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black + // Keyline = TITLE_BACKGROUND @ 50% Opacity over LIST_BACKGROUND + else if (testTwoPrimaryColors(titleBg, 120, 180)) + keyline2 = blend(titleBg, bg, 50); + + // Group 3 + // Rule: If at least 2 of the RGB values are equal to or between 0 and + // 120, then apply specified opacity for Group 3 + // Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX + // Aqua, Wn High Con White, Wn High Con #1 + + // Keyline = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND + else + keyline2 = blend(titleBg, bg, 30); + // H_BOTTOM_KEYLINE2 + createColor(IFormColors.H_BOTTOM_KEYLINE2, keyline2); + } + + private void createFormHeaderDNDColors() { + RGB titleBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND_GRADIENT); + Color bgColor = getImpliedBackground(); + RGB bg = bgColor.getRGB(); + RGB light, full; + // ALL Themes + // + // Light Highlight + // When *near* the 'hot' area + // Rule: If near the title in the 'hot' area, show background highlight + // TITLE_BACKGROUND_GRADIENT @ 40% + light = blend(titleBg, bg, 40); + // Full Highlight + // When *on* the title area (regions 1 and 2) + // Rule: If near the title in the 'hot' area, show background highlight + // TITLE_BACKGROUND_GRADIENT @ 60% + full = blend(titleBg, bg, 60); + // H_DND_LIGHT + // H_DND_FULL + createColor(IFormColors.H_HOVER_LIGHT, light); + createColor(IFormColors.H_HOVER_FULL, full); + } + + private LocalResourceManager getResourceManager() { + if (resources == null) + resources = new LocalResourceManager(JFaceResources.getResources()); + return resources; + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormFonts.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormFonts.java new file mode 100644 index 0000000..9e931ba --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormFonts.java @@ -0,0 +1,122 @@ +package org.argeo.cms.ui.eclipse.forms; + +import java.util.HashMap; + +import org.eclipse.jface.resource.DeviceResourceException; +import org.eclipse.jface.resource.FontDescriptor; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.resource.LocalResourceManager; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Device; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +//import org.eclipse.swt.internal.graphics.Graphics; +import org.eclipse.swt.widgets.Display; + +public class FormFonts { + private static FormFonts instance; + + public static FormFonts getInstance() { + if (instance == null) + instance = new FormFonts(); + return instance; + } + + private LocalResourceManager resources; + private HashMap descriptors; + + private FormFonts() { + } + + private class BoldFontDescriptor extends FontDescriptor { + private FontData[] fFontData; + + BoldFontDescriptor(Font font) { + // RAP [if] Changes due to different way of creating fonts + // fFontData = font.getFontData(); + // for (int i = 0; i < fFontData.length; i++) { + // fFontData[i].setStyle(fFontData[i].getStyle() | SWT.BOLD); + // } + FontData fontData = font.getFontData()[0]; + // Font boldFont = Graphics.getFont( fontData.getName(), + // fontData.getHeight(), + // fontData.getStyle() | SWT.BOLD ); + Font boldFont = new Font(Display.getCurrent(), fontData.getName(), fontData.getHeight(), + fontData.getStyle() | SWT.BOLD); + fFontData = boldFont.getFontData(); + } + + public boolean equals(Object obj) { + if (obj instanceof BoldFontDescriptor) { + BoldFontDescriptor desc = (BoldFontDescriptor) obj; + if (desc.fFontData.length != fFontData.length) + return false; + for (int i = 0; i < fFontData.length; i++) + if (!fFontData[i].equals(desc.fFontData[i])) + return false; + return true; + } + return false; + } + + public int hashCode() { + int hash = 0; + for (int i = 0; i < fFontData.length; i++) + hash = hash * 7 + fFontData[i].hashCode(); + return hash; + } + + public Font createFont(Device device) throws DeviceResourceException { + // RAP [if] Changes due to different way of creating fonts + return new Font(device, fFontData[0]); + // return Graphics.getFont( fFontData[ 0 ] ); + } + + public void destroyFont(Font previouslyCreatedFont) { + // RAP [if] unnecessary + // previouslyCreatedFont.dispose(); + } + } + + public Font getBoldFont(Display display, Font font) { + checkHashMaps(); + BoldFontDescriptor desc = new BoldFontDescriptor(font); + Font result = getResourceManager().createFont(desc); + descriptors.put(result, desc); + return result; + } + + public boolean markFinished(Font boldFont) { + checkHashMaps(); + BoldFontDescriptor desc = (BoldFontDescriptor) descriptors.get(boldFont); + if (desc != null) { + getResourceManager().destroyFont(desc); + if (getResourceManager().find(desc) == null) { + descriptors.remove(boldFont); + validateHashMaps(); + } + return true; + + } + // if the image was not found, dispose of it for the caller + // RAP [if] unnecessary + // boldFont.dispose(); + return false; + } + + private LocalResourceManager getResourceManager() { + if (resources == null) + resources = new LocalResourceManager(JFaceResources.getResources()); + return resources; + } + + private void checkHashMaps() { + if (descriptors == null) + descriptors = new HashMap(); + } + + private void validateHashMaps() { + if (descriptors.size() == 0) + descriptors = null; + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormToolkit.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormToolkit.java new file mode 100644 index 0000000..9927104 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormToolkit.java @@ -0,0 +1,913 @@ +package org.argeo.cms.ui.eclipse.forms; + +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.SWT; +//import org.eclipse.swt.custom.CCombo; +import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.events.FocusAdapter; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.MouseAdapter; +import org.eclipse.swt.events.MouseEvent; +// RAP [rh] Paint events missing +//import org.eclipse.swt.events.PaintEvent; +//import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +//RAP [rh] GC missing +//import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Point; +//import org.eclipse.swt.graphics.RGB; +//import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +//import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Label; +//import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.Widget; +//import org.eclipse.ui.forms.FormColors; +//import org.eclipse.ui.forms.HyperlinkGroup; +//import org.eclipse.ui.forms.IFormColors; +//import org.eclipse.ui.internal.forms.widgets.FormFonts; +//import org.eclipse.ui.internal.forms.widgets.FormUtil; + +/** + * The toolkit is responsible for creating SWT controls adapted to work in + * Eclipse forms. In addition to changing their presentation properties (fonts, + * colors etc.), various listeners are attached to make them behave correctly in + * the form context. + *

+ * In addition to being the control factory, the toolkit is also responsible for + * painting flat borders for select controls, managing hyperlink groups and + * control colors. + *

+ * The toolkit creates some of the most common controls used to populate Eclipse + * forms. Controls that must be created using their constructors, + * adapt() method is available to change its properties in the + * same way as with the supported toolkit controls. + *

+ * Typically, one toolkit object is created per workbench part (for example, an + * editor or a form wizard). The toolkit is disposed when the part is disposed. + * To conserve resources, it is possible to create one color object for the + * entire plug-in and share it between several toolkits. The plug-in is + * responsible for disposing the colors (disposing the toolkit that uses shared + * color object will not dispose the colors). + *

+ * FormToolkit is normally instantiated, but can also be subclassed if some of + * the methods needs to be modified. In those cases, super must + * be called to preserve normal behaviour. + * + * @since 1.0 + */ +public class FormToolkit { + public static final String KEY_DRAW_BORDER = "FormWidgetFactory.drawBorder"; //$NON-NLS-1$ + + public static final String TREE_BORDER = "treeBorder"; //$NON-NLS-1$ + + public static final String TEXT_BORDER = "textBorder"; //$NON-NLS-1$ + + private int borderStyle = SWT.NULL; + + private FormColors colors; + + private int orientation = Window.getDefaultOrientation(); + + // private KeyListener deleteListener; + // RAP [rh] Paint events missing +// private BorderPainter borderPainter; + + private BoldFontHolder boldFontHolder; + +// private HyperlinkGroup hyperlinkGroup; + + private boolean isDisposed = false; + + /* default */ + VisibilityHandler visibilityHandler; + + /* default */ + KeyboardHandler keyboardHandler; + + // RAP [rh] Paint events missing +// private class BorderPainter implements PaintListener { +// public void paintControl(PaintEvent event) { +// Composite composite = (Composite) event.widget; +// Control[] children = composite.getChildren(); +// for (int i = 0; i < children.length; i++) { +// Control c = children[i]; +// boolean inactiveBorder = false; +// boolean textBorder = false; +// if (!c.isVisible()) +// continue; +// /* +// * if (c.getEnabled() == false && !(c instanceof CCombo)) +// * continue; +// */ +// if (c instanceof Hyperlink) +// continue; +// Object flag = c.getData(KEY_DRAW_BORDER); +// if (flag != null) { +// if (flag.equals(Boolean.FALSE)) +// continue; +// if (flag.equals(TREE_BORDER)) +// inactiveBorder = true; +// else if (flag.equals(TEXT_BORDER)) +// textBorder = true; +// } +// if (getBorderStyle() == SWT.BORDER) { +// if (!inactiveBorder && !textBorder) { +// continue; +// } +// if (c instanceof Text || c instanceof Table +// || c instanceof Tree) +// continue; +// } +// if (!inactiveBorder +// && (c instanceof Text || c instanceof CCombo || textBorder)) { +// Rectangle b = c.getBounds(); +// GC gc = event.gc; +// gc.setForeground(c.getBackground()); +// gc.drawRectangle(b.x - 1, b.y - 1, b.width + 1, +// b.height + 1); +// // gc.setForeground(getBorderStyle() == SWT.BORDER ? colors +// // .getBorderColor() : colors.getForeground()); +// gc.setForeground(colors.getBorderColor()); +// if (c instanceof CCombo) +// gc.drawRectangle(b.x - 1, b.y - 1, b.width + 1, +// b.height + 1); +// else +// gc.drawRectangle(b.x - 1, b.y - 2, b.width + 1, +// b.height + 3); +// } else if (inactiveBorder || c instanceof Table +// || c instanceof Tree) { +// Rectangle b = c.getBounds(); +// GC gc = event.gc; +// gc.setForeground(colors.getBorderColor()); +// gc.drawRectangle(b.x - 1, b.y - 1, b.width + 1, +// b.height + 1); +// } +// } +// } +// } + + private static class VisibilityHandler extends FocusAdapter { + public void focusGained(FocusEvent e) { + Widget w = e.widget; + if (w instanceof Control) { + FormUtil.ensureVisible((Control) w); + } + } + } + + private static class KeyboardHandler extends KeyAdapter { + public void keyPressed(KeyEvent e) { + Widget w = e.widget; + if (w instanceof Control) { + if (e.doit) + FormUtil.processKey(e.keyCode, (Control) w); + } + } + } + + private class BoldFontHolder { + private Font normalFont; + + private Font boldFont; + + public BoldFontHolder() { + } + + public Font getBoldFont(Font font) { + createBoldFont(font); + return boldFont; + } + + private void createBoldFont(Font font) { + if (normalFont == null || !normalFont.equals(font)) { + normalFont = font; + dispose(); + } + if (boldFont == null) { + boldFont = FormFonts.getInstance().getBoldFont(colors.getDisplay(), + normalFont); + } + } + + public void dispose() { + if (boldFont != null) { + FormFonts.getInstance().markFinished(boldFont); + boldFont = null; + } + } + } + + /** + * Creates a toolkit that is self-sufficient (will manage its own colors). + *

+ * Clients that call this method must call {@link #dispose()} when they + * are finished using the toolkit. + * + */ + public FormToolkit(Display display) { + this(new FormColors(display)); + } + + /** + * Creates a toolkit that will use the provided (shared) colors. The toolkit + * will dispose the colors if and only if they are not marked as + * shared via the markShared() method. + *

+ * Clients that call this method must call {@link #dispose()} when they + * are finished using the toolkit. + * + * @param colors + * the shared colors + */ + public FormToolkit(FormColors colors) { + this.colors = colors; + initialize(); + } + + /** + * Creates a button as a part of the form. + * + * @param parent + * the button parent + * @param text + * an optional text for the button (can be null) + * @param style + * the button style (for example, SWT.PUSH) + * @return the button widget + */ + public Button createButton(Composite parent, String text, int style) { + Button button = new Button(parent, style | SWT.FLAT | orientation); + if (text != null) + button.setText(text); + adapt(button, true, true); + return button; + } + + /** + * Creates the composite as a part of the form. + * + * @param parent + * the composite parent + * @return the composite widget + */ + public Composite createComposite(Composite parent) { + return createComposite(parent, SWT.NULL); + } + + /** + * Creates the composite as part of the form using the provided style. + * + * @param parent + * the composite parent + * @param style + * the composite style + * @return the composite widget + */ + public Composite createComposite(Composite parent, int style) { +// Composite composite = new LayoutComposite(parent, style | orientation); + Composite composite = new Composite(parent, style | orientation); + adapt(composite); + return composite; + } + + /** + * Creats the composite that can server as a separator between various parts + * of a form. Separator height should be controlled by setting the height + * hint on the layout data for the composite. + * + * @param parent + * the separator parent + * @return the separator widget + */ +// RAP [rh] createCompositeSeparator: currently no useful implementation possible, delete? + public Composite createCompositeSeparator(Composite parent) { + final Composite composite = new Composite(parent, orientation); +// RAP [rh] GC and paint events missing +// composite.addListener(SWT.Paint, new Listener() { +// public void handleEvent(Event e) { +// if (composite.isDisposed()) +// return; +// Rectangle bounds = composite.getBounds(); +// GC gc = e.gc; +// gc.setForeground(colors.getColor(IFormColors.SEPARATOR)); +// if (colors.getBackground() != null) +// gc.setBackground(colors.getBackground()); +// gc.fillGradientRectangle(0, 0, bounds.width, bounds.height, +// false); +// } +// }); +// if (parent instanceof Section) +// ((Section) parent).setSeparatorControl(composite); + return composite; + } + + /** + * Creates a label as a part of the form. + * + * @param parent + * the label parent + * @param text + * the label text + * @return the label widget + */ + public Label createLabel(Composite parent, String text) { + return createLabel(parent, text, SWT.NONE); + } + + /** + * Creates a label as a part of the form. + * + * @param parent + * the label parent + * @param text + * the label text + * @param style + * the label style + * @return the label widget + */ + public Label createLabel(Composite parent, String text, int style) { + Label label = new Label(parent, style | orientation); + if (text != null) + label.setText(text); + adapt(label, false, false); + return label; + } + + /** + * Creates a hyperlink as a part of the form. The hyperlink will be added to + * the hyperlink group that belongs to this toolkit. + * + * @param parent + * the hyperlink parent + * @param text + * the text of the hyperlink + * @param style + * the hyperlink style + * @return the hyperlink widget + */ +// public Hyperlink createHyperlink(Composite parent, String text, int style) { +// Hyperlink hyperlink = new Hyperlink(parent, style | orientation); +// if (text != null) +// hyperlink.setText(text); +// hyperlink.addFocusListener(visibilityHandler); +// hyperlink.addKeyListener(keyboardHandler); +// hyperlinkGroup.add(hyperlink); +// return hyperlink; +// } + + /** + * Creates an image hyperlink as a part of the form. The hyperlink will be + * added to the hyperlink group that belongs to this toolkit. + * + * @param parent + * the hyperlink parent + * @param style + * the hyperlink style + * @return the image hyperlink widget + */ +// public ImageHyperlink createImageHyperlink(Composite parent, int style) { +// ImageHyperlink hyperlink = new ImageHyperlink(parent, style +// | orientation); +// hyperlink.addFocusListener(visibilityHandler); +// hyperlink.addKeyListener(keyboardHandler); +// hyperlinkGroup.add(hyperlink); +// return hyperlink; +// } + + /** + * Creates a rich text as a part of the form. + * + * @param parent + * the rich text parent + * @param trackFocus + * if true, the toolkit will monitor focus + * transfers to ensure that the hyperlink in focus is visible in + * the form. + * @return the rich text widget + * @since 1.2 + */ +// public FormText createFormText(Composite parent, boolean trackFocus) { +// FormText engine = new FormText(parent, SWT.WRAP | orientation); +// engine.marginWidth = 1; +// engine.marginHeight = 0; +// engine.setHyperlinkSettings(getHyperlinkGroup()); +// adapt(engine, trackFocus, true); +// engine.setMenu(parent.getMenu()); +// return engine; +// } + + /** + * Adapts a control to be used in a form that is associated with this + * toolkit. This involves adjusting colors and optionally adding handlers to + * ensure focus tracking and keyboard management. + * + * @param control + * a control to adapt + * @param trackFocus + * if true, form will be scrolled horizontally + * and/or vertically if needed to ensure that the control is + * visible when it gains focus. Set it to false if + * the control is not capable of gaining focus. + * @param trackKeyboard + * if true, the control that is capable of + * gaining focus will be tracked for certain keys that are + * important to the underlying form (for example, PageUp, + * PageDown, ScrollUp, ScrollDown etc.). Set it to + * false if the control is not capable of gaining + * focus or these particular key event are already used by the + * control. + */ + public void adapt(Control control, boolean trackFocus, boolean trackKeyboard) { + control.setBackground(colors.getBackground()); + control.setForeground(colors.getForeground()); +// if (control instanceof ExpandableComposite) { +// ExpandableComposite ec = (ExpandableComposite) control; +// if (ec.toggle != null) { +// if (trackFocus) +// ec.toggle.addFocusListener(visibilityHandler); +// if (trackKeyboard) +// ec.toggle.addKeyListener(keyboardHandler); +// } +// if (ec.textLabel != null) { +// if (trackFocus) +// ec.textLabel.addFocusListener(visibilityHandler); +// if (trackKeyboard) +// ec.textLabel.addKeyListener(keyboardHandler); +// } +// return; +// } + if (trackFocus) + control.addFocusListener(visibilityHandler); + if (trackKeyboard) + control.addKeyListener(keyboardHandler); + } + + /** + * Adapts a composite to be used in a form associated with this toolkit. + * + * @param composite + * the composite to adapt + */ + public void adapt(Composite composite) { + composite.setBackground(colors.getBackground()); + composite.addMouseListener(new MouseAdapter() { + public void mouseDown(MouseEvent e) { + ((Control) e.widget).setFocus(); + } + }); + if (composite.getParent() != null) + composite.setMenu(composite.getParent().getMenu()); + } + + /** + * A helper method that ensures the provided control is visible when + * ScrolledComposite is somewhere in the parent chain. If scroll bars are + * visible and the control is clipped, the client of the scrolled composite + * will be scrolled to reveal the control. + * + * @param c + * the control to reveal + */ + public static void ensureVisible(Control c) { + FormUtil.ensureVisible(c); + } + + /** + * Creates a section as a part of the form. + * + * @param parent + * the section parent + * @param sectionStyle + * the section style + * @return the section widget + */ +// public Section createSection(Composite parent, int sectionStyle) { +// Section section = new Section(parent, orientation, sectionStyle); +// section.setMenu(parent.getMenu()); +// adapt(section, true, true); +// if (section.toggle != null) { +// section.toggle.setHoverDecorationColor(colors +// .getColor(IFormColors.TB_TOGGLE_HOVER)); +// section.toggle.setDecorationColor(colors +// .getColor(IFormColors.TB_TOGGLE)); +// } +// section.setFont(boldFontHolder.getBoldFont(parent.getFont())); +// if ((sectionStyle & Section.TITLE_BAR) != 0 +// || (sectionStyle & Section.SHORT_TITLE_BAR) != 0) { +// colors.initializeSectionToolBarColors(); +// section.setTitleBarBackground(colors.getColor(IFormColors.TB_BG)); +// section.setTitleBarBorderColor(colors +// .getColor(IFormColors.TB_BORDER)); +// } +// // call setTitleBarForeground regardless as it also sets the label color +// section.setTitleBarForeground(colors +// .getColor(IFormColors.TB_TOGGLE)); +// return section; +// } + + /** + * Creates an expandable composite as a part of the form. + * + * @param parent + * the expandable composite parent + * @param expansionStyle + * the expandable composite style + * @return the expandable composite widget + */ +// public ExpandableComposite createExpandableComposite(Composite parent, +// int expansionStyle) { +// ExpandableComposite ec = new ExpandableComposite(parent, orientation, +// expansionStyle); +// ec.setMenu(parent.getMenu()); +// adapt(ec, true, true); +// ec.setFont(boldFontHolder.getBoldFont(ec.getFont())); +// return ec; +// } + + /** + * Creates a separator label as a part of the form. + * + * @param parent + * the separator parent + * @param style + * the separator style + * @return the separator label + */ + public Label createSeparator(Composite parent, int style) { + Label label = new Label(parent, SWT.SEPARATOR | style | orientation); + label.setBackground(colors.getBackground()); + label.setForeground(colors.getBorderColor()); + return label; + } + + /** + * Creates a table as a part of the form. + * + * @param parent + * the table parent + * @param style + * the table style + * @return the table widget + */ + public Table createTable(Composite parent, int style) { + Table table = new Table(parent, style | borderStyle | orientation); + adapt(table, false, false); + // hookDeleteListener(table); + return table; + } + + /** + * Creates a text as a part of the form. + * + * @param parent + * the text parent + * @param value + * the text initial value + * @return the text widget + */ + public Text createText(Composite parent, String value) { + return createText(parent, value, SWT.SINGLE); + } + + /** + * Creates a text as a part of the form. + * + * @param parent + * the text parent + * @param value + * the text initial value + * @param style + * the text style + * @return the text widget + */ + public Text createText(Composite parent, String value, int style) { + Text text = new Text(parent, borderStyle | style | orientation); + if (value != null) + text.setText(value); + text.setForeground(colors.getForeground()); + text.setBackground(colors.getBackground()); + text.addFocusListener(visibilityHandler); + return text; + } + + /** + * Creates a tree widget as a part of the form. + * + * @param parent + * the tree parent + * @param style + * the tree style + * @return the tree widget + */ + public Tree createTree(Composite parent, int style) { + Tree tree = new Tree(parent, borderStyle | style | orientation); + adapt(tree, false, false); + // hookDeleteListener(tree); + return tree; + } + + /** + * Creates a scrolled form widget in the provided parent. If you do not + * require scrolling because there is already a scrolled composite up the + * parent chain, use 'createForm' instead. + * + * @param parent + * the scrolled form parent + * @return the form that can scroll itself + * @see #createForm + */ + public ScrolledComposite createScrolledForm(Composite parent) { + ScrolledComposite form = new ScrolledComposite(parent, SWT.V_SCROLL + | SWT.H_SCROLL | orientation); + form.setExpandHorizontal(true); + form.setExpandVertical(true); + form.setBackground(colors.getBackground()); + form.setForeground(colors.getColor(IFormColors.TITLE)); + form.setFont(JFaceResources.getHeaderFont()); + return form; + } + + /** + * Creates a form widget in the provided parent. Note that this widget does + * not scroll its content, so make sure there is a scrolled composite up the + * parent chain. If you require scrolling, use 'createScrolledForm' instead. + * + * @param parent + * the form parent + * @return the form that does not scroll + * @see #createScrolledForm + */ +// public Form createForm(Composite parent) { +// Form formContent = new Form(parent, orientation); +// formContent.setBackground(colors.getBackground()); +// formContent.setForeground(colors.getColor(IFormColors.TITLE)); +// formContent.setFont(JFaceResources.getHeaderFont()); +// return formContent; +// } + + /** + * Takes advantage of the gradients and other capabilities to decorate the + * form heading using colors computed based on the current skin and + * operating system. + * + * @param form + * the form to decorate + */ + +// public void decorateFormHeading(Form form) { +// Color top = colors.getColor(IFormColors.H_GRADIENT_END); +// Color bot = colors.getColor(IFormColors.H_GRADIENT_START); +// form.setTextBackground(new Color[] { top, bot }, new int[] { 100 }, +// true); +// form.setHeadColor(IFormColors.H_BOTTOM_KEYLINE1, colors +// .getColor(IFormColors.H_BOTTOM_KEYLINE1)); +// form.setHeadColor(IFormColors.H_BOTTOM_KEYLINE2, colors +// .getColor(IFormColors.H_BOTTOM_KEYLINE2)); +// form.setHeadColor(IFormColors.H_HOVER_LIGHT, colors +// .getColor(IFormColors.H_HOVER_LIGHT)); +// form.setHeadColor(IFormColors.H_HOVER_FULL, colors +// .getColor(IFormColors.H_HOVER_FULL)); +// form.setHeadColor(IFormColors.TB_TOGGLE, colors +// .getColor(IFormColors.TB_TOGGLE)); +// form.setHeadColor(IFormColors.TB_TOGGLE_HOVER, colors +// .getColor(IFormColors.TB_TOGGLE_HOVER)); +// form.setSeparatorVisible(true); +// } + + /** + * Creates a scrolled page book widget as a part of the form. + * + * @param parent + * the page book parent + * @param style + * the text style + * @return the scrolled page book widget + */ +// public ScrolledPageBook createPageBook(Composite parent, int style) { +// ScrolledPageBook book = new ScrolledPageBook(parent, style +// | orientation); +// adapt(book, true, true); +// book.setMenu(parent.getMenu()); +// return book; +// } + + /** + * Disposes the toolkit. + */ + public void dispose() { + if (isDisposed) { + return; + } + isDisposed = true; + if (colors.isShared() == false) { + colors.dispose(); + colors = null; + } + boldFontHolder.dispose(); + } + + /** + * Returns the hyperlink group that manages hyperlinks for this toolkit. + * + * @return the hyperlink group + */ +// public HyperlinkGroup getHyperlinkGroup() { +// return hyperlinkGroup; +// } + + /** + * Sets the background color for the entire toolkit. The method delegates + * the call to the FormColors object and also updates the hyperlink group so + * that hyperlinks and other objects are in sync. + * + * @param bg + * the new background color + */ + public void setBackground(Color bg) { +// hyperlinkGroup.setBackground(bg); + colors.setBackground(bg); + } + + /** + * Refreshes the hyperlink colors by loading from JFace settings. + */ +// public void refreshHyperlinkColors() { +// hyperlinkGroup.initializeDefaultForegrounds(colors.getDisplay()); +// } + +// RAP [rh] paintBordersFor not useful as no GC to actually paint borders +// /** +// * Paints flat borders for widgets created by this toolkit within the +// * provided parent. Borders will not be painted if the global border style +// * is SWT.BORDER (i.e. if native borders are used). Call this method during +// * creation of a form composite to get the borders of its children painted. +// * Care should be taken when selection layout margins. At least one pixel +// * pargin width and height must be chosen to allow the toolkit to paint the +// * border on the parent around the widgets. +// *

+// * Borders are painted for some controls that are selected by the toolkit by +// * default. If a control needs a border but is not on its list, it is +// * possible to force border in the following way: +// * +// *

+//	 *
+//	 *
+//	 *
+//	 *             widget.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TREE_BORDER);
+//	 *
+//	 *             or
+//	 *
+//	 *             widget.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER);
+//	 *
+//	 *
+//	 *
+//	 * 
+// * +// * @param parent +// * the parent that owns the children for which the border needs +// * to be painted. +// */ +// public void paintBordersFor(Composite parent) { +// // if (borderStyle == SWT.BORDER) +// // return; +// if (borderPainter == null) +// borderPainter = new BorderPainter(); +// parent.addPaintListener(borderPainter); +// } + + /** + * Returns the colors used by this toolkit. + * + * @return the color object + */ + public FormColors getColors() { + return colors; + } + + /** + * Returns the border style used for various widgets created by this + * toolkit. The intent of the toolkit is to create controls with styles that + * yield a 'flat' appearance. On systems where the native borders are + * already flat, we set the style to SWT.BORDER and don't paint the borders + * ourselves. Otherwise, the style is set to SWT.NULL, and borders are + * painted by the toolkit. + * + * @return the global border style + */ + public int getBorderStyle() { + return borderStyle; + } + + /** + * Returns the margin required around the children whose border is being + * painted by the toolkit using {@link #paintBordersFor(Composite)}. Since + * the border is painted around the controls on the parent, a number of + * pixels needs to be reserved for this border. For windowing systems where + * the native border is used, this margin is 0. + * + * @return the margin in the parent when children have their border painted + */ + public int getBorderMargin() { + return getBorderStyle() == SWT.BORDER ? 0 : 2; + } + + /** + * Sets the border style to be used when creating widgets. The toolkit + * chooses the correct style based on the platform but this value can be + * changed using this method. + * + * @param style + * SWT.BORDER or SWT.NULL + * @see #getBorderStyle + */ + public void setBorderStyle(int style) { + this.borderStyle = style; + } + + /** + * A utility method that ensures that the control is visible in the scrolled + * composite. The prerequisite for this method is that the control has a + * class that extends ScrolledComposite somewhere in the parent chain. If + * the control is partially or fully clipped, the composite is scrolled to + * set by setting the origin to the control origin. + * + * @param c + * the control to make visible + * @param verticalOnly + * if true, the scrolled composite will be + * scrolled only vertically if needed. Otherwise, the scrolled + * composite origin will be set to the control origin. + */ + public static void setControlVisible(Control c, boolean verticalOnly) { + ScrolledComposite scomp = FormUtil.getScrolledComposite(c); + if (scomp == null) + return; + Point location = FormUtil.getControlLocation(scomp, c); + scomp.setOrigin(location); + } + + private void initialize() { + initializeBorderStyle(); +// hyperlinkGroup = new HyperlinkGroup(colors.getDisplay()); +// hyperlinkGroup.setBackground(colors.getBackground()); + visibilityHandler = new VisibilityHandler(); + keyboardHandler = new KeyboardHandler(); + boldFontHolder = new BoldFontHolder(); + } + +// RAP [rh] revise detection of border style: can't ask OS here + private void initializeBorderStyle() { +// String osname = System.getProperty("os.name"); //$NON-NLS-1$ +// String osversion = System.getProperty("os.version"); //$NON-NLS-1$ +// if (osname.startsWith("Windows") && "5.1".compareTo(osversion) <= 0) { //$NON-NLS-1$ //$NON-NLS-2$ +// // Skinned widgets used on newer Windows (e.g. XP (5.1), Vista +// // (6.0)) +// // Check for Windows Classic. If not used, set the style to BORDER +// RGB rgb = colors.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND); +// if (rgb.red != 212 || rgb.green != 208 || rgb.blue != 200) +// borderStyle = SWT.BORDER; +// } else if (osname.startsWith("Mac")) //$NON-NLS-1$ +// borderStyle = SWT.BORDER; + + borderStyle = SWT.BORDER; + } + + /** + * Returns the orientation that all the widgets created by this toolkit will + * inherit, if set. Can be SWT.NULL, + * SWT.LEFT_TO_RIGHT and SWT.RIGHT_TO_LEFT. + * + * @return orientation style for this toolkit, or SWT.NULL if + * not set. The default orientation is inherited from the Window + * default orientation. + * @see org.eclipse.jface.window.Window#getDefaultOrientation() + */ + + public int getOrientation() { + return orientation; + } + + /** + * Sets the orientation that all the widgets created by this toolkit will + * inherit. Can be SWT.NULL, SWT.LEFT_TO_RIGHT + * and SWT.RIGHT_TO_LEFT. + * + * @param orientation + * style for this toolkit. + */ + + public void setOrientation(int orientation) { + this.orientation = orientation; + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormUtil.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormUtil.java new file mode 100644 index 0000000..76e3f11 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormUtil.java @@ -0,0 +1,522 @@ +package org.argeo.cms.ui.eclipse.forms; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.events.MouseEvent; +//import org.eclipse.swt.graphics.Device; +import org.eclipse.swt.graphics.FontMetrics; +import org.eclipse.swt.graphics.GC; +//import org.eclipse.swt.graphics.Image; +//import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Layout; +//import org.eclipse.swt.widgets.ScrollBar; +import org.eclipse.swt.widgets.Text; +//import org.eclipse.ui.forms.widgets.ColumnLayout; +//import org.eclipse.ui.forms.widgets.Form; +//import org.eclipse.ui.forms.widgets.FormText; +//import org.eclipse.ui.forms.widgets.FormToolkit; +//import org.eclipse.ui.forms.widgets.ILayoutExtension; +// +//import com.ibm.icu.text.BreakIterator; + +public class FormUtil { + public static final String PLUGIN_ID = "org.eclipse.ui.forms"; //$NON-NLS-1$ + + static final int H_SCROLL_INCREMENT = 5; + + static final int V_SCROLL_INCREMENT = 64; + + public static final String DEBUG = PLUGIN_ID + "/debug"; //$NON-NLS-1$ + + public static final String DEBUG_TEXT = DEBUG + "/text"; //$NON-NLS-1$ + public static final String DEBUG_TEXTSIZE = DEBUG + "/textsize"; //$NON-NLS-1$ + + public static final String DEBUG_FOCUS = DEBUG + "/focus"; //$NON-NLS-1$ + + public static final String FOCUS_SCROLLING = "focusScrolling"; //$NON-NLS-1$ + + public static final String IGNORE_BODY = "__ignore_body__"; //$NON-NLS-1$ + + public static Text createText(Composite parent, String label, + FormToolkit factory) { + return createText(parent, label, factory, 1); + } + + public static Text createText(Composite parent, String label, + FormToolkit factory, int span) { + factory.createLabel(parent, label); + Text text = factory.createText(parent, ""); //$NON-NLS-1$ + int hfill = span == 1 ? GridData.FILL_HORIZONTAL + : GridData.HORIZONTAL_ALIGN_FILL; + GridData gd = new GridData(hfill | GridData.VERTICAL_ALIGN_CENTER); + gd.horizontalSpan = span; + text.setLayoutData(gd); + return text; + } + + public static Text createText(Composite parent, String label, + FormToolkit factory, int span, int style) { + Label l = factory.createLabel(parent, label); + if ((style & SWT.MULTI) != 0) { + GridData gd = new GridData(GridData.VERTICAL_ALIGN_BEGINNING); + l.setLayoutData(gd); + } + Text text = factory.createText(parent, "", style); //$NON-NLS-1$ + int hfill = span == 1 ? GridData.FILL_HORIZONTAL + : GridData.HORIZONTAL_ALIGN_FILL; + GridData gd = new GridData(hfill | GridData.VERTICAL_ALIGN_CENTER); + gd.horizontalSpan = span; + text.setLayoutData(gd); + return text; + } + + public static Text createText(Composite parent, FormToolkit factory, + int span) { + Text text = factory.createText(parent, ""); //$NON-NLS-1$ + int hfill = span == 1 ? GridData.FILL_HORIZONTAL + : GridData.HORIZONTAL_ALIGN_FILL; + GridData gd = new GridData(hfill | GridData.VERTICAL_ALIGN_CENTER); + gd.horizontalSpan = span; + text.setLayoutData(gd); + return text; + } + + public static int computeMinimumWidth(GC gc, String text) { +// BreakIterator wb = BreakIterator.getWordInstance(); +// wb.setText(text); +// int last = 0; +// +// int width = 0; +// +// for (int loc = wb.first(); loc != BreakIterator.DONE; loc = wb.next()) { +// String word = text.substring(last, loc); +// Point extent = gc.textExtent(word); +// width = Math.max(width, extent.x); +// last = loc; +// } +// String lastWord = text.substring(last); +// Point extent = gc.textExtent(lastWord); +// width = Math.max(width, extent.x); +// return width; + return 0; + } + + public static Point computeWrapSize(GC gc, String text, int wHint) { +// BreakIterator wb = BreakIterator.getWordInstance(); +// wb.setText(text); + FontMetrics fm = gc.getFontMetrics(); + int lineHeight = fm.getHeight(); + + int saved = 0; + int last = 0; + int height = lineHeight; + int maxWidth = 0; +// for (int loc = wb.first(); loc != BreakIterator.DONE; loc = wb.next()) { +// String word = text.substring(saved, loc); +// Point extent = gc.textExtent(word); +// if (extent.x > wHint) { +// // overflow +// saved = last; +// height += extent.y; +// // switch to current word so maxWidth will accommodate very long single words +// word = text.substring(last, loc); +// extent = gc.textExtent(word); +// } +// maxWidth = Math.max(maxWidth, extent.x); +// last = loc; +// } + /* + * Correct the height attribute in case it was calculated wrong due to wHint being less than maxWidth. + * The recursive call proved to be the only thing that worked in all cases. Some attempts can be made + * to estimate the height, but the algorithm needs to be run again to be sure. + */ + if (maxWidth > wHint) + return computeWrapSize(gc, text, maxWidth); + return new Point(maxWidth, height); + } + +// RAP [rh] paintWrapText unnecessary +// public static void paintWrapText(GC gc, String text, Rectangle bounds) { +// paintWrapText(gc, text, bounds, false); +// } + +// RAP [rh] paintWrapText unnecessary +// public static void paintWrapText(GC gc, String text, Rectangle bounds, +// boolean underline) { +// BreakIterator wb = BreakIterator.getWordInstance(); +// wb.setText(text); +// FontMetrics fm = gc.getFontMetrics(); +// int lineHeight = fm.getHeight(); +// int descent = fm.getDescent(); +// +// int saved = 0; +// int last = 0; +// int y = bounds.y; +// int width = bounds.width; +// +// for (int loc = wb.first(); loc != BreakIterator.DONE; loc = wb.next()) { +// String line = text.substring(saved, loc); +// Point extent = gc.textExtent(line); +// +// if (extent.x > width) { +// // overflow +// String prevLine = text.substring(saved, last); +// gc.drawText(prevLine, bounds.x, y, true); +// if (underline) { +// Point prevExtent = gc.textExtent(prevLine); +// int lineY = y + lineHeight - descent + 1; +// gc +// .drawLine(bounds.x, lineY, bounds.x + prevExtent.x, +// lineY); +// } +// +// saved = last; +// y += lineHeight; +// } +// last = loc; +// } +// // paint the last line +// String lastLine = text.substring(saved, last); +// gc.drawText(lastLine, bounds.x, y, true); +// if (underline) { +// int lineY = y + lineHeight - descent + 1; +// Point lastExtent = gc.textExtent(lastLine); +// gc.drawLine(bounds.x, lineY, bounds.x + lastExtent.x, lineY); +// } +// } + + public static ScrolledComposite getScrolledComposite(Control c) { + Composite parent = c.getParent(); + + while (parent != null) { + if (parent instanceof ScrolledComposite) { + return (ScrolledComposite) parent; + } + parent = parent.getParent(); + } + return null; + } + + public static void ensureVisible(Control c) { + ScrolledComposite scomp = getScrolledComposite(c); + if (scomp != null) { + Object data = scomp.getData(FOCUS_SCROLLING); + if (data == null || !data.equals(Boolean.FALSE)) + FormUtil.ensureVisible(scomp, c); + } + } + + public static void ensureVisible(ScrolledComposite scomp, Control control) { + // if the control is a FormText we do not need to scroll since it will + // ensure visibility of its segments as necessary +// if (control instanceof FormText) +// return; + Point controlSize = control.getSize(); + Point controlOrigin = getControlLocation(scomp, control); + ensureVisible(scomp, controlOrigin, controlSize); + } + + public static void ensureVisible(ScrolledComposite scomp, + Point controlOrigin, Point controlSize) { + Rectangle area = scomp.getClientArea(); + Point scompOrigin = scomp.getOrigin(); + + int x = scompOrigin.x; + int y = scompOrigin.y; + + // horizontal right, but only if the control is smaller + // than the client area + if (controlSize.x < area.width + && (controlOrigin.x + controlSize.x > scompOrigin.x + + area.width)) { + x = controlOrigin.x + controlSize.x - area.width; + } + // horizontal left - make sure the left edge of + // the control is showing + if (controlOrigin.x < x) { + if (controlSize.x < area.width) + x = controlOrigin.x + controlSize.x - area.width; + else + x = controlOrigin.x; + } + // vertical bottom + if (controlSize.y < area.height + && (controlOrigin.y + controlSize.y > scompOrigin.y + + area.height)) { + y = controlOrigin.y + controlSize.y - area.height; + } + // vertical top - make sure the top of + // the control is showing + if (controlOrigin.y < y) { + if (controlSize.y < area.height) + y = controlOrigin.y + controlSize.y - area.height; + else + y = controlOrigin.y; + } + + if (scompOrigin.x != x || scompOrigin.y != y) { + // scroll to reveal + scomp.setOrigin(x, y); + } + } + + public static void ensureVisible(ScrolledComposite scomp, Control control, + MouseEvent e) { + Point controlOrigin = getControlLocation(scomp, control); + int rX = controlOrigin.x + e.x; + int rY = controlOrigin.y + e.y; + Rectangle area = scomp.getClientArea(); + Point scompOrigin = scomp.getOrigin(); + + int x = scompOrigin.x; + int y = scompOrigin.y; + // System.out.println("Ensure: area="+area+", origin="+scompOrigin+", + // cloc="+controlOrigin+", csize="+controlSize+", x="+x+", y="+y); + + // horizontal right + if (rX > scompOrigin.x + area.width) { + x = rX - area.width; + } + // horizontal left + else if (rX < x) { + x = rX; + } + // vertical bottom + if (rY > scompOrigin.y + area.height) { + y = rY - area.height; + } + // vertical top + else if (rY < y) { + y = rY; + } + + if (scompOrigin.x != x || scompOrigin.y != y) { + // scroll to reveal + scomp.setOrigin(x, y); + } + } + + public static Point getControlLocation(ScrolledComposite scomp, + Control control) { + int x = 0; + int y = 0; + Control content = scomp.getContent(); + Control currentControl = control; + for (;;) { + if (currentControl == content) + break; + Point location = currentControl.getLocation(); + // if (location.x > 0) + // x += location.x; + // if (location.y > 0) + // y += location.y; + x += location.x; + y += location.y; + currentControl = currentControl.getParent(); + } + return new Point(x, y); + } + + static void scrollVertical(ScrolledComposite scomp, boolean up) { + scroll(scomp, 0, up ? -V_SCROLL_INCREMENT : V_SCROLL_INCREMENT); + } + + static void scrollHorizontal(ScrolledComposite scomp, boolean left) { + scroll(scomp, left ? -H_SCROLL_INCREMENT : H_SCROLL_INCREMENT, 0); + } + + static void scrollPage(ScrolledComposite scomp, boolean up) { + Rectangle clientArea = scomp.getClientArea(); + int increment = up ? -clientArea.height : clientArea.height; + scroll(scomp, 0, increment); + } + + static void scroll(ScrolledComposite scomp, int xoffset, int yoffset) { + Point origin = scomp.getOrigin(); + Point contentSize = scomp.getContent().getSize(); + int xorigin = origin.x + xoffset; + int yorigin = origin.y + yoffset; + xorigin = Math.max(xorigin, 0); + xorigin = Math.min(xorigin, contentSize.x - 1); + yorigin = Math.max(yorigin, 0); + yorigin = Math.min(yorigin, contentSize.y - 1); + scomp.setOrigin(xorigin, yorigin); + } + +// RAP [rh] FormUtil#updatePageIncrement: empty implementation + public static void updatePageIncrement(ScrolledComposite scomp) { +// ScrollBar vbar = scomp.getVerticalBar(); +// if (vbar != null) { +// Rectangle clientArea = scomp.getClientArea(); +// int increment = clientArea.height - 5; +// vbar.setPageIncrement(increment); +// } +// ScrollBar hbar = scomp.getHorizontalBar(); +// if (hbar != null) { +// Rectangle clientArea = scomp.getClientArea(); +// int increment = clientArea.width - 5; +// hbar.setPageIncrement(increment); +// } + } + + public static void processKey(int keyCode, Control c) { + if (c.isDisposed()) { + return; + } + ScrolledComposite scomp = FormUtil.getScrolledComposite(c); + if (scomp != null) { + if (c instanceof Combo) + return; + switch (keyCode) { + case SWT.ARROW_DOWN: + if (scomp.getData("novarrows") == null) //$NON-NLS-1$ + FormUtil.scrollVertical(scomp, false); + break; + case SWT.ARROW_UP: + if (scomp.getData("novarrows") == null) //$NON-NLS-1$ + FormUtil.scrollVertical(scomp, true); + break; + case SWT.ARROW_LEFT: + FormUtil.scrollHorizontal(scomp, true); + break; + case SWT.ARROW_RIGHT: + FormUtil.scrollHorizontal(scomp, false); + break; + case SWT.PAGE_UP: + FormUtil.scrollPage(scomp, true); + break; + case SWT.PAGE_DOWN: + FormUtil.scrollPage(scomp, false); + break; + } + } + } + + public static boolean isWrapControl(Control c) { + if ((c.getStyle() & SWT.WRAP) != 0) + return true; + if (c instanceof Composite) { + return false; +// return ((Composite) c).getLayout() instanceof ILayoutExtension; + } + return false; + } + + public static int getWidthHint(int wHint, Control c) { + boolean wrap = isWrapControl(c); + return wrap ? wHint : SWT.DEFAULT; + } + + public static int getHeightHint(int hHint, Control c) { + if (c instanceof Composite) { + Layout layout = ((Composite) c).getLayout(); +// if (layout instanceof ColumnLayout) +// return hHint; + } + return SWT.DEFAULT; + } + + public static int computeMinimumWidth(Control c, boolean changed) { + if (c instanceof Composite) { + Layout layout = ((Composite) c).getLayout(); +// if (layout instanceof ILayoutExtension) +// return ((ILayoutExtension) layout).computeMinimumWidth( +// (Composite) c, changed); + } + return c.computeSize(FormUtil.getWidthHint(5, c), SWT.DEFAULT, changed).x; + } + + public static int computeMaximumWidth(Control c, boolean changed) { + if (c instanceof Composite) { + Layout layout = ((Composite) c).getLayout(); +// if (layout instanceof ILayoutExtension) +// return ((ILayoutExtension) layout).computeMaximumWidth( +// (Composite) c, changed); + } + return c.computeSize(SWT.DEFAULT, SWT.DEFAULT, changed).x; + } + +// public static Form getForm(Control c) { +// Composite parent = c.getParent(); +// while (parent != null) { +// if (parent instanceof Form) { +// return (Form) parent; +// } +// parent = parent.getParent(); +// } +// return null; +// } + +// RAP [rh] FormUtil#createAlphaMashImage unnecessary +// public static Image createAlphaMashImage(Device device, Image srcImage) { +// Rectangle bounds = srcImage.getBounds(); +// int alpha = 0; +// int calpha = 0; +// ImageData data = srcImage.getImageData(); +// // Create a new image with alpha values alternating +// // between fully transparent (0) and fully opaque (255). +// // This image will show the background through the +// // transparent pixels. +// for (int i = 0; i < bounds.height; i++) { +// // scan line +// alpha = calpha; +// for (int j = 0; j < bounds.width; j++) { +// // column +// data.setAlpha(j, i, alpha); +// alpha = alpha == 255 ? 0 : 255; +// } +// calpha = calpha == 255 ? 0 : 255; +// } +// return new Image(device, data); +// } + + public static boolean mnemonicMatch(String text, char key) { + char mnemonic = findMnemonic(text); + if (mnemonic == '\0') + return false; + return Character.toUpperCase(key) == Character.toUpperCase(mnemonic); + } + + private static char findMnemonic(String string) { + int index = 0; + int length = string.length(); + do { + while (index < length && string.charAt(index) != '&') + index++; + if (++index >= length) + return '\0'; + if (string.charAt(index) != '&') + return string.charAt(index); + index++; + } while (index < length); + return '\0'; + } + + public static void setFocusScrollingEnabled(Control c, boolean enabled) { + ScrolledComposite scomp = null; + + if (c instanceof ScrolledComposite) + scomp = (ScrolledComposite)c; + else + scomp = getScrolledComposite(c); + if (scomp!=null) + scomp.setData(FormUtil.FOCUS_SCROLLING, enabled?null:Boolean.FALSE); + } + + // RAP [rh] FormUtil#setAntialias unnecessary +// public static void setAntialias(GC gc, int style) { +// if (!gc.getAdvanced()) { +// gc.setAdvanced(true); +// if (!gc.getAdvanced()) +// return; +// } +// gc.setAntialias(style); +// } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IFormColors.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IFormColors.java new file mode 100644 index 0000000..cf0e5d3 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IFormColors.java @@ -0,0 +1,102 @@ +package org.argeo.cms.ui.eclipse.forms; + +/** + * A place to hold all the color constants used in the forms package. + * + * @since 1.0 + */ + +public interface IFormColors { + /** + * A prefix for all the keys. + */ + String PREFIX = "org.eclipse.ui.forms."; //$NON-NLS-1$ + /** + * Key for the form title foreground color. + */ + String TITLE = PREFIX + "TITLE"; //$NON-NLS-1$ + + /** + * A prefix for the header color constants. + */ + String H_PREFIX = PREFIX + "H_"; //$NON-NLS-1$ + /* + * A prefix for the section title bar color constants. + */ + String TB_PREFIX = PREFIX + "TB_"; //$NON-NLS-1$ + /** + * Key for the form header background gradient ending color. + */ + String H_GRADIENT_END = H_PREFIX + "GRADIENT_END"; //$NON-NLS-1$ + + /** + * Key for the form header background gradient starting color. + * + */ + String H_GRADIENT_START = H_PREFIX + "GRADIENT_START"; //$NON-NLS-1$ + /** + * Key for the form header bottom keyline 1 color. + * + */ + String H_BOTTOM_KEYLINE1 = H_PREFIX + "BOTTOM_KEYLINE1"; //$NON-NLS-1$ + /** + * Key for the form header bottom keyline 2 color. + * + */ + String H_BOTTOM_KEYLINE2 = H_PREFIX + "BOTTOM_KEYLINE2"; //$NON-NLS-1$ + /** + * Key for the form header light hover color. + * + */ + String H_HOVER_LIGHT = H_PREFIX + "H_HOVER_LIGHT"; //$NON-NLS-1$ + /** + * Key for the form header full hover color. + * + */ + String H_HOVER_FULL = H_PREFIX + "H_HOVER_FULL"; //$NON-NLS-1$ + + /** + * Key for the tree/table border color. + */ + String BORDER = PREFIX + "BORDER"; //$NON-NLS-1$ + + /** + * Key for the section separator color. + */ + String SEPARATOR = PREFIX + "SEPARATOR"; //$NON-NLS-1$ + + /** + * Key for the section title bar background. + */ + String TB_BG = TB_PREFIX + "BG"; //$NON-NLS-1$ + + /** + * Key for the section title bar foreground. + */ + String TB_FG = TB_PREFIX + "FG"; //$NON-NLS-1$ + + /** + * Key for the section title bar gradient. + * @deprecated Since 3.3, this color is not used any more. The + * tool bar gradient is created starting from {@link #TB_BG} to + * the section background color. + */ + String TB_GBG = TB_BG; + + /** + * Key for the section title bar border. + */ + String TB_BORDER = TB_PREFIX + "BORDER"; //$NON-NLS-1$ + + /** + * Key for the section toggle color. Since 3.1, this color is used for all + * section styles. + */ + String TB_TOGGLE = TB_PREFIX + "TOGGLE"; //$NON-NLS-1$ + + /** + * Key for the section toggle hover color. + * + */ + String TB_TOGGLE_HOVER = TB_PREFIX + "TOGGLE_HOVER"; //$NON-NLS-1$ +} \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IFormPart.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IFormPart.java new file mode 100644 index 0000000..954cc03 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IFormPart.java @@ -0,0 +1,108 @@ +package org.argeo.cms.ui.eclipse.forms; + +/** + * Classes that implement this interface can be added to the managed form and + * take part in the form life cycle. The part is initialized with the form and + * will be asked to accept focus. The part can receive form input and can elect + * to do something according to it (for example, select an object that matches + * the input). + *

+ * The form part has two 'out of sync' states in respect to the model(s) that + * feed the form: dirty and stale. When a part is dirty, it + * means that the user interacted with it and now its widgets contain state that + * is newer than the model. In order to sync up with the model, 'commit' needs + * to be called. In contrast, the model can change 'under' the form (as a result + * of some actions outside the form), resulting in data in the model being + * 'newer' than the content presented in the form. A 'stale' form part is + * brought in sync with the model by calling 'refresh'. The part is responsible + * for notifying the form when one of these states change in the part. The form + * reserves the right to handle this notification in the most appropriate way + * for the situation (for example, if the form is in a page of the multi-page + * editor, it may do nothing for stale parts if the page is currently not + * showing). + *

+ * When the form is disposed, each registered part is disposed as well. Parts + * are responsible for releasing any system resources they created and for + * removing themselves as listeners from all event providers. + * + * @see IManagedForm + * @since 1.0 + * + */ +public interface IFormPart { + /** + * Initializes the part. + * + * @param form + * the managed form that manages the part + */ + void initialize(IManagedForm form); + + /** + * Disposes the part allowing it to release allocated resources. + */ + void dispose(); + + /** + * Returns true if the part has been modified with respect to the data + * loaded from the model. + * + * @return true if the part has been modified with respect to the data + * loaded from the model + */ + boolean isDirty(); + + /** + * If part is displaying information loaded from a model, this method + * instructs it to commit the new (modified) data back into the model. + * + * @param onSave + * indicates if commit is called during 'save' operation or for + * some other reason (for example, if form is contained in a + * wizard or a multi-page editor and the user is about to leave + * the page). + */ + void commit(boolean onSave); + + /** + * Notifies the part that an object has been set as overall form's input. + * The part can elect to react by revealing or selecting the object, or do + * nothing if not applicable. + * + * @return true if the part has selected and revealed the + * input object, false otherwise. + */ + boolean setFormInput(Object input); + + /** + * Instructs form part to transfer focus to the widget that should has focus + * in that part. The method can do nothing (if it has no widgets capable of + * accepting focus). + */ + void setFocus(); + + /** + * Tests whether the form part is stale and needs refreshing. Parts can + * receive notification from models that will make their content stale, but + * may need to delay refreshing to improve performance (for example, there + * is no need to immediately refresh a part on a form that is current on a + * hidden page). + *

+ * It is important to differentiate 'stale' and 'dirty' states. Part is + * 'dirty' if user interacted with its editable widgets and changed the + * values. In contrast, part is 'stale' when the data it presents in the + * widgets has been changed in the model without direct user interaction. + * + * @return true if the part needs refreshing, + * false otherwise. + */ + boolean isStale(); + + /** + * Refreshes the part completely from the information freshly obtained from + * the model. The method will not be called if the part is not stale. + * Otherwise, the part is responsible for clearing the 'stale' flag after + * refreshing itself. + */ + void refresh(); +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IManagedForm.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IManagedForm.java new file mode 100644 index 0000000..490d3a3 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IManagedForm.java @@ -0,0 +1,175 @@ +package org.argeo.cms.ui.eclipse.forms; + +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.swt.custom.ScrolledComposite; +//import org.eclipse.ui.forms.widgets.FormToolkit; +//import org.eclipse.ui.forms.widgets.ScrolledForm; + +/** + * Managed form wraps a form widget and adds life cycle methods for form parts. + * A form part is a portion of the form that participates in form life cycle + * events. + *

+ * There is no 1/1 mapping between widgets and form parts. A widget like Section + * can be a part by itself, but a number of widgets can gather around one form + * part. + *

+ * This interface should not be extended or implemented. New form instances + * should be created using ManagedForm. + * + * @see ManagedForm + * @since 1.0 + * @noimplement This interface is not intended to be implemented by clients. + * @noextend This interface is not intended to be extended by clients. + */ +public interface IManagedForm { + /** + * Initializes the form by looping through the managed parts and + * initializing them. Has no effect if already called once. + */ + public void initialize(); + + /** + * Returns the toolkit used by this form. + * + * @return the toolkit + */ + public FormToolkit getToolkit(); + + /** + * Returns the form widget managed by this form. + * + * @return the form widget + */ + public ScrolledComposite getForm(); + + /** + * Reflows the form as a result of the layout change. + * + * @param changed + * if true, discard cached layout information + */ + public void reflow(boolean changed); + + /** + * A part can use this method to notify other parts that implement + * IPartSelectionListener about selection changes. + * + * @param part + * the part that broadcasts the selection + * @param selection + * the selection in the part + */ + public void fireSelectionChanged(IFormPart part, ISelection selection); + + /** + * Returns all the parts currently managed by this form. + * + * @return the managed parts + */ + IFormPart[] getParts(); + + /** + * Adds the new part to the form. + * + * @param part + * the part to add + */ + void addPart(IFormPart part); + + /** + * Removes the part from the form. + * + * @param part + * the part to remove + */ + void removePart(IFormPart part); + + /** + * Sets the input of this page to the provided object. + * + * @param input + * the new page input + * @return true if the form contains this object, + * false otherwise. + */ + boolean setInput(Object input); + + /** + * Returns the current page input. + * + * @return page input object or null if not applicable. + */ + Object getInput(); + + /** + * Tests if form is dirty. A managed form is dirty if at least one managed + * part is dirty. + * + * @return true if at least one managed part is dirty, + * false otherwise. + */ + boolean isDirty(); + + /** + * Notifies the form that the dirty state of one of its parts has changed. + * The global dirty state of the form can be obtained by calling 'isDirty'. + * + * @see #isDirty + */ + void dirtyStateChanged(); + + /** + * Commits the dirty form. All pending changes in the widgets are flushed + * into the model. + * + * @param onSave + */ + void commit(boolean onSave); + + /** + * Tests if form is stale. A managed form is stale if at least one managed + * part is stale. This can happen when the underlying model changes, + * resulting in the presentation of the part being out of sync with the + * model and needing refreshing. + * + * @return true if the form is stale, false + * otherwise. + */ + boolean isStale(); + + /** + * Notifies the form that the stale state of one of its parts has changed. + * The global stale state of the form can be obtained by calling 'isStale'. + */ + void staleStateChanged(); + + /** + * Refreshes the form by refreshing every part that is stale. + */ + void refresh(); + + /** + * Sets the container that owns this form. Depending on the context, the + * container may be wizard, editor page, editor etc. + * + * @param container + * the container of this form + */ + void setContainer(Object container); + + /** + * Returns the container of this form. + * + * @return the form container + */ + Object getContainer(); + + /** + * Returns the message manager that will keep track of messages in this + * form. + * + * @return the message manager instance + */ +// IMessageManager getMessageManager(); +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IPartSelectionListener.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IPartSelectionListener.java new file mode 100644 index 0000000..0f557d4 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IPartSelectionListener.java @@ -0,0 +1,23 @@ +package org.argeo.cms.ui.eclipse.forms; + +import org.eclipse.jface.viewers.ISelection; + +/** + * Form parts can implement this interface if they want to be + * notified when another part on the same form changes selection + * state. + * + * @see IFormPart + * @since 1.0 + */ +public interface IPartSelectionListener { + /** + * Called when the provided part has changed selection state. + * + * @param part + * the selection source + * @param selection + * the new selection + */ + public void selectionChanged(IFormPart part, ISelection selection); +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/ManagedForm.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/ManagedForm.java new file mode 100644 index 0000000..4140465 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/ManagedForm.java @@ -0,0 +1,323 @@ +package org.argeo.cms.ui.eclipse.forms; + +import java.util.Vector; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.widgets.Composite; +//import org.eclipse.ui.forms.widgets.FormToolkit; +//import org.eclipse.ui.forms.widgets.ScrolledForm; + +/** + * Managed form wraps a form widget and adds life cycle methods for form parts. + * A form part is a portion of the form that participates in form life cycle + * events. + *

+ * There is requirement for 1/1 mapping between widgets and form parts. A widget + * like Section can be a part by itself, but a number of widgets can join around + * one form part. + *

+ * Note to developers: this class is left public to allow its use beyond the + * original intention (inside a multi-page editor's page). You should limit the + * use of this class to make new instances inside a form container (wizard page, + * dialog etc.). Clients that need access to the class should not do it + * directly. Instead, they should do it through IManagedForm interface as much + * as possible. + * + * @since 1.0 + */ +public class ManagedForm implements IManagedForm { + private Object input; + + private ScrolledComposite form; + + private FormToolkit toolkit; + + private Object container; + + private boolean ownsToolkit; + + private boolean initialized; + + private Vector parts = new Vector(); + + /** + * Creates a managed form in the provided parent. Form toolkit and widget + * will be created and owned by this object. + * + * @param parent + * the parent widget + */ + public ManagedForm(Composite parent) { + toolkit = new FormToolkit(parent.getDisplay()); + ownsToolkit = true; + form = toolkit.createScrolledForm(parent); + + } + + /** + * Creates a managed form that will use the provided toolkit and + * + * @param toolkit + * @param form + */ + public ManagedForm(FormToolkit toolkit, ScrolledComposite form) { + this.form = form; + this.toolkit = toolkit; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#addPart(org.eclipse.ui.forms.IFormPart) + */ + public void addPart(IFormPart part) { + parts.add(part); + part.initialize(this); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#removePart(org.eclipse.ui.forms.IFormPart) + */ + public void removePart(IFormPart part) { + parts.remove(part); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#getParts() + */ + public IFormPart[] getParts() { + return (IFormPart[]) parts.toArray(new IFormPart[parts.size()]); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#getToolkit() + */ + public FormToolkit getToolkit() { + return toolkit; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#getForm() + */ + public ScrolledComposite getForm() { + return form; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#reflow(boolean) + */ + public void reflow(boolean changed) { +// form.reflow(changed); + } + + /** + * A part can use this method to notify other parts that implement + * IPartSelectionListener about selection changes. + * + * @param part + * the part that broadcasts the selection + * @param selection + * the selection in the part + * @see IPartSelectionListener + */ + public void fireSelectionChanged(IFormPart part, ISelection selection) { + for (int i = 0; i < parts.size(); i++) { + IFormPart cpart = (IFormPart) parts.get(i); + if (part.equals(cpart)) + continue; +// if (cpart instanceof IPartSelectionListener) { +// ((IPartSelectionListener) cpart).selectionChanged(part, +// selection); +// } + } + } + + /** + * Initializes the form by looping through the managed parts and + * initializing them. Has no effect if already called once. + */ + public void initialize() { + if (initialized) + return; + for (int i = 0; i < parts.size(); i++) { + IFormPart part = (IFormPart) parts.get(i); + part.initialize(this); + } + initialized = true; + } + + /** + * Disposes all the parts in this form. + */ + public void dispose() { + for (int i = 0; i < parts.size(); i++) { + IFormPart part = (IFormPart) parts.get(i); + part.dispose(); + } + if (ownsToolkit) { + toolkit.dispose(); + } + } + + /** + * Refreshes the form by refreshes all the stale parts. Since 3.1, this + * method is performed on a UI thread when called from another thread so it + * is not needed to wrap the call in Display.syncExec or + * asyncExec. + */ + public void refresh() { + Thread t = Thread.currentThread(); + Thread dt = toolkit.getColors().getDisplay().getThread(); + if (t.equals(dt)) + doRefresh(); + else { + toolkit.getColors().getDisplay().asyncExec(new Runnable() { + public void run() { + doRefresh(); + } + }); + } + } + + private void doRefresh() { + int nrefreshed = 0; + for (int i = 0; i < parts.size(); i++) { + IFormPart part = (IFormPart) parts.get(i); + if (part.isStale()) { + part.refresh(); + nrefreshed++; + } + } +// if (nrefreshed > 0) +// form.reflow(true); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#commit(boolean) + */ + public void commit(boolean onSave) { + for (int i = 0; i < parts.size(); i++) { + IFormPart part = (IFormPart) parts.get(i); + if (part.isDirty()) + part.commit(onSave); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#setInput(java.lang.Object) + */ + public boolean setInput(Object input) { + boolean pageResult = false; + + this.input = input; + for (int i = 0; i < parts.size(); i++) { + IFormPart part = (IFormPart) parts.get(i); + boolean result = part.setFormInput(input); + if (result) + pageResult = true; + } + return pageResult; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#getInput() + */ + public Object getInput() { + return input; + } + + /** + * Transfers the focus to the first form part. + */ + public void setFocus() { + if (parts.size() > 0) { + IFormPart part = (IFormPart) parts.get(0); + part.setFocus(); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#isDirty() + */ + public boolean isDirty() { + for (int i = 0; i < parts.size(); i++) { + IFormPart part = (IFormPart) parts.get(i); + if (part.isDirty()) + return true; + } + return false; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#isStale() + */ + public boolean isStale() { + for (int i = 0; i < parts.size(); i++) { + IFormPart part = (IFormPart) parts.get(i); + if (part.isStale()) + return true; + } + return false; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#dirtyStateChanged() + */ + public void dirtyStateChanged() { + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#staleStateChanged() + */ + public void staleStateChanged() { + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#getContainer() + */ + public Object getContainer() { + return container; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#setContainer(java.lang.Object) + */ + public void setContainer(Object container) { + this.container = container; + } + + /* (non-Javadoc) + * @see org.eclipse.ui.forms.IManagedForm#getMessageManager() + */ +// public IMessageManager getMessageManager() { +// return form.getMessageManager(); +// } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/editor/FormEditor.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/editor/FormEditor.java new file mode 100644 index 0000000..484dae8 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/editor/FormEditor.java @@ -0,0 +1,85 @@ +package org.argeo.cms.ui.eclipse.forms.editor; + +import org.argeo.cms.ui.eclipse.forms.FormToolkit; +import org.eclipse.jface.dialogs.IPageChangeProvider; + +/** + * This class forms a base of multi-page form editors that typically use one or + * more pages with forms and one page for raw source of the editor input. + *

+ * Pages are added 'lazily' i.e. adding a page reserves a tab for it but does + * not cause the page control to be created. Page control is created when an + * attempt is made to select the page in question. This allows editors with + * several tabs and complex pages to open quickly. + *

+ * Subclasses should extend this class and implement addPages + * method. One of the two addPage methods should be called to + * contribute pages to the editor. One adds complete (standalone) editors as + * nested tabs. These editors will be created right away and will be hooked so + * that key bindings, selection service etc. is compatible with the one for the + * standalone case. The other method adds classes that implement + * IFormPage interface. These pages will be created lazily and + * they will share the common key binding and selection service. Since 3.1, + * FormEditor is a page change provider. It allows listeners to attach to it and + * get notified when pages are changed. This new API in JFace allows dynamic + * help to update on page changes. + * + * @since 1.0 + */ +// RAP [if] As RAP is still using workbench 3.4, the implementation of +// IPageChangeProvider is missing from MultiPageEditorPart. Remove this code +// with the adoption of workbench > 3.5 +//public abstract class FormEditor extends MultiPageEditorPart { +public abstract class FormEditor implements + IPageChangeProvider { + private FormToolkit formToolkit; + + +public FormToolkit getToolkit() { + return formToolkit; + } + +public void editorDirtyStateChanged() { + +} + +public FormPage getActivePageInstance() { + return null; +} + + // RAP [if] As RAP is still using workbench 3.4, the implementation of +// IPageChangeProvider is missing from MultiPageEditorPart. Remove this code +// with the adoption of workbench > 3.5 +// private ListenerList pageListeners = new ListenerList(); +// +// /* +// * (non-Javadoc) +// * +// * @see org.eclipse.jface.dialogs.IPageChangeProvider#addPageChangedListener(org.eclipse.jface.dialogs.IPageChangedListener) +// */ +// public void addPageChangedListener(IPageChangedListener listener) { +// pageListeners.add(listener); +// } +// +// /* +// * (non-Javadoc) +// * +// * @see org.eclipse.jface.dialogs.IPageChangeProvider#removePageChangedListener(org.eclipse.jface.dialogs.IPageChangedListener) +// */ +// public void removePageChangedListener(IPageChangedListener listener) { +// pageListeners.remove(listener); +// } +// +// private void firePageChanged(final PageChangedEvent event) { +// Object[] listeners = pageListeners.getListeners(); +// for (int i = 0; i < listeners.length; ++i) { +// final IPageChangedListener l = (IPageChangedListener) listeners[i]; +// SafeRunnable.run(new SafeRunnable() { +// public void run() { +// l.pageChanged(event); +// } +// }); +// } +// } +// RAPEND [if] +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/editor/FormPage.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/editor/FormPage.java new file mode 100644 index 0000000..a788412 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/editor/FormPage.java @@ -0,0 +1,276 @@ +package org.argeo.cms.ui.eclipse.forms.editor; +import org.argeo.cms.ui.eclipse.forms.IManagedForm; +import org.argeo.cms.ui.eclipse.forms.ManagedForm; +import org.eclipse.swt.custom.BusyIndicator; +import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +/** + * A base class that all pages that should be added to FormEditor must subclass. + * Form page has an instance of PageForm that extends managed form. Subclasses + * should override method 'createFormContent(ManagedForm)' to fill the form with + * content. Note that page itself can be loaded lazily (on first open). + * Consequently, the call to create the form content can come after the editor + * has been opened for a while (in fact, it is possible to open and close the + * editor and never create the form because no attempt has been made to show the + * page). + * + * @since 1.0 + */ +public class FormPage implements IFormPage { + private FormEditor editor; + private PageForm mform; + private int index; + private String id; + + private String partName; + + + + public void setPartName(String partName) { + this.partName = partName; + } + private static class PageForm extends ManagedForm { + public PageForm(FormPage page, ScrolledComposite form) { + super(page.getEditor().getToolkit(), form); + setContainer(page); + } + + public FormPage getPage() { + return (FormPage)getContainer(); + } + public void dirtyStateChanged() { + getPage().getEditor().editorDirtyStateChanged(); + } + public void staleStateChanged() { + if (getPage().isActive()) + refresh(); + } + } + /** + * A constructor that creates the page and initializes it with the editor. + * + * @param editor + * the parent editor + * @param id + * the unique identifier + * @param title + * the page title + */ + public FormPage(FormEditor editor, String id, String title) { + this(id, title); + initialize(editor); + } + /** + * The constructor. The parent editor need to be passed in the + * initialize method if this constructor is used. + * + * @param id + * a unique page identifier + * @param title + * a user-friendly page title + */ + public FormPage(String id, String title) { + this.id = id; + setPartName(title); + } + /** + * Initializes the form page. + * + * @see IEditorPart#init + */ +// public void init(IEditorSite site, IEditorInput input) { +// setSite(site); +// setInput(input); +// } + /** + * Primes the form page with the parent editor instance. + * + * @param editor + * the parent editor + */ + public void initialize(FormEditor editor) { + this.editor = editor; + } + /** + * Returns the parent editor. + * + * @return parent editor instance + */ + public FormEditor getEditor() { + return editor; + } + /** + * Returns the managed form owned by this page. + * + * @return the managed form + */ + public IManagedForm getManagedForm() { + return mform; + } + /** + * Implements the required method by refreshing the form when set active. + * Subclasses must call super when overriding this method. + */ + public void setActive(boolean active) { + if (active) { + // We are switching to this page - refresh it + // if needed. + if (mform != null) + mform.refresh(); + } + } + /** + * Tests if the page is active by asking the parent editor if this page is + * the currently active page. + * + * @return true if the page is currently active, + * false otherwise. + */ + public boolean isActive() { + return this.equals(editor.getActivePageInstance()); + } + /** + * Creates the part control by creating the managed form using the parent + * editor's toolkit. Subclasses should override + * createFormContent(IManagedForm) to populate the form with + * content. + * + * @param parent + * the page parent composite + */ + public void createPartControl(Composite parent) { + ScrolledComposite form = editor.getToolkit().createScrolledForm(parent); + mform = new PageForm(this, form); + BusyIndicator.showWhile(parent.getDisplay(), new Runnable() { + public void run() { + createFormContent(mform); + } + }); + } + /** + * Subclasses should override this method to create content in the form + * hosted in this page. + * + * @param managedForm + * the form hosted in this page. + */ + protected void createFormContent(IManagedForm managedForm) { + } + /** + * Returns the form page control. + * + * @return managed form's control + */ + public Control getPartControl() { + return mform != null ? mform.getForm() : null; + } + /** + * Disposes the managed form. + */ + public void dispose() { + if (mform != null) + mform.dispose(); + } + /** + * Returns the unique identifier that can be used to reference this page. + * + * @return the unique page identifier + */ + public String getId() { + return id; + } + /** + * Returns null- form page has no title image. Subclasses + * may override. + * + * @return null + */ + public Image getTitleImage() { + return null; + } + /** + * Sets the focus by delegating to the managed form. + */ + public void setFocus() { + if (mform != null) + mform.setFocus(); + } + /** + * @see org.eclipse.ui.ISaveablePart#doSave(org.eclipse.core.runtime.IProgressMonitor) + */ +// public void doSave(IProgressMonitor monitor) { +// if (mform != null) +// mform.commit(true); +// } + /** + * @see org.eclipse.ui.ISaveablePart#doSaveAs() + */ + public void doSaveAs() { + } + /** + * @see org.eclipse.ui.ISaveablePart#isSaveAsAllowed() + */ + public boolean isSaveAsAllowed() { + return false; + } + /** + * Implemented by testing if the managed form is dirty. + * + * @return true if the managed form is dirty, + * false otherwise. + * + * @see org.eclipse.ui.ISaveablePart#isDirty() + */ + public boolean isDirty() { + return mform != null ? mform.isDirty() : false; + } + /** + * Preserves the page index. + * + * @param index + * the assigned page index + */ + public void setIndex(int index) { + this.index = index; + } + /** + * Returns the saved page index. + * + * @return the page index + */ + public int getIndex() { + return index; + } + /** + * Form pages are not editors. + * + * @return false + */ + public boolean isEditor() { + return false; + } + /** + * Attempts to select and reveal the given object by passing the request to + * the managed form. + * + * @param object + * the object to select and reveal in the page if possible. + * @return true if the page has been successfully selected + * and revealed by one of the managed form parts, false + * otherwise. + */ + public boolean selectReveal(Object object) { + if (mform != null) + return mform.setInput(object); + return false; + } + /** + * By default, editor will be allowed to flip the page. + * @return true + */ + public boolean canLeaveThePage() { + return true; + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/editor/IFormPage.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/editor/IFormPage.java new file mode 100644 index 0000000..eb08cb5 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/editor/IFormPage.java @@ -0,0 +1,119 @@ +package org.argeo.cms.ui.eclipse.forms.editor; +import org.argeo.cms.ui.eclipse.forms.IManagedForm; +import org.eclipse.swt.widgets.Control; +/** + * Interface that all GUI pages need to implement in order + * to be added to FormEditor part. The interface makes + * several assumptions: + *

    + *
  • The form page has a managed form
  • + *
  • The form page has a unique id
  • + *
  • The form page can be GUI but can also wrap a complete + * editor class (in that case, it should return true + * from isEditor() method).
  • + *
  • The form page is lazy i.e. understands that + * its part control will be created at the last possible + * moment.
  • . + *
+ *

Existing editors can be wrapped by implementing + * this interface. In this case, 'isEditor' should return true. + * A common editor to wrap in TextEditor that is + * often added to show the raw source code of the file open into + * the multi-page editor. + * + * @since 1.0 + */ +public interface IFormPage { + /** + * @param editor + * the form editor that this page belongs to + */ + void initialize(FormEditor editor); + /** + * Returns the editor this page belongs to. + * + * @return the form editor + */ + FormEditor getEditor(); + /** + * Returns the managed form of this page, unless this is a source page. + * + * @return the managed form or null if this is a source page. + */ + IManagedForm getManagedForm(); + /** + * Indicates whether the page has become the active in the editor. Classes + * that implement this interface may use this method to commit the page (on + * false) or lazily create and/or populate the content on + * true. + * + * @param active + * true if page should be visible, false + * otherwise. + */ + void setActive(boolean active); + /** + * Returns true if page is currently active, false if not. + * + * @return true for active page. + */ + boolean isActive(); + /** + * Tests if the content of the page is in a state that allows the + * editor to flip to another page. Typically, pages that contain + * raw source with syntax errors should not allow editors to + * leave them until errors are corrected. + * @return true if the editor can flip to another page, + * false otherwise. + */ + boolean canLeaveThePage(); + /** + * Returns the control associated with this page. + * + * @return the control of this page if created or null if the + * page has not been shown yet. + */ + Control getPartControl(); + /** + * Page must have a unique id that can be used to show it without knowing + * its relative position in the editor. + * + * @return the unique page identifier + */ + String getId(); + /** + * Returns the position of the page in the editor. + * + * @return the zero-based index of the page in the editor. + */ + int getIndex(); + /** + * Sets the position of the page in the editor. + * + * @param index + * the zero-based index of the page in the editor. + */ + void setIndex(int index); + /** + * Tests whether this page wraps a complete editor that + * can be registered on its own, or represents a page + * that cannot exist outside the multi-page editor context. + * + * @return true if the page wraps an editor, + * false if this is a form page. + */ + boolean isEditor(); + /** + * A hint to bring the provided object into focus. If the object is in a + * tree or table control, select it. If it is shown on a scrollable page, + * ensure that it is visible. If the object is not presented in + * the page, false should be returned to allow another + * page to try. + * + * @param object + * object to select and reveal + * @return true if the request was successful, false + * otherwise. + */ + boolean selectReveal(Object object); +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditableLink.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditableLink.java new file mode 100644 index 0000000..3c1e8cd --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditableLink.java @@ -0,0 +1,75 @@ +package org.argeo.cms.ui.forms; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.swt.SwtEditablePart; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +/** Editable String that displays a browsable link when read-only */ +public class EditableLink extends EditablePropertyString implements + SwtEditablePart { + private static final long serialVersionUID = 5055000749992803591L; + + private String type; + private String message; + private boolean readOnly; + + public EditableLink(Composite parent, int style, Node node, + String propertyName, String type, String message) + throws RepositoryException { + super(parent, style, node, propertyName, message); + this.message = message; + this.type = type; + + readOnly = SWT.READ_ONLY == (style & SWT.READ_ONLY); + if (node.hasProperty(propertyName)) { + this.setStyle(FormStyle.propertyText.style()); + this.setText(node.getProperty(propertyName).getString()); + } else { + this.setStyle(FormStyle.propertyMessage.style()); + this.setText(""); + } + } + + public void setText(String text) { + Control child = getControl(); + if (child instanceof Label) { + Label lbl = (Label) child; + if (EclipseUiUtils.isEmpty(text)) + lbl.setText(message); + else if (readOnly) + setLinkValue(lbl, text); + else + // if canEdit() we put only the value with no link + // to avoid glitches of the edition life cycle + lbl.setText(text); + } else if (child instanceof Text) { + Text txt = (Text) child; + if (EclipseUiUtils.isEmpty(text)) { + txt.setText(""); + txt.setMessage(message); + } else + txt.setText(text); + } + } + + private void setLinkValue(Label lbl, String text) { + if (FormStyle.email.style().equals(type)) + lbl.setText(FormUtils.getMailLink(text)); + else if (FormStyle.phone.style().equals(type)) + lbl.setText(FormUtils.getPhoneLink(text)); + else if (FormStyle.website.style().equals(type)) + lbl.setText(FormUtils.getUrlLink(text)); + else if (FormStyle.facebook.style().equals(type) + || FormStyle.instagram.style().equals(type) + || FormStyle.linkedIn.style().equals(type) + || FormStyle.twitter.style().equals(type)) + lbl.setText(FormUtils.getUrlLink(text)); + } +} \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditableMultiStringProperty.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditableMultiStringProperty.java new file mode 100644 index 0000000..ff82700 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditableMultiStringProperty.java @@ -0,0 +1,261 @@ +package org.argeo.cms.ui.forms; + +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.swt.SwtEditablePart; +import org.argeo.cms.ui.widgets.StyledControl; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.events.TraverseEvent; +import org.eclipse.swt.events.TraverseListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.layout.RowLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +/** Display, add or remove values from a list in a CMS context */ +public class EditableMultiStringProperty extends StyledControl implements SwtEditablePart { + private static final long serialVersionUID = -7044614381252178595L; + + private String propertyName; + private String message; + // TODO implement the ability to provide a list of possible values +// private String[] possibleValues; + private boolean canEdit; + private SelectionListener removeValueSL; + private List values; + + // TODO manage within the CSS + private int rowSpacing = 5; + private int rowMarging = 0; + private int oneValueMargingRight = 5; + private int btnWidth = 16; + private int btnHeight = 16; + private int btnHorizontalIndent = 3; + + public EditableMultiStringProperty(Composite parent, int style, Node node, String propertyName, List values, + String[] possibleValues, String addValueMsg, SelectionListener removeValueSelectionListener) + throws RepositoryException { + super(parent, style, node, true); + + this.propertyName = propertyName; + this.values = values; +// this.possibleValues = new String[] { "Un", "Deux", "Trois" }; + this.message = addValueMsg; + this.canEdit = removeValueSelectionListener != null; + this.removeValueSL = removeValueSelectionListener; + } + + public List getValues() { + return values; + } + + public void setValues(List values) { + this.values = values; + } + + // Row layout items do not need explicit layout data + protected void setControlLayoutData(Control control) { + } + + /** To be overridden */ + protected void setContainerLayoutData(Composite composite) { + composite.setLayoutData(CmsSwtUtils.fillWidth()); + } + + @Override + public Control getControl() { + return super.getControl(); + } + + @Override + protected Control createControl(Composite box, String style) { + Composite row = new Composite(box, SWT.NO_FOCUS); + row.setLayoutData(EclipseUiUtils.fillAll()); + + RowLayout rl = new RowLayout(SWT.HORIZONTAL); + rl.wrap = true; + rl.spacing = rowSpacing; + rl.marginRight = rl.marginLeft = rl.marginBottom = rl.marginTop = rowMarging; + row.setLayout(rl); + + if (values != null) { + for (final String value : values) { + if (canEdit) + createRemovableValue(row, SWT.SINGLE, value); + else + createValueLabel(row, SWT.SINGLE, value); + } + } + + if (!canEdit) + return row; + else if (isEditing()) + return createText(row, style); + else + return createLabel(row, style); + } + + /** + * Override to provide specific layout for the existing values, typically adding + * a pound (#) char for tags or anchor info for browsable links. We assume the + * parent composite already has a layout and it is the caller responsibility to + * apply corresponding layout data + */ + protected Label createValueLabel(Composite parent, int style, String value) { + Label label = new Label(parent, style); + label.setText("#" + value); + CmsSwtUtils.markup(label); + CmsSwtUtils.style(label, FormStyle.propertyText.style()); + return label; + } + + private Composite createRemovableValue(Composite parent, int style, String value) { + Composite valCmp = new Composite(parent, SWT.NO_FOCUS); + GridLayout gl = EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, false)); + gl.marginRight = oneValueMargingRight; + valCmp.setLayout(gl); + + createValueLabel(valCmp, SWT.WRAP, value); + + Button deleteBtn = new Button(valCmp, SWT.FLAT); + deleteBtn.setData(FormConstants.LINKED_VALUE, value); + deleteBtn.addSelectionListener(removeValueSL); + CmsSwtUtils.style(deleteBtn, FormStyle.delete.style() + FormStyle.BUTTON_SUFFIX); + GridData gd = new GridData(); + gd.heightHint = btnHeight; + gd.widthHint = btnWidth; + gd.horizontalIndent = btnHorizontalIndent; + deleteBtn.setLayoutData(gd); + + return valCmp; + } + + protected Text createText(Composite box, String style) { + final Text text = new Text(box, getStyle()); + // The "add new value" text is not meant to change, so we can set it on + // creation + text.setMessage(message); + CmsSwtUtils.style(text, style); + text.setFocus(); + + text.addTraverseListener(new TraverseListener() { + private static final long serialVersionUID = 1L; + + public void keyTraversed(TraverseEvent e) { + if (e.keyCode == SWT.CR) { + addValue(text); + e.doit = false; + } + } + }); + + // The OK button does not work with the focusOut listener + // because focus out is called before the OK button is pressed + + // // we must call layout() now so that the row data can compute the + // height + // // of the other controls. + // text.getParent().layout(); + // int height = text.getSize().y; + // + // Button okBtn = new Button(box, SWT.BORDER | SWT.PUSH | SWT.BOTTOM); + // okBtn.setText("OK"); + // RowData rd = new RowData(SWT.DEFAULT, height - 2); + // okBtn.setLayoutData(rd); + // + // okBtn.addSelectionListener(new SelectionAdapter() { + // private static final long serialVersionUID = 2780819012423622369L; + // + // @Override + // public void widgetSelected(SelectionEvent e) { + // addValue(text); + // } + // }); + + return text; + } + + /** Performs the real addition, overwrite to make further sanity checks */ + protected void addValue(Text text) { + String value = text.getText(); + String errMsg = null; + + if (EclipseUiUtils.isEmpty(value)) + return; + + if (values.contains(value)) + errMsg = "Dupplicated value: " + value + ", please correct and try again"; + if (errMsg != null) + MessageDialog.openError(this.getShell(), "Addition not allowed", errMsg); + else { + values.add(value); + Composite newCmp = createRemovableValue(text.getParent(), SWT.SINGLE, value); + newCmp.moveAbove(text); + text.setText(""); + newCmp.getParent().layout(); + } + } + + protected Label createLabel(Composite box, String style) { + if (canEdit) { + Label lbl = new Label(box, getStyle()); + lbl.setText(message); + CmsSwtUtils.style(lbl, style); + CmsSwtUtils.markup(lbl); + if (mouseListener != null) + lbl.addMouseListener(mouseListener); + return lbl; + } + return null; + } + + protected void clear(boolean deep) { + Control child = getControl(); + if (deep) + super.clear(deep); + else { + child.getParent().dispose(); + } + } + + public void setText(String text) { + Control child = getControl(); + if (child instanceof Label) { + Label lbl = (Label) child; + if (canEdit) + lbl.setText(text); + else + lbl.setText(""); + } else if (child instanceof Text) { + Text txt = (Text) child; + txt.setText(text); + } + } + + public synchronized void startEditing() { + CmsSwtUtils.style(getControl(), FormStyle.propertyText.style()); +// getControl().setData(STYLE, FormStyle.propertyText.style()); + super.startEditing(); + } + + public synchronized void stopEditing() { + CmsSwtUtils.style(getControl(), FormStyle.propertyMessage.style()); +// getControl().setData(STYLE, FormStyle.propertyMessage.style()); + super.stopEditing(); + } + + public String getPropertyName() { + return propertyName; + } +} \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditablePropertyDate.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditablePropertyDate.java new file mode 100644 index 0000000..641f916 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditablePropertyDate.java @@ -0,0 +1,298 @@ +package org.argeo.cms.ui.forms; + +import java.text.DateFormat; +import java.util.Calendar; +import java.util.GregorianCalendar; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.swt.SwtEditablePart; +import org.argeo.cms.ui.widgets.StyledControl; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.ShellAdapter; +import org.eclipse.swt.events.ShellEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.DateTime; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +/** CMS form part to display and edit a date */ +public class EditablePropertyDate extends StyledControl implements SwtEditablePart { + private static final long serialVersionUID = 2500215515778162468L; + + // Context + private String propertyName; + private String message; + private DateFormat dateFormat; + + // UI Objects + private Text dateTxt; + private Button openCalBtn; + + // TODO manage within the CSS + private int fieldBtnSpacing = 5; + + /** + * + * @param parent + * @param style + * @param node + * @param propertyName + * @param message + * @param dateFormat provide a {@link DateFormat} as contract to be able to + * read/write dates as strings + * @throws RepositoryException + */ + public EditablePropertyDate(Composite parent, int style, Node node, String propertyName, String message, + DateFormat dateFormat) throws RepositoryException { + super(parent, style, node, false); + + this.propertyName = propertyName; + this.message = message; + this.dateFormat = dateFormat; + + if (node.hasProperty(propertyName)) { + this.setStyle(FormStyle.propertyText.style()); + this.setText(dateFormat.format(node.getProperty(propertyName).getDate().getTime())); + } else { + this.setStyle(FormStyle.propertyMessage.style()); + this.setText(message); + } + } + + public void setText(String text) { + Control child = getControl(); + if (child instanceof Label) { + Label lbl = (Label) child; + if (EclipseUiUtils.isEmpty(text)) + lbl.setText(message); + else + lbl.setText(text); + } else if (child instanceof Text) { + Text txt = (Text) child; + if (EclipseUiUtils.isEmpty(text)) { + txt.setText(""); + } else + txt.setText(text); + } + } + + public synchronized void startEditing() { + // if (dateTxt != null && !dateTxt.isDisposed()) + CmsSwtUtils.style(getControl(), FormStyle.propertyText); +// getControl().setData(STYLE, FormStyle.propertyText.style()); + super.startEditing(); + } + + public synchronized void stopEditing() { + if (EclipseUiUtils.isEmpty(dateTxt.getText())) + CmsSwtUtils.style(getControl(), FormStyle.propertyMessage); +// getControl().setData(STYLE, FormStyle.propertyMessage.style()); + else + CmsSwtUtils.style(getControl(), FormStyle.propertyText); +// getControl().setData(STYLE, FormStyle.propertyText.style()); + super.stopEditing(); + } + + public String getPropertyName() { + return propertyName; + } + + @Override + protected Control createControl(Composite box, String style) { + if (isEditing()) { + return createCustomEditableControl(box, style); + } else + return createLabel(box, style); + } + + protected Label createLabel(Composite box, String style) { + Label lbl = new Label(box, getStyle() | SWT.WRAP); + lbl.setLayoutData(CmsSwtUtils.fillWidth()); + CmsSwtUtils.style(lbl, style); + CmsSwtUtils.markup(lbl); + if (mouseListener != null) + lbl.addMouseListener(mouseListener); + return lbl; + } + + private Control createCustomEditableControl(Composite box, String style) { + box.setLayoutData(CmsSwtUtils.fillWidth()); + Composite dateComposite = new Composite(box, SWT.NONE); + GridLayout gl = EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, false)); + gl.horizontalSpacing = fieldBtnSpacing; + dateComposite.setLayout(gl); + dateTxt = new Text(dateComposite, SWT.BORDER); + CmsSwtUtils.style(dateTxt, style); + dateTxt.setLayoutData(new GridData(120, SWT.DEFAULT)); + dateTxt.setToolTipText( + "Enter a date with form \"" + FormUtils.DEFAULT_SHORT_DATE_FORMAT + "\" or use the calendar"); + openCalBtn = new Button(dateComposite, SWT.FLAT); + CmsSwtUtils.style(openCalBtn, FormStyle.calendar.style() + FormStyle.BUTTON_SUFFIX); + GridData gd = new GridData(SWT.CENTER, SWT.CENTER, false, false); + gd.heightHint = 17; + openCalBtn.setLayoutData(gd); + // openCalBtn.setImage(PeopleRapImages.CALENDAR_BTN); + + openCalBtn.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = 1L; + + public void widgetSelected(SelectionEvent event) { + CalendarPopup popup = new CalendarPopup(dateTxt); + popup.open(); + } + }); + + // dateTxt.addFocusListener(new FocusListener() { + // private static final long serialVersionUID = 1L; + // + // @Override + // public void focusLost(FocusEvent event) { + // String newVal = dateTxt.getText(); + // // Enable reset of the field + // if (FormUtils.notNull(newVal)) + // calendar = null; + // else { + // try { + // Calendar newCal = parseDate(newVal); + // // DateText.this.setText(newCal); + // calendar = newCal; + // } catch (ParseException pe) { + // // Silent. Manage error popup? + // if (calendar != null) + // EditablePropertyDate.this.setText(calendar); + // } + // } + // } + // + // @Override + // public void focusGained(FocusEvent event) { + // } + // }); + return dateTxt; + } + + protected void clear(boolean deep) { + Control child = getControl(); + if (deep || child instanceof Label) + super.clear(deep); + else { + child.getParent().dispose(); + } + } + + /** Enable setting a custom tooltip on the underlying text */ + @Deprecated + public void setToolTipText(String toolTipText) { + dateTxt.setToolTipText(toolTipText); + } + + @Deprecated + /** Enable setting a custom message on the underlying text */ + public void setMessage(String message) { + dateTxt.setMessage(message); + } + + @Deprecated + public void setText(Calendar cal) { + String newValueStr = ""; + if (cal != null) + newValueStr = dateFormat.format(cal.getTime()); + if (!newValueStr.equals(dateTxt.getText())) + dateTxt.setText(newValueStr); + } + + // UTILITIES TO MANAGE THE CALENDAR POPUP + // TODO manage the popup shell in a cleaner way + private class CalendarPopup extends Shell { + private static final long serialVersionUID = 1L; + private DateTime dateTimeCtl; + + public CalendarPopup(Control source) { + super(source.getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP); + populate(); + // Add border and shadow style + CmsSwtUtils.markup(CalendarPopup.this); + CmsSwtUtils.style(CalendarPopup.this, FormStyle.popupCalendar.style()); + pack(); + layout(); + setLocation(source.toDisplay((source.getLocation().x - 2), (source.getSize().y) + 3)); + + addShellListener(new ShellAdapter() { + private static final long serialVersionUID = 5178980294808435833L; + + @Override + public void shellDeactivated(ShellEvent e) { + close(); + dispose(); + } + }); + open(); + } + + private void setProperty() { + // Direct set does not seems to work. investigate + // cal.set(dateTimeCtl.getYear(), dateTimeCtl.getMonth(), + // dateTimeCtl.getDay(), 12, 0); + Calendar cal = new GregorianCalendar(); + cal.set(Calendar.YEAR, dateTimeCtl.getYear()); + cal.set(Calendar.MONTH, dateTimeCtl.getMonth()); + cal.set(Calendar.DAY_OF_MONTH, dateTimeCtl.getDay()); + String dateStr = dateFormat.format(cal.getTime()); + dateTxt.setText(dateStr); + } + + protected void populate() { + setLayout(EclipseUiUtils.noSpaceGridLayout()); + + dateTimeCtl = new DateTime(this, SWT.CALENDAR); + dateTimeCtl.setLayoutData(EclipseUiUtils.fillAll()); + + Calendar calendar = FormUtils.parseDate(dateFormat, dateTxt.getText()); + + if (calendar != null) + dateTimeCtl.setDate(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), + calendar.get(Calendar.DAY_OF_MONTH)); + + dateTimeCtl.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = -8414377364434281112L; + + @Override + public void widgetSelected(SelectionEvent e) { + setProperty(); + } + }); + + dateTimeCtl.addMouseListener(new MouseListener() { + private static final long serialVersionUID = 1L; + + @Override + public void mouseUp(MouseEvent e) { + } + + @Override + public void mouseDown(MouseEvent e) { + } + + @Override + public void mouseDoubleClick(MouseEvent e) { + setProperty(); + close(); + dispose(); + } + }); + } + } +} \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditablePropertyString.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditablePropertyString.java new file mode 100644 index 0000000..f2575e1 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditablePropertyString.java @@ -0,0 +1,80 @@ +package org.argeo.cms.ui.forms; + +import static org.argeo.cms.ui.forms.FormStyle.propertyMessage; +import static org.argeo.cms.ui.forms.FormStyle.propertyText; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.swt.SwtEditablePart; +import org.argeo.cms.ui.widgets.EditableText; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +/** Editable String in a CMS context */ +public class EditablePropertyString extends EditableText implements SwtEditablePart { + private static final long serialVersionUID = 5055000749992803591L; + + private String propertyName; + private String message; + + // encode the '&' character in rap + private final static String AMPERSAND = "&"; + private final static String AMPERSAND_REGEX = "&(?![#a-zA-Z0-9]+;)"; + + public EditablePropertyString(Composite parent, int style, Node node, String propertyName, String message) + throws RepositoryException { + super(parent, style, node, true); + //setUseTextAsLabel(true); + this.propertyName = propertyName; + this.message = message; + + if (node.hasProperty(propertyName)) { + this.setStyle(propertyText.style()); + this.setText(node.getProperty(propertyName).getString()); + } else { + this.setStyle(propertyMessage.style()); + this.setText(message + " "); + } + } + + public void setText(String text) { + Control child = getControl(); + if (child instanceof Label) { + Label lbl = (Label) child; + if (EclipseUiUtils.isEmpty(text)) + lbl.setText(message + " "); + else + // TODO enhance this + lbl.setText(text.replaceAll(AMPERSAND_REGEX, AMPERSAND)); + } else if (child instanceof Text) { + Text txt = (Text) child; + if (EclipseUiUtils.isEmpty(text)) { + txt.setText(""); + txt.setMessage(message + " "); + } else + txt.setText(text.replaceAll("
", "\n")); + } + } + + public synchronized void startEditing() { + CmsSwtUtils.style(getControl(), FormStyle.propertyText); + super.startEditing(); + } + + public synchronized void stopEditing() { + if (EclipseUiUtils.isEmpty(((Text) getControl()).getText())) + CmsSwtUtils.style(getControl(), FormStyle.propertyMessage); + else + CmsSwtUtils.style(getControl(), FormStyle.propertyText); + super.stopEditing(); + } + + public String getPropertyName() { + return propertyName; + } +} \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormConstants.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormConstants.java new file mode 100644 index 0000000..fe9f7e7 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormConstants.java @@ -0,0 +1,7 @@ +package org.argeo.cms.ui.forms; + +/** Constants used in the various CMS Forms */ +public interface FormConstants { + // DATAKEYS + public final static String LINKED_VALUE = "LinkedValue"; +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormEditorHeader.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormEditorHeader.java new file mode 100644 index 0000000..a75c191 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormEditorHeader.java @@ -0,0 +1,114 @@ +package org.argeo.cms.ui.forms; + +import java.util.Observable; +import java.util.Observer; + +import javax.jcr.Node; + +import org.argeo.api.cms.ux.CmsEditable; +import org.argeo.cms.swt.CmsSwtUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; + +/** Add life cycle management abilities to an editable form page */ +public class FormEditorHeader implements SelectionListener, Observer { + private static final long serialVersionUID = 7392898696542484282L; + + // private final Node context; + private final CmsEditable cmsEditable; + private Button publishBtn; + + // Should we provide here the ability to switch from read only to edition + // mode? + // private Button editBtn; + // private boolean readOnly; + + // TODO add information about the current node status, typically if it is + // dirty or not + + private Composite parent; + private Composite display; + private Object layoutData; + + public FormEditorHeader(Composite parent, int style, Node context, + CmsEditable cmsEditable) { + this.cmsEditable = cmsEditable; + this.parent = parent; + // readOnly = SWT.READ_ONLY == (style & SWT.READ_ONLY); + // this.context = context; + if (this.cmsEditable instanceof Observable) + ((Observable) this.cmsEditable).addObserver(this); + refresh(); + } + + public void setLayoutData(Object layoutData) { + this.layoutData = layoutData; + if (display != null && !display.isDisposed()) + display.setLayoutData(layoutData); + } + + protected void refresh() { + if (display != null && !display.isDisposed()) + display.dispose(); + + display = new Composite(parent, SWT.NONE); + display.setLayoutData(layoutData); + + CmsSwtUtils.style(display, FormStyle.header.style()); + display.setBackgroundMode(SWT.INHERIT_FORCE); + + display.setLayout(CmsSwtUtils.noSpaceGridLayout()); + + publishBtn = createSimpleBtn(display, getPublishButtonLabel()); + display.moveAbove(null); + parent.layout(); + } + + private Button createSimpleBtn(Composite parent, String label) { + Button button = new Button(parent, SWT.FLAT | SWT.PUSH); + button.setText(label); + CmsSwtUtils.style(button, FormStyle.header.style()); + button.addSelectionListener(this); + return button; + } + + private String getPublishButtonLabel() { + // Rather check if the current node differs from what has been + // previously committed + // For the time being, we always reach here, the underlying CmsEditable + // is always editing. + if (cmsEditable.isEditing()) + return " Publish "; + else + return " Edit "; + } + + @Override + public void widgetSelected(SelectionEvent e) { + if (e.getSource() == publishBtn) { + // For the time being, the underlying CmsEditable + // is always editing when we reach this point + if (cmsEditable.isEditing()) { + // we always leave the node in a check outed state + cmsEditable.stopEditing(); + cmsEditable.startEditing(); + } else { + cmsEditable.startEditing(); + } + } + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + } + + @Override + public void update(Observable o, Object arg) { + if (o == cmsEditable) { + refresh(); + } + } +} \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormPageViewer.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormPageViewer.java new file mode 100644 index 0000000..1888055 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormPageViewer.java @@ -0,0 +1,608 @@ +package org.argeo.cms.ui.forms; + +import java.io.IOException; +import java.io.InputStream; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.Value; +import javax.jcr.ValueFormatException; + +import org.argeo.api.cms.CmsLog; +import org.argeo.api.cms.ux.Cms2DSize; +import org.argeo.api.cms.ux.CmsEditable; +import org.argeo.api.cms.ux.CmsImageManager; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.swt.SwtEditablePart; +import org.argeo.cms.ui.viewers.AbstractPageViewer; +import org.argeo.cms.ui.viewers.Section; +import org.argeo.cms.ui.viewers.SectionPart; +import org.argeo.cms.ui.widgets.EditableImage; +import org.argeo.cms.ui.widgets.Img; +import org.argeo.cms.ui.widgets.StyledControl; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.jcr.JcrException; +import org.argeo.jcr.JcrUtils; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.rap.fileupload.FileDetails; +import org.eclipse.rap.fileupload.FileUploadEvent; +import org.eclipse.rap.fileupload.FileUploadHandler; +import org.eclipse.rap.fileupload.FileUploadListener; +import org.eclipse.rap.fileupload.FileUploadReceiver; +import org.eclipse.rap.rwt.service.ServerPushSession; +import org.eclipse.rap.rwt.widgets.FileUpload; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.MouseAdapter; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.FormAttachment; +import org.eclipse.swt.layout.FormData; +import org.eclipse.swt.layout.FormLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.layout.RowLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +/** Manage life cycle of a form page that is linked to a given node */ +public class FormPageViewer extends AbstractPageViewer { + private final static CmsLog log = CmsLog.getLog(FormPageViewer.class); + private static final long serialVersionUID = 5277789504209413500L; + + private final Section mainSection; + + // TODO manage within the CSS + private Integer labelColWidth = null; + private int rowLayoutHSpacing = 8; + + // Context cached in the viewer + // The reference to translate from text to calendar and reverse + private DateFormat dateFormat = new SimpleDateFormat(FormUtils.DEFAULT_SHORT_DATE_FORMAT); + private CmsImageManager imageManager; + private FileUploadListener fileUploadListener; + + public FormPageViewer(Section mainSection, int style, CmsEditable cmsEditable) throws RepositoryException { + super(mainSection, style, cmsEditable); + this.mainSection = mainSection; + + if (getCmsEditable().canEdit()) { + fileUploadListener = new FUL(); + } + } + + @Override + protected void prepare(SwtEditablePart part, Object caretPosition) { + if (part instanceof Img) { + ((Img) part).setFileUploadListener(fileUploadListener); + } + } + + /** To be overridden.Save the edited part. */ + protected void save(SwtEditablePart part) throws RepositoryException { + Node node = null; + if (part instanceof EditableMultiStringProperty) { + EditableMultiStringProperty ept = (EditableMultiStringProperty) part; + // SWT : View + List values = ept.getValues(); + // JCR : Model + node = ept.getNode(); + String propName = ept.getPropertyName(); + if (values.isEmpty()) { + if (node.hasProperty(propName)) + node.getProperty(propName).remove(); + } else { + node.setProperty(propName, values.toArray(new String[0])); + } + // => Viewer : Controller + } else if (part instanceof EditablePropertyString) { + EditablePropertyString ept = (EditablePropertyString) part; + // SWT : View + String txt = ((Text) ept.getControl()).getText(); + // JCR : Model + node = ept.getNode(); + String propName = ept.getPropertyName(); + if (EclipseUiUtils.isEmpty(txt)) { + if (node.hasProperty(propName)) + node.getProperty(propName).remove(); + } else { + setPropertySilently(node, propName, txt); + // node.setProperty(propName, txt); + } + // node.getSession().save(); + // => Viewer : Controller + } else if (part instanceof EditablePropertyDate) { + EditablePropertyDate ept = (EditablePropertyDate) part; + Calendar cal = FormUtils.parseDate(dateFormat, ((Text) ept.getControl()).getText()); + node = ept.getNode(); + String propName = ept.getPropertyName(); + if (cal == null) { + if (node.hasProperty(propName)) + node.getProperty(propName).remove(); + } else { + node.setProperty(propName, cal); + } + // node.getSession().save(); + // => Viewer : Controller + } + // TODO: make this configurable, sometimes we do not want to save the + // current session at this stage + if (node != null && node.getSession().hasPendingChanges()) { + JcrUtils.updateLastModified(node, true); + node.getSession().save(); + } + } + + @Override + protected void updateContent(SwtEditablePart part) throws RepositoryException { + if (part instanceof EditableMultiStringProperty) { + EditableMultiStringProperty ept = (EditableMultiStringProperty) part; + // SWT : View + Node node = ept.getNode(); + String propName = ept.getPropertyName(); + List valStrings = new ArrayList(); + if (node.hasProperty(propName)) { + Value[] values = node.getProperty(propName).getValues(); + for (Value val : values) + valStrings.add(val.getString()); + } + ept.setValues(valStrings); + } else if (part instanceof EditablePropertyString) { + // || part instanceof EditableLink + EditablePropertyString ept = (EditablePropertyString) part; + // JCR : Model + Node node = ept.getNode(); + String propName = ept.getPropertyName(); + if (node.hasProperty(propName)) { + String value = node.getProperty(propName).getString(); + ept.setText(value); + } else + ept.setText(""); + // => Viewer : Controller + } else if (part instanceof EditablePropertyDate) { + EditablePropertyDate ept = (EditablePropertyDate) part; + // JCR : Model + Node node = ept.getNode(); + String propName = ept.getPropertyName(); + if (node.hasProperty(propName)) + ept.setText(dateFormat.format(node.getProperty(propName).getDate().getTime())); + else + ept.setText(""); + } else if (part instanceof SectionPart) { + SectionPart sectionPart = (SectionPart) part; + Node partNode = sectionPart.getNode(); + // use control AFTER setting style, since it may have been reset + if (part instanceof EditableImage) { + EditableImage editableImage = (EditableImage) part; + imageManager().load(partNode, part.getControl(), editableImage.getPreferredImageSize()); + } + } + } + + // FILE UPLOAD LISTENER + protected class FUL implements FileUploadListener { + + public FUL() { + } + + public void uploadProgress(FileUploadEvent event) { + // TODO Monitor upload progress + } + + public void uploadFailed(FileUploadEvent event) { + throw new IllegalStateException("Upload failed " + event, event.getException()); + } + + public void uploadFinished(FileUploadEvent event) { + for (FileDetails file : event.getFileDetails()) { + if (log.isDebugEnabled()) + log.debug("Received: " + file.getFileName()); + } + mainSection.getDisplay().syncExec(new Runnable() { + @Override + public void run() { + saveEdit(); + } + }); + FileUploadHandler uploadHandler = (FileUploadHandler) event.getSource(); + uploadHandler.dispose(); + } + } + + // FOCUS OUT LISTENER + protected FocusListener createFocusListener() { + return new FocusOutListener(); + } + + private class FocusOutListener implements FocusListener { + private static final long serialVersionUID = -6069205786732354186L; + + @Override + public void focusLost(FocusEvent event) { + saveEdit(); + } + + @Override + public void focusGained(FocusEvent event) { + // does nothing; + } + } + + // MOUSE LISTENER + @Override + protected MouseListener createMouseListener() { + return new ML(); + } + + private class ML extends MouseAdapter { + private static final long serialVersionUID = 8526890859876770905L; + + @Override + public void mouseDoubleClick(MouseEvent e) { + if (e.button == 1) { + Control source = (Control) e.getSource(); + if (getCmsEditable().canEdit()) { + if (getCmsEditable().isEditing() && !(getEdited() instanceof Img)) { + if (source == mainSection) + return; + SwtEditablePart part = findDataParent(source); + upload(part); + } else { + getCmsEditable().startEditing(); + } + } + } + } + + @Override + public void mouseDown(MouseEvent e) { + if (getCmsEditable().isEditing()) { + if (e.button == 1) { + Control source = (Control) e.getSource(); + SwtEditablePart composite = findDataParent(source); + Point point = new Point(e.x, e.y); + if (!(composite instanceof Img)) + edit(composite, source.toDisplay(point)); + } else if (e.button == 3) { + // EditablePart composite = findDataParent((Control) e + // .getSource()); + // if (styledTools != null) + // styledTools.show(composite, new Point(e.x, e.y)); + } + } + } + + protected synchronized void upload(SwtEditablePart part) { + if (part instanceof SectionPart) { + if (part instanceof Img) { + if (getEdited() == part) + return; + edit(part, null); + layout(part.getControl()); + } + } + } + } + + @Override + public Control getControl() { + return mainSection; + } + + protected CmsImageManager imageManager() { + if (imageManager == null) + imageManager = (CmsImageManager) CmsSwtUtils.getCmsView(mainSection).getImageManager(); + return imageManager; + } + + // LOCAL UI HELPERS + protected Section createSectionIfNeeded(Composite body, Node node) throws RepositoryException { + Section section = null; + if (node != null) { + section = new Section(body, SWT.NO_FOCUS, node); + section.setLayoutData(CmsSwtUtils.fillWidth()); + section.setLayout(CmsSwtUtils.noSpaceGridLayout()); + } + return section; + } + + protected void createSimpleLT(Composite bodyRow, Node node, String propName, String label, String msg) + throws RepositoryException { + if (getCmsEditable().canEdit() || node.hasProperty(propName)) { + createPropertyLbl(bodyRow, label); + EditablePropertyString eps = new EditablePropertyString(bodyRow, SWT.WRAP | SWT.LEFT, node, propName, msg); + eps.setMouseListener(getMouseListener()); + eps.setFocusListener(getFocusListener()); + eps.setLayoutData(CmsSwtUtils.fillWidth()); + } + } + + protected void createMultiStringLT(Composite bodyRow, Node node, String propName, String label, String msg) + throws RepositoryException { + boolean canEdit = getCmsEditable().canEdit(); + if (canEdit || node.hasProperty(propName)) { + createPropertyLbl(bodyRow, label); + + List valueStrings = new ArrayList(); + + if (node.hasProperty(propName)) { + Value[] values = node.getProperty(propName).getValues(); + for (Value value : values) + valueStrings.add(value.getString()); + } + + // TODO use a drop down to display possible values to the end user + EditableMultiStringProperty emsp = new EditableMultiStringProperty(bodyRow, SWT.SINGLE | SWT.LEAD, node, + propName, valueStrings, new String[] { "Implement this" }, msg, + canEdit ? getRemoveValueSelListener() : null); + addListeners(emsp); + // emsp.setMouseListener(getMouseListener()); + emsp.setStyle(FormStyle.propertyMessage.style()); + emsp.setLayoutData(CmsSwtUtils.fillWidth()); + } + } + + protected Label createPropertyLbl(Composite parent, String value) { + return createPropertyLbl(parent, value, SWT.NONE); + } + + protected Label createPropertyLbl(Composite parent, String value, int vAlign) { + // boolean isSmall = CmsView.getCmsView(parent).getUxContext().isSmall(); + Label label = new Label(parent, SWT.LEAD | SWT.WRAP); + label.setText(value + " "); + CmsSwtUtils.style(label, FormStyle.propertyLabel.style()); + GridData gd = new GridData(SWT.LEAD, vAlign, false, false); + if (labelColWidth != null) + gd.widthHint = labelColWidth; + label.setLayoutData(gd); + return label; + } + + protected Label newStyledLabel(Composite parent, String style, String value) { + Label label = new Label(parent, SWT.NONE); + label.setText(value); + CmsSwtUtils.style(label, style); + return label; + } + + protected Composite createRowLayoutComposite(Composite parent) throws RepositoryException { + Composite bodyRow = new Composite(parent, SWT.NO_FOCUS); + bodyRow.setLayoutData(CmsSwtUtils.fillWidth()); + RowLayout rl = new RowLayout(SWT.WRAP); + rl.type = SWT.HORIZONTAL; + rl.spacing = rowLayoutHSpacing; + rl.marginHeight = rl.marginWidth = 0; + rl.marginTop = rl.marginBottom = rl.marginLeft = rl.marginRight = 0; + bodyRow.setLayout(rl); + return bodyRow; + } + + protected Composite createAddImgComposite(final Section section, Composite parent, final Node parentNode) + throws RepositoryException { + + Composite body = new Composite(parent, SWT.NO_FOCUS); + body.setLayout(new GridLayout()); + + FormFileUploadReceiver receiver = new FormFileUploadReceiver(section, parentNode, null); + final FileUploadHandler currentUploadHandler = new FileUploadHandler(receiver); + if (fileUploadListener != null) + currentUploadHandler.addUploadListener(fileUploadListener); + + // Button creation + final FileUpload fileUpload = new FileUpload(body, SWT.BORDER); + fileUpload.setText("Import an image"); + fileUpload.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true)); + fileUpload.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = 4869523412991968759L; + + @Override + public void widgetSelected(SelectionEvent e) { + ServerPushSession pushSession = new ServerPushSession(); + pushSession.start(); + String uploadURL = currentUploadHandler.getUploadUrl(); + fileUpload.submit(uploadURL); + } + }); + + return body; + } + + protected class FormFileUploadReceiver extends FileUploadReceiver { + + private Node context; + private Section section; + private String name; + + public FormFileUploadReceiver(Section section, Node context, String name) { + this.context = context; + this.section = section; + this.name = name; + } + + @Override + public void receive(InputStream stream, FileDetails details) throws IOException { + + if (name == null) + name = details.getFileName(); + + // TODO clean image name more carefully + String cleanedName = name.replaceAll("[^a-zA-Z0-9-.]", ""); + // We add a unique prefix to workaround the cache issue: when + // deleting and re-adding a new image with same name, the end user + // browser will use the cache and the image will remain unchanged + // for a while + cleanedName = System.currentTimeMillis() % 100000 + "_" + cleanedName; + + imageManager().uploadImage(context, context, cleanedName, stream, details.getContentType()); + // TODO clean refresh strategy + section.getDisplay().asyncExec(new Runnable() { + @Override + public void run() { + try { + FormPageViewer.this.refresh(section); + section.layout(); + section.getParent().layout(); + } catch (RepositoryException re) { + throw new JcrException("Unable to refresh " + "image section for " + context, re); + } + } + }); + } + } + + protected void addListeners(StyledControl control) { + control.setMouseListener(getMouseListener()); + control.setFocusListener(getFocusListener()); + } + + protected Img createImgComposite(Composite parent, Node node, Point preferredSize) throws RepositoryException { + Img img = new Img(parent, SWT.NONE, node, new Cms2DSize(preferredSize.x, preferredSize.y)) { + private static final long serialVersionUID = 1297900641952417540L; + + @Override + protected void setContainerLayoutData(Composite composite) { + composite.setLayoutData(CmsSwtUtils.grabWidth(SWT.CENTER, SWT.DEFAULT)); + } + + @Override + protected void setControlLayoutData(Control control) { + control.setLayoutData(CmsSwtUtils.grabWidth(SWT.CENTER, SWT.DEFAULT)); + } + }; + img.setLayoutData(CmsSwtUtils.grabWidth(SWT.CENTER, SWT.DEFAULT)); + updateContent(img); + addListeners(img); + return img; + } + + protected Composite addDeleteAbility(final Section section, final Node sessionNode, int topWeight, + int rightWeight) { + Composite comp = new Composite(section, SWT.NONE); + comp.setLayoutData(CmsSwtUtils.fillAll()); + comp.setLayout(new FormLayout()); + + // The body to be populated + Composite body = new Composite(comp, SWT.NO_FOCUS); + body.setLayoutData(EclipseUiUtils.fillFormData()); + + if (getCmsEditable().canEdit()) { + // the delete button + Button deleteBtn = new Button(comp, SWT.FLAT); + CmsSwtUtils.style(deleteBtn, FormStyle.deleteOverlay.style()); + FormData formData = new FormData(); + formData.right = new FormAttachment(rightWeight, 0); + formData.top = new FormAttachment(topWeight, 0); + deleteBtn.setLayoutData(formData); + deleteBtn.moveAbove(body); + + deleteBtn.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = 4304223543657238462L; + + @Override + public void widgetSelected(SelectionEvent e) { + super.widgetSelected(e); + if (MessageDialog.openConfirm(section.getShell(), "Confirm deletion", + "Are you really you want to remove this?")) { + Session session; + try { + session = sessionNode.getSession(); + Section parSection = section.getParentSection(); + sessionNode.remove(); + session.save(); + refresh(parSection); + layout(parSection); + } catch (RepositoryException re) { + throw new JcrException("Unable to delete " + sessionNode, re); + } + + } + + } + }); + } + return body; + } + +// // LOCAL HELPERS FOR NODE MANAGEMENT +// private Node getOrCreateNode(Node parent, String nodeName, String nodeType) throws RepositoryException { +// Node node = null; +// if (getCmsEditable().canEdit() && !parent.hasNode(nodeName)) { +// node = JcrUtils.mkdirs(parent, nodeName, nodeType); +// parent.getSession().save(); +// } +// +// if (getCmsEditable().canEdit() || parent.hasNode(nodeName)) +// node = parent.getNode(nodeName); +// +// return node; +// } + + private SelectionListener getRemoveValueSelListener() { + return new SelectionAdapter() { + private static final long serialVersionUID = 9022259089907445195L; + + @Override + public void widgetSelected(SelectionEvent e) { + Object source = e.getSource(); + if (source instanceof Button) { + Button btn = (Button) source; + Object obj = btn.getData(FormConstants.LINKED_VALUE); + SwtEditablePart ep = findDataParent(btn); + if (ep != null && ep instanceof EditableMultiStringProperty) { + EditableMultiStringProperty emsp = (EditableMultiStringProperty) ep; + List values = emsp.getValues(); + if (values.contains(obj)) { + values.remove(values.indexOf(obj)); + emsp.setValues(values); + try { + save(emsp); + // TODO workaround to force refresh + edit(emsp, 0); + cancelEdit(); + } catch (RepositoryException e1) { + throw new JcrException("Unable to remove value " + obj, e1); + } + layout(emsp); + } + } + } + } + }; + } + + protected void setPropertySilently(Node node, String propName, String value) throws RepositoryException { + try { + // TODO Clean this: + // Format strings to replace \n + value = value.replaceAll("\n", "
"); + // Do not make the update if validation fails + try { + MarkupValidatorCopy.getInstance().validate(value); + } catch (Exception e) { + log.warn("Cannot set [" + value + "] on prop " + propName + "of " + node + + ", String cannot be validated - " + e.getMessage()); + return; + } + // TODO check if the newly created property is of the correct type, + // otherwise the property will be silently created with a STRING + // property type. + node.setProperty(propName, value); + } catch (ValueFormatException vfe) { + log.warn("Cannot set [" + value + "] on prop " + propName + "of " + node + " - " + vfe.getMessage()); + } + } +} \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormStyle.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormStyle.java new file mode 100644 index 0000000..709ecd0 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormStyle.java @@ -0,0 +1,29 @@ +package org.argeo.cms.ui.forms; + +import org.argeo.api.cms.ux.CmsStyle; + +/** Syles used */ +public enum FormStyle implements CmsStyle { + // Main + form, title, + // main part + header, headerBtn, headerCombo, section, sectionHeader, + // Property fields + propertyLabel, propertyText, propertyMessage, errorMessage, + // Date + popupCalendar, + // Buttons + starred, unstarred, starOverlay, editOverlay, deleteOverlay, updateOverlay, deleteOverlaySmall, calendar, delete, + // Contacts + email, address, phone, website, + // Social Media + facebook, twitter, linkedIn, instagram; + + @Override + public String getClassPrefix() { + return "argeo-form"; + } + + // TODO clean button style management + public final static String BUTTON_SUFFIX = "_btn"; +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormUtils.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormUtils.java new file mode 100644 index 0000000..eeafabb --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormUtils.java @@ -0,0 +1,196 @@ +package org.argeo.cms.ui.forms; + +import java.text.DateFormat; +import java.text.ParseException; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.api.cms.CmsLog; +import org.argeo.api.cms.ux.CmsView; +import org.argeo.cms.swt.CmsException; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.eclipse.jface.fieldassist.ControlDecoration; +import org.eclipse.jface.fieldassist.FieldDecorationRegistry; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +/** Utilitary methods to ease implementation of CMS forms */ +public class FormUtils { + private final static CmsLog log = CmsLog.getLog(FormUtils.class); + + public final static String DEFAULT_SHORT_DATE_FORMAT = "dd/MM/yyyy"; + + /** Best effort to convert a String to a calendar. Fails silently */ + public static Calendar parseDate(DateFormat dateFormat, String calStr) { + Calendar cal = null; + if (EclipseUiUtils.notEmpty(calStr)) { + try { + Date date = dateFormat.parse(calStr); + cal = new GregorianCalendar(); + cal.setTime(date); + } catch (ParseException pe) { + // Silent + log.warn("Unable to parse date: " + calStr + " - msg: " + + pe.getMessage()); + } + } + return cal; + } + + /** Add a double click listener on tables that display a JCR node list */ + public static void addCanonicalDoubleClickListener(final TableViewer v) { + v.addDoubleClickListener(new IDoubleClickListener() { + + @Override + public void doubleClick(DoubleClickEvent event) { + CmsView cmsView = CmsUiUtils.getCmsView(); + Node node = (Node) ((IStructuredSelection) event.getSelection()) + .getFirstElement(); + try { + cmsView.navigateTo(node.getPath()); + } catch (RepositoryException e) { + throw new CmsException("Unable to get path for node " + + node + " before calling navigateTo(path)", e); + } + } + }); + } + + // MANAGE ERROR DECORATION + + public static ControlDecoration addDecoration(final Text text) { + final ControlDecoration dynDecoration = new ControlDecoration(text, + SWT.LEFT); + Image icon = getDecorationImage(FieldDecorationRegistry.DEC_ERROR); + dynDecoration.setImage(icon); + dynDecoration.setMarginWidth(3); + dynDecoration.hide(); + return dynDecoration; + } + + public static void refreshDecoration(Text text, ControlDecoration deco, + boolean isValid, boolean clean) { + if (isValid || clean) { + text.setBackground(null); + deco.hide(); + } else { + text.setBackground(new Color(text.getDisplay(), 250, 200, 150)); + deco.show(); + } + } + + public static Image getDecorationImage(String image) { + FieldDecorationRegistry registry = FieldDecorationRegistry.getDefault(); + return registry.getFieldDecoration(image).getImage(); + } + + public static void addCompulsoryDecoration(Label label) { + final ControlDecoration dynDecoration = new ControlDecoration(label, + SWT.RIGHT | SWT.TOP); + Image icon = getDecorationImage(FieldDecorationRegistry.DEC_REQUIRED); + dynDecoration.setImage(icon); + dynDecoration.setMarginWidth(3); + } + + // TODO the read only generation of read only links for various contact type + // should be factorised in the cms Utils. + /** + * Creates the read-only HTML snippet to display in a label with styling + * enabled in order to provide a click-able phone number + */ + public static String getPhoneLink(String value) { + return getPhoneLink(value, value); + } + + /** + * Creates the read-only HTML snippet to display in a label with styling + * enabled in order to provide a click-able phone number + * + * @param value + * @param label + * a potentially distinct label + * @return the link + */ + public static String getPhoneLink(String value, String label) { + StringBuilder builder = new StringBuilder(); + builder.append("").append(label) + .append(""); + return builder.toString(); + } + + /** + * Creates the read-only HTML snippet to display in a label with styling + * enabled in order to provide a click-able mail + */ + public static String getMailLink(String value) { + return getMailLink(value, value); + } + + /** + * Creates the read-only HTML snippet to display in a label with styling + * enabled in order to provide a click-able mail + * + * @param value + * @param label + * a potentially distinct label + * @return the link + */ + public static String getMailLink(String value, String label) { + StringBuilder builder = new StringBuilder(); + value = replaceAmpersand(value); + builder.append("").append(label).append(""); + return builder.toString(); + } + + /** + * Creates the read-only HTML snippet to display in a label with styling + * enabled in order to provide a click-able link + */ + public static String getUrlLink(String value) { + return getUrlLink(value, value); + } + + /** + * Creates the read-only HTML snippet to display in a label with styling + * enabled in order to provide a click-able link + */ + public static String getUrlLink(String value, String label) { + StringBuilder builder = new StringBuilder(); + value = replaceAmpersand(value); + label = replaceAmpersand(label); + if (!(value.startsWith("http://") || value.startsWith("https://"))) + value = "http://" + value; + builder.append("" + label + ""); + return builder.toString(); + } + + private static String AMPERSAND = "&"; + + /** + * Cleans a String by replacing any '&' by its HTML encoding '&#38;' to + * avoid SAXParseException while rendering HTML with RWT + */ + public static String replaceAmpersand(String value) { + value = value.replaceAll("&(?![#a-zA-Z0-9]+;)", AMPERSAND); + return value; + } + + // Prevents instantiation + private FormUtils() { + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/MarkupValidatorCopy.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/MarkupValidatorCopy.java new file mode 100644 index 0000000..3f588d1 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/MarkupValidatorCopy.java @@ -0,0 +1,169 @@ +package org.argeo.cms.ui.forms; + +import java.io.StringReader; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.eclipse.rap.rwt.SingletonUtil; +import org.eclipse.swt.widgets.Widget; +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.helpers.DefaultHandler; + +/** + * Copy of RAP v2.3 since it is in an internal package. + */ +class MarkupValidatorCopy { + + // Used by Eclipse Scout project + public static final String MARKUP_VALIDATION_DISABLED = "org.eclipse.rap.rwt.markupValidationDisabled"; + + private static final String DTD = createDTD(); + private static final Map SUPPORTED_ELEMENTS = createSupportedElementsMap(); + private final SAXParser saxParser; + + public static MarkupValidatorCopy getInstance() { + return SingletonUtil.getSessionInstance(MarkupValidatorCopy.class); + } + + public MarkupValidatorCopy() { + saxParser = createSAXParser(); + } + + public void validate(String text) { + StringBuilder markup = new StringBuilder(); + markup.append(DTD); + markup.append(""); + markup.append(text); + markup.append(""); + InputSource inputSource = new InputSource(new StringReader(markup.toString())); + try { + saxParser.parse(inputSource, new MarkupHandler()); + } catch (RuntimeException exception) { + throw exception; + } catch (Exception exception) { + throw new IllegalArgumentException("Failed to parse markup text", exception); + } + } + + public static boolean isValidationDisabledFor(Widget widget) { + return Boolean.TRUE.equals(widget.getData(MARKUP_VALIDATION_DISABLED)); + } + + private static SAXParser createSAXParser() { + SAXParser result = null; + SAXParserFactory parserFactory = SAXParserFactory.newInstance(); + try { + result = parserFactory.newSAXParser(); + } catch (Exception exception) { + throw new RuntimeException("Failed to create SAX parser", exception); + } + return result; + } + + private static String createDTD() { + StringBuilder result = new StringBuilder(); + result.append(""); + result.append(""); + result.append(""); + result.append(""); + result.append(""); + result.append(""); + result.append(""); + result.append(""); + result.append(""); + result.append(""); + result.append("]>"); + return result.toString(); + } + + private static Map createSupportedElementsMap() { + Map result = new HashMap(); + result.put("html", new String[0]); + result.put("br", new String[0]); + result.put("b", new String[] { "style" }); + result.put("strong", new String[] { "style" }); + result.put("i", new String[] { "style" }); + result.put("em", new String[] { "style" }); + result.put("sub", new String[] { "style" }); + result.put("sup", new String[] { "style" }); + result.put("big", new String[] { "style" }); + result.put("small", new String[] { "style" }); + result.put("del", new String[] { "style" }); + result.put("ins", new String[] { "style" }); + result.put("code", new String[] { "style" }); + result.put("samp", new String[] { "style" }); + result.put("kbd", new String[] { "style" }); + result.put("var", new String[] { "style" }); + result.put("cite", new String[] { "style" }); + result.put("dfn", new String[] { "style" }); + result.put("q", new String[] { "style" }); + result.put("abbr", new String[] { "style", "title" }); + result.put("span", new String[] { "style" }); + result.put("img", new String[] { "style", "src", "width", "height", "title", "alt" }); + result.put("a", new String[] { "style", "href", "target", "title" }); + return result; + } + + private static class MarkupHandler extends DefaultHandler { + + @Override + public void startElement(String uri, String localName, String name, Attributes attributes) { + checkSupportedElements(name, attributes); + checkSupportedAttributes(name, attributes); + checkMandatoryAttributes(name, attributes); + } + + private static void checkSupportedElements(String elementName, Attributes attributes) { + if (!SUPPORTED_ELEMENTS.containsKey(elementName)) { + throw new IllegalArgumentException("Unsupported element in markup text: " + elementName); + } + } + + private static void checkSupportedAttributes(String elementName, Attributes attributes) { + if (attributes.getLength() > 0) { + List supportedAttributes = Arrays.asList(SUPPORTED_ELEMENTS.get(elementName)); + int index = 0; + String attributeName = attributes.getQName(index); + while (attributeName != null) { + if (!supportedAttributes.contains(attributeName)) { + String message = "Unsupported attribute \"{0}\" for element \"{1}\" in markup text"; + message = MessageFormat.format(message, new Object[] { attributeName, elementName }); + throw new IllegalArgumentException(message); + } + index++; + attributeName = attributes.getQName(index); + } + } + } + + private static void checkMandatoryAttributes(String elementName, Attributes attributes) { + checkIntAttribute(elementName, attributes, "img", "width"); + checkIntAttribute(elementName, attributes, "img", "height"); + } + + private static void checkIntAttribute(String elementName, Attributes attributes, String checkedElementName, + String checkedAttributeName) { + if (checkedElementName.equals(elementName)) { + String attribute = attributes.getValue(checkedAttributeName); + try { + Integer.parseInt(attribute); + } catch (NumberFormatException exception) { + String message = "Mandatory attribute \"{0}\" for element \"{1}\" is missing or not a valid integer"; + Object[] arguments = new Object[] { checkedAttributeName, checkedElementName }; + message = MessageFormat.format(message, arguments); + throw new IllegalArgumentException(message); + } + } + } + + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/package-info.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/package-info.java new file mode 100644 index 0000000..5f954c1 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/package-info.java @@ -0,0 +1,2 @@ +/** Argeo CMS forms, based on SWT/JFace. */ +package org.argeo.cms.ui.forms; \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/CmsFsBrowser.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/CmsFsBrowser.java new file mode 100644 index 0000000..d9c1c12 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/CmsFsBrowser.java @@ -0,0 +1,524 @@ +package org.argeo.cms.ui.fs; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.DirectoryStream; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.spi.FileSystemProvider; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.ArrayList; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.Repository; +import javax.jcr.Session; + +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.jcr.CmsJcrUtils; +import org.argeo.cms.swt.CmsException; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.eclipse.ui.ColumnDefinition; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.fs.FileIconNameLabelProvider; +import org.argeo.eclipse.ui.fs.FsTableViewer; +import org.argeo.eclipse.ui.fs.FsUiConstants; +import org.argeo.eclipse.ui.fs.FsUiUtils; +import org.argeo.eclipse.ui.fs.NioFileLabelProvider; +import org.argeo.jcr.JcrUtils; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.SashForm; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.MouseAdapter; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.layout.RowData; +import org.eclipse.swt.layout.RowLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.Text; + +/** + * Default CMS browser composite: a sashForm layout with bookmarks at the left + * hand side, a simple table in the middle and an overview at right hand side. + */ +public class CmsFsBrowser extends Composite { + // private final static Log log = LogFactory.getLog(CmsFsBrowser.class); + private static final long serialVersionUID = -40347919096946585L; + + private final FileSystemProvider nodeFileSystemProvider; + private final Node currentBaseContext; + + // UI Parts for the browser + private Composite leftPannelCmp; + private Composite filterCmp; + private Text filterTxt; + private FsTableViewer directoryDisplayViewer; + private Composite rightPannelCmp; + + private FsContextMenu contextMenu; + + // Local context (this composite is state full) + private Path initialPath; + private Path currDisplayedFolder; + private Path currSelected; + + // local variables (to be cleaned) + private int bookmarkColWith = 500; + + /* + * WARNING: unfinalised implementation of the mechanism to retrieve base + * paths + */ + + private final static String NODE_PREFIX = "node://"; + + private String getCurrentHomePath() { + Session session = null; + try { + Repository repo = currentBaseContext.getSession().getRepository(); + session = CurrentUser.tryAs(() -> repo.login()); + String homepath = CmsJcrUtils.getUserHome(session).getPath(); + return homepath; + } catch (Exception e) { + throw new CmsException("Cannot retrieve Current User Home Path", e); + } finally { + JcrUtils.logoutQuietly(session); + } + } + + protected Path[] getMyFilesPath() { + // return Paths.get(System.getProperty("user.dir")); + String currHomeUriStr = NODE_PREFIX + getCurrentHomePath(); + try { + URI uri = new URI(currHomeUriStr); + FileSystem fileSystem = nodeFileSystemProvider.getFileSystem(uri); + if (fileSystem == null) { + PrivilegedExceptionAction pea = new PrivilegedExceptionAction() { + @Override + public FileSystem run() throws Exception { + return nodeFileSystemProvider.newFileSystem(uri, null); + } + + }; + fileSystem = CurrentUser.tryAs(pea); + } + Path[] paths = { fileSystem.getPath(getCurrentHomePath()), fileSystem.getPath("/") }; + return paths; + } catch (URISyntaxException | PrivilegedActionException e) { + throw new RuntimeException("unable to initialise home file system for " + currHomeUriStr, e); + } + } + + private Path[] getMyGroupsFilesPath() { + // TODO + Path[] paths = { Paths.get(System.getProperty("user.dir")), Paths.get("/tmp") }; + return paths; + } + + private Path[] getMyBookmarks() { + // TODO + Path[] paths = { Paths.get(System.getProperty("user.dir")), Paths.get("/tmp"), Paths.get("/opt") }; + return paths; + } + + /* End of warning */ + + public CmsFsBrowser(Composite parent, int style, Node context, FileSystemProvider fileSystemProvider) { + super(parent, style); + this.nodeFileSystemProvider = fileSystemProvider; + this.currentBaseContext = context; + + this.setLayout(EclipseUiUtils.noSpaceGridLayout()); + + SashForm form = new SashForm(this, SWT.HORIZONTAL); + + leftPannelCmp = new Composite(form, SWT.NO_FOCUS); + // Bookmarks are still static + populateBookmarks(leftPannelCmp); + + Composite centerCmp = new Composite(form, SWT.BORDER | SWT.NO_FOCUS); + createDisplay(centerCmp); + + rightPannelCmp = new Composite(form, SWT.NO_FOCUS); + + form.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + form.setWeights(new int[] { 15, 40, 20 }); + } + + void refresh() { + modifyFilter(false); + // also refresh bookmarks and groups + } + + private void createDisplay(final Composite parent) { + parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); + + // top filter + filterCmp = new Composite(parent, SWT.NO_FOCUS); + filterCmp.setLayoutData(EclipseUiUtils.fillWidth()); + addFilterPanel(filterCmp); + + // Main display + directoryDisplayViewer = new FsTableViewer(parent, SWT.MULTI); + List colDefs = new ArrayList<>(); + colDefs.add(new ColumnDefinition(new FileIconNameLabelProvider(), "Name", 250)); + colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_SIZE), "Size", 100)); + colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_TYPE), "Type", 150)); + colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_LAST_MODIFIED), + "Last modified", 400)); + final Table table = directoryDisplayViewer.configureDefaultTable(colDefs); + table.setLayoutData(EclipseUiUtils.fillAll()); + + // table.addKeyListener(new KeyListener() { + // private static final long serialVersionUID = -8083424284436715709L; + // + // @Override + // public void keyReleased(KeyEvent e) { + // } + // + // @Override + // public void keyPressed(KeyEvent e) { + // if (log.isDebugEnabled()) + // log.debug("Key event received: " + e.keyCode); + // IStructuredSelection selection = (IStructuredSelection) + // directoryDisplayViewer.getSelection(); + // Path selected = null; + // if (!selection.isEmpty()) + // selected = ((Path) selection.getFirstElement()); + // if (e.keyCode == SWT.CR) { + // if (!Files.isDirectory(selected)) + // return; + // if (selected != null) { + // currDisplayedFolder = selected; + // directoryDisplayViewer.setInput(currDisplayedFolder, "*"); + // } + // } else if (e.keyCode == SWT.BS) { + // currDisplayedFolder = currDisplayedFolder.getParent(); + // directoryDisplayViewer.setInput(currDisplayedFolder, "*"); + // directoryDisplayViewer.getTable().setFocus(); + // } + // } + // }); + + directoryDisplayViewer.addSelectionChangedListener(new ISelectionChangedListener() { + + @Override + public void selectionChanged(SelectionChangedEvent event) { + IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection(); + Path selected = null; + if (selection.isEmpty()) + setSelected(null); + else + selected = ((Path) selection.getFirstElement()); + if (selected != null) { + // TODO manage multiple selection + setSelected(selected); + } + } + }); + + directoryDisplayViewer.addDoubleClickListener(new IDoubleClickListener() { + @Override + public void doubleClick(DoubleClickEvent event) { + IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection(); + Path selected = null; + if (!selection.isEmpty()) + selected = ((Path) selection.getFirstElement()); + if (selected != null) { + if (!Files.isDirectory(selected)) + return; + setInput(selected); + } + } + }); + + // The context menu + contextMenu = new FsContextMenu(this); + + table.addMouseListener(new MouseAdapter() { + private static final long serialVersionUID = 6737579410648595940L; + + @Override + public void mouseDown(MouseEvent e) { + if (e.button == 3) { + // contextMenu.setCurrFolderPath(currDisplayedFolder); + contextMenu.show(table, new Point(e.x, e.y), currDisplayedFolder); + } + } + }); + } + + private void addPathElementBtn(Path path) { + Button elemBtn = new Button(filterCmp, SWT.PUSH); + String nameStr; + if (path.toString().equals("/")) + nameStr = "[jcr:root]"; + else + nameStr = path.getFileName().toString(); + elemBtn.setText(nameStr + " >> "); + CmsSwtUtils.style(elemBtn, FsStyles.BREAD_CRUMB_BTN); + elemBtn.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = -4103695476023480651L; + + @Override + public void widgetSelected(SelectionEvent e) { + setInput(path); + } + }); + } + + public void setInput(Path path) { + if (path.equals(currDisplayedFolder)) + return; + currDisplayedFolder = path; + + Path diff = initialPath.relativize(currDisplayedFolder); + + for (Control child : filterCmp.getChildren()) + if (!child.equals(filterTxt)) + child.dispose(); + + addPathElementBtn(initialPath); + Path currTarget = initialPath; + if (!diff.toString().equals("")) + for (Path pathElem : diff) { + currTarget = currTarget.resolve(pathElem); + addPathElementBtn(currTarget); + } + + filterTxt.setText(""); + filterTxt.moveBelow(null); + setSelected(null); + filterCmp.getParent().layout(true, true); + } + + private void setSelected(Path path) { + currSelected = path; + setOverviewInput(path); + } + + public Viewer getViewer() { + return directoryDisplayViewer; + } + + private void populateBookmarks(Composite parent) { + CmsSwtUtils.clear(parent); + parent.setLayout(new GridLayout()); + ISelectionChangedListener selList = new BookmarksSelChangeListener(); + + FsTableViewer homeViewer = new FsTableViewer(parent, SWT.SINGLE | SWT.NO_SCROLL); + Table table = homeViewer.configureDefaultSingleColumnTable(bookmarkColWith); + GridData gd = EclipseUiUtils.fillWidth(); + gd.horizontalIndent = 10; + table.setLayoutData(gd); + homeViewer.addSelectionChangedListener(selList); + homeViewer.setPathsInput(getMyFilesPath()); + + appendTitle(parent, "Shared files"); + FsTableViewer groupsViewer = new FsTableViewer(parent, SWT.SINGLE | SWT.NO_SCROLL); + table = groupsViewer.configureDefaultSingleColumnTable(bookmarkColWith); + gd = EclipseUiUtils.fillWidth(); + gd.horizontalIndent = 10; + table.setLayoutData(gd); + groupsViewer.addSelectionChangedListener(selList); + groupsViewer.setPathsInput(getMyGroupsFilesPath()); + + appendTitle(parent, "My bookmarks"); + FsTableViewer bookmarksViewer = new FsTableViewer(parent, SWT.SINGLE | SWT.NO_SCROLL); + table = bookmarksViewer.configureDefaultSingleColumnTable(bookmarkColWith); + gd = EclipseUiUtils.fillWidth(); + gd.horizontalIndent = 10; + table.setLayoutData(gd); + bookmarksViewer.addSelectionChangedListener(selList); + bookmarksViewer.setPathsInput(getMyBookmarks()); + } + + /** + * Recreates the content of the box that displays information about the + * current selected Path. + */ + private void setOverviewInput(Path path) { + try { + EclipseUiUtils.clear(rightPannelCmp); + rightPannelCmp.setLayout(new GridLayout()); + if (path != null) { + // if (isImg(context)) { + // EditableImage image = new Img(parent, RIGHT, context, + // imageWidth); + // image.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, + // true, false, + // 2, 1)); + // } + + Label contextL = new Label(rightPannelCmp, SWT.NONE); + contextL.setText(path.getFileName().toString()); + contextL.setFont(EclipseUiUtils.getBoldFont(rightPannelCmp)); + addProperty(rightPannelCmp, "Last modified", Files.getLastModifiedTime(path).toString()); + // addProperty(rightPannelCmp, "Owner", + // Files.getOwner(path).getName()); + if (Files.isDirectory(path)) { + addProperty(rightPannelCmp, "Type", "Folder"); + } else { + String mimeType = Files.probeContentType(path); + if (EclipseUiUtils.isEmpty(mimeType)) + mimeType = "Unknown"; + addProperty(rightPannelCmp, "Type", mimeType); + addProperty(rightPannelCmp, "Size", FsUiUtils.humanReadableByteCount(Files.size(path), false)); + } + } + rightPannelCmp.layout(true, true); + } catch (IOException e) { + throw new CmsException("Cannot display details for " + path.toString(), e); + } + } + + private void addFilterPanel(Composite parent) { + RowLayout rl = new RowLayout(SWT.HORIZONTAL); + rl.wrap = true; + parent.setLayout(rl); + // parent.setLayout(EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, + // false))); + + filterTxt = new Text(parent, SWT.SEARCH | SWT.ICON_CANCEL); + filterTxt.setMessage("Search current folder"); + filterTxt.setLayoutData(new RowData(250, SWT.DEFAULT)); + filterTxt.addModifyListener(new ModifyListener() { + private static final long serialVersionUID = 1L; + + public void modifyText(ModifyEvent event) { + modifyFilter(false); + } + }); + filterTxt.addKeyListener(new KeyListener() { + private static final long serialVersionUID = 2533535233583035527L; + + @Override + public void keyReleased(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + // boolean shiftPressed = (e.stateMask & SWT.SHIFT) != 0; + // // boolean altPressed = (e.stateMask & SWT.ALT) != 0; + // FilterEntitiesVirtualTable currTable = null; + // if (currEdited != null) { + // FilterEntitiesVirtualTable table = + // browserCols.get(currEdited); + // if (table != null && !table.isDisposed()) + // currTable = table; + // } + // + // if (e.keyCode == SWT.ARROW_DOWN) + // currTable.setFocus(); + // else if (e.keyCode == SWT.BS) { + // if (filterTxt.getText().equals("") + // && !(currEdited.getNameCount() == 1 || + // currEdited.equals(initialPath))) { + // Path oldEdited = currEdited; + // Path parentPath = currEdited.getParent(); + // setEdited(parentPath); + // if (browserCols.containsKey(parentPath)) + // browserCols.get(parentPath).setSelected(oldEdited); + // filterTxt.setFocus(); + // e.doit = false; + // } + // } else if (e.keyCode == SWT.TAB && !shiftPressed) { + // Path uniqueChild = getOnlyChild(currEdited, + // filterTxt.getText()); + // if (uniqueChild != null) { + // // Highlight the unique chosen child + // currTable.setSelected(uniqueChild); + // setEdited(uniqueChild); + // } + // filterTxt.setFocus(); + // e.doit = false; + // } + } + }); + } + + private Path getOnlyChild(Path parent, String filter) { + try (DirectoryStream stream = Files.newDirectoryStream(currDisplayedFolder, filter + "*")) { + Path uniqueChild = null; + boolean moreThanOne = false; + loop: for (Path entry : stream) { + if (uniqueChild == null) { + uniqueChild = entry; + } else { + moreThanOne = true; + break loop; + } + } + if (!moreThanOne) + return uniqueChild; + return null; + } catch (IOException ioe) { + throw new CmsException( + "Unable to determine unique child existence and get it under " + parent + " with filter " + filter, + ioe); + } + } + + private void modifyFilter(boolean fromOutside) { + if (!fromOutside) + if (currDisplayedFolder != null) { + String filter = filterTxt.getText() + "*"; + directoryDisplayViewer.setInput(currDisplayedFolder, filter); + } + } + + private class BookmarksSelChangeListener implements ISelectionChangedListener { + + @Override + public void selectionChanged(SelectionChangedEvent event) { + IStructuredSelection selection = (IStructuredSelection) event.getSelection(); + if (selection.isEmpty()) + return; + else { + Path newSelected = (Path) selection.getFirstElement(); + if (newSelected.equals(currDisplayedFolder) && newSelected.equals(initialPath)) + return; + initialPath = newSelected; + setInput(newSelected); + } + } + } + + // Simplify UI implementation + private void addProperty(Composite parent, String propName, String value) { + Label contextL = new Label(parent, SWT.NONE); + contextL.setText(propName + ": " + value); + } + + private Label appendTitle(Composite parent, String value) { + Label titleLbl = new Label(parent, SWT.NONE); + titleLbl.setText(value); + titleLbl.setFont(EclipseUiUtils.getBoldFont(parent)); + GridData gd = EclipseUiUtils.fillWidth(); + gd.horizontalIndent = 5; + gd.verticalIndent = 5; + titleLbl.setLayoutData(gd); + return titleLbl; + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/FileDrop.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/FileDrop.java new file mode 100644 index 0000000..e875b5a --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/FileDrop.java @@ -0,0 +1,37 @@ +package org.argeo.cms.ui.fs; + +import java.io.IOException; +import java.io.InputStream; + +import org.argeo.api.cms.CmsLog; +import org.argeo.eclipse.ui.specific.FileDropAdapter; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.DropTarget; +import org.eclipse.swt.dnd.DropTargetEvent; +import org.eclipse.swt.widgets.Control; + +/** Allows a control to receive file drops. */ +public class FileDrop { + private final static CmsLog log = CmsLog.getLog(FileDrop.class); + + public void createDropTarget(Control control) { + FileDropAdapter fileDropAdapter = new FileDropAdapter() { + @Override + protected void processUpload(InputStream in, String fileName, String contentType) throws IOException { + if (log.isDebugEnabled()) + log.debug("Process upload of " + fileName + " (" + contentType + ")"); + processFileUpload(in, fileName, contentType); + } + }; + DropTarget dropTarget = new DropTarget(control, DND.DROP_MOVE | DND.DROP_COPY); + fileDropAdapter.prepareDropTarget(control, dropTarget); + } + + public void handleFileDrop(Control control, DropTargetEvent event) { + } + + /** Executed in UI thread */ + protected void processFileUpload(InputStream in, String fileName, String contentType) throws IOException { + + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/FsContextMenu.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/FsContextMenu.java new file mode 100644 index 0000000..1fb3c2a --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/FsContextMenu.java @@ -0,0 +1,383 @@ +package org.argeo.cms.ui.fs; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Method; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.commons.io.IOUtils; +import org.argeo.api.cms.CmsLog; +import org.argeo.cms.swt.CmsException; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.dialogs.SingleValue; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.ShellEvent; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; + +/** Generic popup context menu to manage NIO Path in a Viewer. */ +public class FsContextMenu extends Shell { + private static final long serialVersionUID = -9120261153509855795L; + + private final static CmsLog log = CmsLog.getLog(FsContextMenu.class); + + // Default known actions + public final static String ACTION_ID_CREATE_FOLDER = "createFolder"; + public final static String ACTION_ID_BOOKMARK_FOLDER = "bookmarkFolder"; + public final static String ACTION_ID_SHARE_FOLDER = "shareFolder"; + public final static String ACTION_ID_DOWNLOAD_FOLDER = "downloadFolder"; + public final static String ACTION_ID_DELETE = "delete"; + public final static String ACTION_ID_UPLOAD_FILE = "uploadFiles"; + public final static String ACTION_ID_OPEN = "open"; + + // Local context + private final CmsFsBrowser browser; + // private final Viewer viewer; + private final static String KEY_ACTION_ID = "actionId"; + private final static String[] DEFAULT_ACTIONS = { ACTION_ID_CREATE_FOLDER, ACTION_ID_BOOKMARK_FOLDER, + ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_DELETE, ACTION_ID_UPLOAD_FILE, + ACTION_ID_OPEN }; + private Map actionButtons = new HashMap(); + + private Path currFolderPath; + + public FsContextMenu(CmsFsBrowser browser) { // Viewer viewer, Display + // display) { + super(browser.getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP); + this.browser = browser; + setLayout(EclipseUiUtils.noSpaceGridLayout()); + + Composite boxCmp = new Composite(this, SWT.NO_FOCUS | SWT.BORDER); + boxCmp.setLayout(EclipseUiUtils.noSpaceGridLayout()); + CmsSwtUtils.style(boxCmp, FsStyles.CONTEXT_MENU_BOX); + createContextMenu(boxCmp); + + addShellListener(new ActionsShellListener()); + } + + protected void createContextMenu(Composite boxCmp) { + ActionsSelListener asl = new ActionsSelListener(); + for (String actionId : DEFAULT_ACTIONS) { + Button btn = new Button(boxCmp, SWT.FLAT | SWT.PUSH | SWT.LEAD); + btn.setText(getLabel(actionId)); + btn.setLayoutData(EclipseUiUtils.fillWidth()); + CmsSwtUtils.markup(btn); + CmsSwtUtils.style(btn, actionId + FsStyles.BUTTON_SUFFIX); + btn.setData(KEY_ACTION_ID, actionId); + btn.addSelectionListener(asl); + actionButtons.put(actionId, btn); + } + } + + protected String getLabel(String actionId) { + switch (actionId) { + case ACTION_ID_CREATE_FOLDER: + return "Create Folder"; + case ACTION_ID_BOOKMARK_FOLDER: + return "Bookmark Folder"; + case ACTION_ID_SHARE_FOLDER: + return "Share Folder"; + case ACTION_ID_DOWNLOAD_FOLDER: + return "Download as zip archive"; + case ACTION_ID_DELETE: + return "Delete"; + case ACTION_ID_UPLOAD_FILE: + return "Upload Files"; + case ACTION_ID_OPEN: + return "Open"; + default: + throw new IllegalArgumentException("Unknown action ID " + actionId); + } + } + + protected void aboutToShow(Control source, Point location) { + IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection()); + boolean emptySel = true; + boolean multiSel = false; + boolean isFolder = true; + if (selection != null && !selection.isEmpty()) { + emptySel = false; + multiSel = selection.size() > 1; + if (!multiSel && selection.getFirstElement() instanceof Path) { + isFolder = Files.isDirectory((Path) selection.getFirstElement()); + } + } + if (emptySel) { + setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE); + setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_DELETE, ACTION_ID_OPEN, + // to be implemented + ACTION_ID_BOOKMARK_FOLDER); + } else if (multiSel) { + setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_DELETE); + setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_OPEN, + // to be implemented + ACTION_ID_BOOKMARK_FOLDER); + } else if (isFolder) { + setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_DELETE); + setVisible(false, ACTION_ID_OPEN, + // to be implemented + ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_BOOKMARK_FOLDER); + } else { + setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_OPEN, ACTION_ID_DELETE); + setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, + // to be implemented + ACTION_ID_BOOKMARK_FOLDER); + } + } + + private void setVisible(boolean visible, String... buttonIds) { + for (String id : buttonIds) { + Button button = actionButtons.get(id); + button.setVisible(visible); + GridData gd = (GridData) button.getLayoutData(); + gd.heightHint = visible ? SWT.DEFAULT : 0; + } + } + + public void show(Control source, Point location, Path currFolderPath) { + if (isVisible()) + setVisible(false); + // TODO find a better way to retrieve the parent path (cannot be deduced + // from table content because it will fail on an empty folder) + this.currFolderPath = currFolderPath; + aboutToShow(source, location); + pack(); + layout(); + if (source instanceof Control) + setLocation(((Control) source).toDisplay(location.x, location.y)); + open(); + } + + class StyleButton extends Label { + private static final long serialVersionUID = 7731102609123946115L; + + public StyleButton(Composite parent, int swtStyle) { + super(parent, swtStyle); + } + + } + + // class ActionsMouseListener extends MouseAdapter { + // private static final long serialVersionUID = -1041871937815812149L; + // + // @Override + // public void mouseDown(MouseEvent e) { + // Object eventSource = e.getSource(); + // if (e.button == 1) { + // if (eventSource instanceof Button) { + // Button pressedBtn = (Button) eventSource; + // String actionId = (String) pressedBtn.getData(KEY_ACTION_ID); + // switch (actionId) { + // case ACTION_ID_CREATE_FOLDER: + // createFolder(); + // break; + // case ACTION_ID_DELETE: + // deleteItems(); + // break; + // default: + // throw new IllegalArgumentException("Unimplemented action " + actionId); + // // case ACTION_ID_SHARE_FOLDER: + // // return "Share Folder"; + // // case ACTION_ID_DOWNLOAD_FOLDER: + // // return "Download as zip archive"; + // // case ACTION_ID_UPLOAD_FILE: + // // return "Upload Files"; + // // case ACTION_ID_OPEN: + // // return "Open"; + // } + // } + // } + // viewer.getControl().setFocus(); + // // setVisible(false); + // } + // } + + class ActionsSelListener extends SelectionAdapter { + private static final long serialVersionUID = -1041871937815812149L; + + @Override + public void widgetSelected(SelectionEvent e) { + Object eventSource = e.getSource(); + if (eventSource instanceof Button) { + Button pressedBtn = (Button) eventSource; + String actionId = (String) pressedBtn.getData(KEY_ACTION_ID); + switch (actionId) { + case ACTION_ID_CREATE_FOLDER: + createFolder(); + break; + case ACTION_ID_DELETE: + deleteItems(); + break; + case ACTION_ID_OPEN: + openFile(); + break; + case ACTION_ID_UPLOAD_FILE: + uploadFiles(); + break; + default: + throw new IllegalArgumentException("Unimplemented action " + actionId); + // case ACTION_ID_SHARE_FOLDER: + // return "Share Folder"; + // case ACTION_ID_DOWNLOAD_FOLDER: + // return "Download as zip archive"; + // case ACTION_ID_OPEN: + // return "Open"; + } + } + browser.setFocus(); + // viewer.getControl().setFocus(); + // setVisible(false); + + } + } + + class ActionsShellListener extends org.eclipse.swt.events.ShellAdapter { + private static final long serialVersionUID = -5092341449523150827L; + + @Override + public void shellDeactivated(ShellEvent e) { + setVisible(false); + } + } + + private void openFile() { + log.warn("Implement single sourced, workbench independant \"Open File\" action"); + } + + private void deleteItems() { + IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection()); + if (selection.isEmpty()) + return; + + StringBuilder builder = new StringBuilder(); + @SuppressWarnings("unchecked") + Iterator iterator = selection.iterator(); + List paths = new ArrayList<>(); + + while (iterator.hasNext()) { + Path path = (Path) iterator.next(); + builder.append(path.getFileName() + ", "); + paths.add(path); + } + String msg = "You are about to delete following elements: " + builder.substring(0, builder.length() - 2) + + ". Are you sure?"; + if (MessageDialog.openConfirm(this, "Confirm deletion", msg)) { + for (Path path : paths) { + try { + // Might have already been deleted if we are in a tree + Files.deleteIfExists(path); + } catch (IOException e) { + throw new CmsException("Cannot delete path " + path, e); + } + } + browser.refresh(); + } + } + + private void createFolder() { + String msg = "Please provide a name."; + String name = SingleValue.ask("Create folder", msg); + // TODO enhance check of name validity + if (EclipseUiUtils.notEmpty(name)) { + try { + Path child = currFolderPath.resolve(name); + if (Files.exists(child)) + throw new CmsException("An item with name " + name + " already exists at " + + currFolderPath.toString() + ", cannot create"); + else + Files.createDirectories(child); + browser.refresh(); + } catch (IOException e) { + throw new CmsException("Cannot create folder " + name + " at " + currFolderPath.toString(), e); + } + } + } + + private void uploadFiles() { + try { + FileDialog dialog = new FileDialog(browser.getShell(), SWT.MULTI); + dialog.setText("Choose one or more files to upload"); + + if (EclipseUiUtils.notEmpty(dialog.open())) { + String[] names = dialog.getFileNames(); + // Workaround small differences between RAP and RCP + // 1. returned names are absolute path on RAP and + // relative in RCP + // 2. in RCP we must use getFilterPath that does not + // exists on RAP + Method filterMethod = null; + Path parPath = null; + try { + filterMethod = dialog.getClass().getDeclaredMethod("getFilterPath"); + String filterPath = (String) filterMethod.invoke(dialog); + parPath = Paths.get(filterPath); + } catch (NoSuchMethodException nsme) { // RAP + } + if (names.length == 0) + return; + else { + loop: for (String name : names) { + Path tmpPath = Paths.get(name); + if (parPath != null) + tmpPath = parPath.resolve(tmpPath); + if (Files.exists(tmpPath)) { + URI uri = tmpPath.toUri(); + String uriStr = uri.toString(); + + if (Files.isDirectory(tmpPath)) { + MessageDialog.openError(browser.getShell(), "Unimplemented directory import", + "Upload of directories in the system is not yet implemented"); + continue loop; + } + Path targetPath = currFolderPath.resolve(tmpPath.getFileName().toString()); + InputStream in = null; + try { + in = new ByteArrayInputStream(Files.readAllBytes(tmpPath)); + Files.copy(in, targetPath); + Files.delete(tmpPath); + } finally { + IOUtils.closeQuietly(in); + } + if (log.isDebugEnabled()) + log.debug("copied uploaded file " + uriStr + " to " + targetPath.toString()); + } else { + String msg = "Cannot copy tmp file from " + tmpPath.toString(); + if (parPath != null) + msg += "\nPlease remember that file upload fails when choosing files from the \"Recently Used\" bookmarks on some OS"; + MessageDialog.openError(browser.getShell(), "Missing file", msg); + continue loop; + } + } + browser.refresh(); + } + } + } catch (Exception e) { + e.printStackTrace(); + MessageDialog.openError(getShell(), "Upload has failed", "Cannot import files to " + currFolderPath); + } + } + + public void setCurrFolderPath(Path currFolderPath) { + this.currFolderPath = currFolderPath; + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/FsStyles.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/FsStyles.java new file mode 100644 index 0000000..9ae3192 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/FsStyles.java @@ -0,0 +1,8 @@ +package org.argeo.cms.ui.fs; + +/** FS Ui specific CSS styles */ +public interface FsStyles { + String BREAD_CRUMB_BTN = "breadCrumb_btn"; + String CONTEXT_MENU_BOX = "contextMenu_box"; + String BUTTON_SUFFIX = "_btn"; +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/package-info.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/package-info.java new file mode 100644 index 0000000..6a6c272 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/package-info.java @@ -0,0 +1,2 @@ +/** SWT/JFace file system components. */ +package org.argeo.cms.ui.fs; \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/Activator.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/Activator.java new file mode 100644 index 0000000..e10da3a --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/Activator.java @@ -0,0 +1,37 @@ +package org.argeo.cms.ui.internal; + +import org.argeo.api.cms.CmsState; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.util.tracker.ServiceTracker; + +public class Activator implements BundleActivator { + + // avoid dependency to RWT OSGi + private final static String CONTEXT_NAME_PROP = "contextName"; + + private static ServiceTracker nodeState; + + // @Override + public void start(BundleContext bc) throws Exception { + // UI +// bc.registerService(ApplicationConfiguration.class, new MaintenanceUi(), +// LangUtils.dico(CONTEXT_NAME_PROP, "system")); +// bc.registerService(ApplicationConfiguration.class, new UserUi(), LangUtils.dico(CONTEXT_NAME_PROP, "user")); + + nodeState = new ServiceTracker<>(bc, CmsState.class, null); + nodeState.open(); + } + + @Override + public void stop(BundleContext context) throws Exception { + if (nodeState != null) { + nodeState.close(); + nodeState = null; + } + } + + public static CmsState getNodeState() { + return nodeState.getService(); + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/JcrContentProvider.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/JcrContentProvider.java new file mode 100644 index 0000000..ea0abdf --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/JcrContentProvider.java @@ -0,0 +1,81 @@ +package org.argeo.cms.ui.internal; + +import java.util.ArrayList; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.RepositoryException; + +import org.argeo.cms.swt.CmsException; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.Viewer; + +@Deprecated +class JcrContentProvider implements ITreeContentProvider { + private static final long serialVersionUID = -1333678161322488674L; + + @Override + public void dispose() { + } + + @Override + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + if (newInput == null) + return; + if (!(newInput instanceof Node)) + throw new CmsException("Input " + newInput + " must be a node"); + } + + @Override + public Object[] getElements(Object inputElement) { + try { + Node node = (Node) inputElement; + ArrayList arr = new ArrayList(); + NodeIterator nit = node.getNodes(); + while (nit.hasNext()) { + arr.add(nit.nextNode()); + } + return arr.toArray(); + } catch (RepositoryException e) { + throw new CmsException("Cannot get elements", e); + } + } + + @Override + public Object[] getChildren(Object parentElement) { + try { + Node node = (Node) parentElement; + ArrayList arr = new ArrayList(); + NodeIterator nit = node.getNodes(); + while (nit.hasNext()) { + arr.add(nit.nextNode()); + } + return arr.toArray(); + } catch (RepositoryException e) { + throw new CmsException("Cannot get elements", e); + } + } + + @Override + public Object getParent(Object element) { + try { + Node node = (Node) element; + if (node.getName().equals("")) + return null; + else + return node.getParent(); + } catch (RepositoryException e) { + throw new CmsException("Cannot get elements", e); + } + } + + @Override + public boolean hasChildren(Object element) { + try { + Node node = (Node) element; + return node.hasNodes(); + } catch (RepositoryException e) { + throw new CmsException("Cannot get elements", e); + } + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/JcrFileUploadReceiver.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/JcrFileUploadReceiver.java new file mode 100644 index 0000000..60bb42b --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/JcrFileUploadReceiver.java @@ -0,0 +1,74 @@ +package org.argeo.cms.ui.internal; + +import static javax.jcr.nodetype.NodeType.NT_FILE; + +import java.io.IOException; +import java.io.InputStream; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.RepositoryException; +import javax.jcr.nodetype.NodeType; + +import org.apache.commons.io.FilenameUtils; +import org.argeo.api.cms.ux.CmsImageManager; +import org.argeo.cms.ui.widgets.Img; +import org.argeo.jcr.JcrException; +import org.argeo.jcr.JcrUtils; +import org.eclipse.rap.fileupload.FileDetails; +import org.eclipse.rap.fileupload.FileUploadReceiver; + +public class JcrFileUploadReceiver extends FileUploadReceiver { + private Img img; + private final Node parentNode; + private final String nodeName; + private final CmsImageManager imageManager; + + /** If nodeName is null, use the uploaded file name */ + public JcrFileUploadReceiver(Img img, Node parentNode, String nodeName, CmsImageManager imageManager) { + super(); + this.img = img; + this.parentNode = parentNode; + this.nodeName = nodeName; + this.imageManager = imageManager; + } + + @Override + public void receive(InputStream stream, FileDetails details) throws IOException { + try { + String fileName = nodeName != null ? nodeName : details.getFileName(); + String contentType = details.getContentType(); + if (isImage(details.getFileName(), contentType)) { + imageManager.uploadImage(img.getNode(),parentNode, fileName, stream, contentType); + return; + } + + Node fileNode; + if (parentNode.hasNode(fileName)) { + fileNode = parentNode.getNode(fileName); + if (!fileNode.isNodeType(NT_FILE)) + fileNode.remove(); + } + fileNode = JcrUtils.copyStreamAsFile(parentNode, fileName, stream); + + if (contentType != null) { + fileNode.addMixin(NodeType.MIX_MIMETYPE); + fileNode.setProperty(Property.JCR_MIMETYPE, contentType); + } + processNewFile(fileNode); + fileNode.getSession().save(); + } catch (RepositoryException e) { + throw new JcrException("Cannot receive " + details, e); + } + } + + protected Boolean isImage(String fileName, String contentType) { + String ext = FilenameUtils.getExtension(fileName); + return ext != null && (ext.equals("png") || ext.equalsIgnoreCase("jpg")); + } + + protected void processNewFile(Node node) { + + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/SimpleEditableImage.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/SimpleEditableImage.java new file mode 100644 index 0000000..6162a74 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/SimpleEditableImage.java @@ -0,0 +1,74 @@ +package org.argeo.cms.ui.internal; + +import javax.jcr.RepositoryException; + +import org.argeo.api.cms.ux.Cms2DSize; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.argeo.cms.ui.widgets.EditableImage; +import org.argeo.cms.ux.AbstractImageManager; +import org.argeo.cms.ux.CmsUxUtils; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Text; + +/** NOT working yet. */ +public class SimpleEditableImage extends EditableImage { + private static final long serialVersionUID = -5689145523114022890L; + + private String src; + private Cms2DSize imageSize; + + public SimpleEditableImage(Composite parent, int swtStyle) { + super(parent, swtStyle); + // load(getControl()); + getParent().layout(); + } + + public SimpleEditableImage(Composite parent, int swtStyle, String src, Cms2DSize imageSize) { + super(parent, swtStyle); + this.src = src; + this.imageSize = imageSize; + } + + @Override + protected Control createControl(Composite box, String style) { + if (isEditing()) { + return createText(box, style); + } else { + return createLabel(box, style); + } + } + + protected String createImgTag() throws RepositoryException { + String imgTag; + if (src != null) + imgTag = CmsUxUtils.img(src, imageSize); + else + imgTag = CmsUiUtils.noImg(imageSize != null ? imageSize : AbstractImageManager.NO_IMAGE_SIZE); + return imgTag; + } + + protected Text createText(Composite box, String style) { + Text text = new Text(box, getStyle()); + CmsSwtUtils.style(text, style); + return text; + } + + public String getSrc() { + return src; + } + + public void setSrc(String src) { + this.src = src; + } + + public Cms2DSize getImageSize() { + return imageSize; + } + + public void setImageSize(Cms2DSize imageSize) { + this.imageSize = imageSize; + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/DefaultRepositoryRegister.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/DefaultRepositoryRegister.java new file mode 100644 index 0000000..3806341 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/DefaultRepositoryRegister.java @@ -0,0 +1,75 @@ +package org.argeo.cms.ui.jcr; + +import java.util.Collections; +import java.util.Map; +import java.util.Observable; +import java.util.TreeMap; + +import javax.jcr.Repository; +import javax.jcr.RepositoryException; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.api.cms.CmsLog; + +public class DefaultRepositoryRegister extends Observable implements RepositoryRegister { + /** Key for a JCR repository alias */ + private final static String CN = CmsConstants.CN; + /** Key for a JCR repository URI */ + // public final static String JCR_REPOSITORY_URI = "argeo.jcr.repository.uri"; + private final static CmsLog log = CmsLog.getLog(DefaultRepositoryRegister.class); + + /** Read only map which will be directly exposed. */ + private Map repositories = Collections.unmodifiableMap(new TreeMap()); + + @SuppressWarnings("rawtypes") + public synchronized Repository getRepository(Map parameters) throws RepositoryException { + if (!parameters.containsKey(CN)) + throw new RepositoryException("Parameter " + CN + " has to be defined."); + String alias = parameters.get(CN).toString(); + if (!repositories.containsKey(alias)) + throw new RepositoryException("No repository registered with alias " + alias); + + return repositories.get(alias); + } + + /** Access to the read-only map */ + public synchronized Map getRepositories() { + return repositories; + } + + /** Registers a service, typically called when OSGi services are bound. */ + @SuppressWarnings("rawtypes") + public synchronized void register(Repository repository, Map properties) { + String alias; + if (properties == null || !properties.containsKey(CN)) { + log.warn("Cannot register a repository if no " + CN + " property is specified."); + return; + } + alias = properties.get(CN).toString(); + Map map = new TreeMap(repositories); + map.put(alias, repository); + repositories = Collections.unmodifiableMap(map); + setChanged(); + notifyObservers(alias); + } + + /** Unregisters a service, typically called when OSGi services are unbound. */ + @SuppressWarnings("rawtypes") + public synchronized void unregister(Repository repository, Map properties) { + // TODO: also check bean name? + if (properties == null || !properties.containsKey(CN)) { + log.warn("Cannot unregister a repository without property " + CN); + return; + } + + String alias = properties.get(CN).toString(); + Map map = new TreeMap(repositories); + if (map.remove(alias) == null) { + log.warn("No repository was registered with alias " + alias); + return; + } + repositories = Collections.unmodifiableMap(map); + setChanged(); + notifyObservers(alias); + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/FullVersioningTreeContentProvider.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/FullVersioningTreeContentProvider.java new file mode 100644 index 0000000..0f7ee77 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/FullVersioningTreeContentProvider.java @@ -0,0 +1,98 @@ +package org.argeo.cms.ui.jcr; + +import java.util.ArrayList; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.nodetype.NodeType; +import javax.jcr.version.Version; +import javax.jcr.version.VersionHistory; +import javax.jcr.version.VersionIterator; +import javax.jcr.version.VersionManager; + +import org.argeo.eclipse.ui.EclipseUiException; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.Viewer; + +/** + * Display some version information of a JCR full versionable node in a tree + * like structure + */ +public class FullVersioningTreeContentProvider implements ITreeContentProvider { + private static final long serialVersionUID = 8691772509491211112L; + + /** + * Sends back the first level of the Tree. input element must be a single + * node object + */ + public Object[] getElements(Object inputElement) { + try { + Node rootNode = (Node) inputElement; + String curPath = rootNode.getPath(); + VersionManager vm = rootNode.getSession().getWorkspace() + .getVersionManager(); + + VersionHistory vh = vm.getVersionHistory(curPath); + List result = new ArrayList(); + VersionIterator vi = vh.getAllLinearVersions(); + + while (vi.hasNext()) { + result.add(vi.nextVersion()); + } + return result.toArray(); + } catch (RepositoryException re) { + throw new EclipseUiException( + "Unexpected error while getting version elements", re); + } + } + + public Object[] getChildren(Object parentElement) { + try { + if (parentElement instanceof Version) { + List tmp = new ArrayList(); + tmp.add(((Version) parentElement).getFrozenNode()); + return tmp.toArray(); + } + } catch (RepositoryException re) { + throw new EclipseUiException("Unexpected error while getting child " + + "node for version element", re); + } + return null; + } + + public Object getParent(Object element) { + try { + // this will not work in a simpleVersionning environment, parent is + // not a node. + if (element instanceof Node + && ((Node) element).isNodeType(NodeType.NT_FROZEN_NODE)) { + Node node = (Node) element; + return node.getParent(); + } else + return null; + } catch (RepositoryException e) { + return null; + } + } + + public boolean hasChildren(Object element) { + try { + if (element instanceof Version) + return true; + else if (element instanceof Node) + return ((Node) element).hasNodes(); + else + return false; + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot check children of " + element, e); + } + } + + public void dispose() { + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrBrowserUtils.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrBrowserUtils.java new file mode 100644 index 0000000..b36acc3 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrBrowserUtils.java @@ -0,0 +1,68 @@ +package org.argeo.cms.ui.jcr; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; + +import org.argeo.cms.ui.jcr.model.RepositoriesElem; +import org.argeo.cms.ui.jcr.model.RepositoryElem; +import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; +import org.argeo.cms.ui.jcr.model.WorkspaceElem; +import org.argeo.cms.ux.widgets.TreeParent; +import org.argeo.eclipse.ui.EclipseUiException; + +/** Useful methods to manage the JCR Browser */ +public class JcrBrowserUtils { + + public static String getPropertyTypeAsString(Property prop) { + try { + return PropertyType.nameFromValue(prop.getType()); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot check type for " + prop, e); + } + } + + /** Insure that the UI component is not stale, refresh if needed */ + public static void forceRefreshIfNeeded(TreeParent element) { + Node curNode = null; + + boolean doRefresh = false; + + try { + if (element instanceof SingleJcrNodeElem) { + curNode = ((SingleJcrNodeElem) element).getNode(); + } else if (element instanceof WorkspaceElem) { + curNode = ((WorkspaceElem) element).getRootNode(); + } + + if (curNode != null && element.getChildren().length != curNode.getNodes().getSize()) + doRefresh = true; + else if (element instanceof RepositoryElem) { + RepositoryElem rn = (RepositoryElem) element; + if (rn.isConnected()) { + String[] wkpNames = rn.getAccessibleWorkspaceNames(); + if (element.getChildren().length != wkpNames.length) + doRefresh = true; + } + } else if (element instanceof RepositoriesElem) { + doRefresh = true; + // Always force refresh for RepositoriesElem : the condition + // below does not take remote repository into account and it is + // not trivial to do so. + + // RepositoriesElem rn = (RepositoriesElem) element; + // if (element.getChildren().length != + // rn.getRepositoryRegister() + // .getRepositories().size()) + // doRefresh = true; + } + if (doRefresh) { + element.clearChildren(); + element.getChildren(); + } + } catch (RepositoryException re) { + throw new EclipseUiException("Unexpected error while synchronising the UI with the JCR repository", re); + } + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrDClickListener.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrDClickListener.java new file mode 100644 index 0000000..1707681 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrDClickListener.java @@ -0,0 +1,60 @@ +package org.argeo.cms.ui.jcr; + +import javax.jcr.Node; + +import org.argeo.cms.ui.jcr.model.RepositoryElem; +import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; +import org.argeo.cms.ui.jcr.model.WorkspaceElem; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.TreeViewer; + +/** Centralizes the management of double click on a NodeTreeViewer */ +public class JcrDClickListener implements IDoubleClickListener { + // private final static Log log = LogFactory + // .getLog(GenericNodeDoubleClickListener.class); + + private TreeViewer nodeViewer; + + // private JcrFileProvider jfp; + // private FileHandler fileHandler; + + public JcrDClickListener(TreeViewer nodeViewer) { + this.nodeViewer = nodeViewer; + // jfp = new JcrFileProvider(); + // Commented out. see https://www.argeo.org/bugzilla/show_bug.cgi?id=188 + // fileHandler = null; + // fileHandler = new FileHandler(jfp); + } + + public void doubleClick(DoubleClickEvent event) { + if (event.getSelection() == null || event.getSelection().isEmpty()) + return; + Object obj = ((IStructuredSelection) event.getSelection()).getFirstElement(); + if (obj instanceof RepositoryElem) { + RepositoryElem rpNode = (RepositoryElem) obj; + if (rpNode.isConnected()) { + rpNode.logout(); + } else { + rpNode.login(); + } + nodeViewer.refresh(obj); + } else if (obj instanceof WorkspaceElem) { + WorkspaceElem wn = (WorkspaceElem) obj; + if (wn.isConnected()) + wn.logout(); + else + wn.login(); + nodeViewer.refresh(obj); + } else if (obj instanceof SingleJcrNodeElem) { + SingleJcrNodeElem sjn = (SingleJcrNodeElem) obj; + Node node = sjn.getNode(); + openNode(node); + } + } + + protected void openNode(Node node) { + // TODO implement generic behaviour + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrImages.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrImages.java new file mode 100644 index 0000000..d1d1e31 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrImages.java @@ -0,0 +1,24 @@ +package org.argeo.cms.ui.jcr; + +import org.argeo.cms.ui.theme.CmsImages; +import org.eclipse.swt.graphics.Image; + +/** Shared icons. */ +public class JcrImages { + public final static Image NODE = CmsImages.createIcon("node.gif"); + public final static Image FOLDER = CmsImages.createIcon("folder.gif"); + public final static Image FILE = CmsImages.createIcon("file.gif"); + public final static Image BINARY = CmsImages.createIcon("binary.png"); + public final static Image HOME = CmsImages.createIcon("person-logged-in.png"); + public final static Image SORT = CmsImages.createIcon("sort.gif"); + public final static Image REMOVE = CmsImages.createIcon("remove.gif"); + + public final static Image REPOSITORIES = CmsImages.createIcon("repositories.gif"); + public final static Image REPOSITORY_DISCONNECTED = CmsImages.createIcon("repository_disconnected.gif"); + public final static Image REPOSITORY_CONNECTED = CmsImages.createIcon("repository_connected.gif"); + public final static Image REMOTE_DISCONNECTED = CmsImages.createIcon("remote_disconnected.gif"); + public final static Image REMOTE_CONNECTED = CmsImages.createIcon("remote_connected.gif"); + public final static Image WORKSPACE_DISCONNECTED = CmsImages.createIcon("workspace_disconnected.png"); + public final static Image WORKSPACE_CONNECTED = CmsImages.createIcon("workspace_connected.png"); + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrTreeContentProvider.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrTreeContentProvider.java new file mode 100644 index 0000000..cc8479f --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrTreeContentProvider.java @@ -0,0 +1,82 @@ +package org.argeo.cms.ui.jcr; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.RepositoryException; + +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.eclipse.ui.jcr.util.JcrItemsComparator; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.Viewer; + +/** + * Implementation of the {@code ITreeContentProvider} in order to display a + * single JCR node and its children in a tree like structure + */ +public class JcrTreeContentProvider implements ITreeContentProvider { + private static final long serialVersionUID = -2128326504754297297L; + // private Node rootNode; + private JcrItemsComparator itemComparator = new JcrItemsComparator(); + + /** + * Sends back the first level of the Tree. input element must be a single node + * object + */ + public Object[] getElements(Object inputElement) { + Node rootNode = (Node) inputElement; + return childrenNodes(rootNode); + } + + public Object[] getChildren(Object parentElement) { + return childrenNodes((Node) parentElement); + } + + public Object getParent(Object element) { + try { + Node node = (Node) element; + if (!node.getPath().equals("/")) + return node.getParent(); + else + return null; + } catch (RepositoryException e) { + return null; + } + } + + public boolean hasChildren(Object element) { + try { + return ((Node) element).hasNodes(); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot check children existence on " + element, e); + } + } + + protected Object[] childrenNodes(Node parentNode) { + try { + List children = new ArrayList(); + NodeIterator nit = parentNode.getNodes(); + while (nit.hasNext()) { + Node node = nit.nextNode(); +// if (node.getName().startsWith("rep:") || node.getName().startsWith("jcr:") +// || node.getName().startsWith("nt:")) +// continue nodes; + children.add(node); + } + Node[] arr = children.toArray(new Node[0]); + Arrays.sort(arr, itemComparator); + return arr; + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot list children of " + parentNode, e); + } + } + + public void dispose() { + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/NodeContentProvider.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/NodeContentProvider.java new file mode 100644 index 0000000..0625cc8 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/NodeContentProvider.java @@ -0,0 +1,175 @@ +package org.argeo.cms.ui.jcr; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.RepositoryFactory; +import javax.jcr.Session; +import javax.jcr.nodetype.NodeType; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.cms.jcr.CmsJcrUtils; +import org.argeo.cms.security.Keyring; +import org.argeo.cms.ui.jcr.model.RepositoriesElem; +import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; +import org.argeo.cms.ux.widgets.TreeParent; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.Viewer; + +/** + * Implementation of the {@code ITreeContentProvider} to display multiple + * repository environment in a tree like structure + */ +public class NodeContentProvider implements ITreeContentProvider { + private static final long serialVersionUID = -4083809398848374403L; + final private RepositoryRegister repositoryRegister; + final private RepositoryFactory repositoryFactory; + + // Current user session on the default workspace of the argeo Node + final private Session userSession; + final private Keyring keyring; + private boolean sortChildren; + + // Reference for cleaning + private SingleJcrNodeElem homeNode = null; + private RepositoriesElem repositoriesNode = null; + + // Utils + private TreeBrowserComparator itemComparator = new TreeBrowserComparator(); + + public NodeContentProvider(Session userSession, Keyring keyring, + RepositoryRegister repositoryRegister, + RepositoryFactory repositoryFactory, Boolean sortChildren) { + this.userSession = userSession; + this.keyring = keyring; + this.repositoryRegister = repositoryRegister; + this.repositoryFactory = repositoryFactory; + this.sortChildren = sortChildren; + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + if (newInput == null)// dispose + return; + + if (userSession != null) { + Node userHome = CmsJcrUtils.getUserHome(userSession); + if (userHome != null) { + // TODO : find a way to dynamically get alias for the node + if (homeNode != null) + homeNode.dispose(); + homeNode = new SingleJcrNodeElem(null, userHome, + userSession.getUserID(), CmsConstants.EGO_REPOSITORY); + } + } + if (repositoryRegister != null) { + if (repositoriesNode != null) + repositoriesNode.dispose(); + repositoriesNode = new RepositoriesElem("Repositories", + repositoryRegister, repositoryFactory, null, userSession, + keyring); + } + } + + /** + * Sends back the first level of the Tree. Independent from inputElement + * that can be null + */ + public Object[] getElements(Object inputElement) { + List objs = new ArrayList(); + if (homeNode != null) + objs.add(homeNode); + if (repositoriesNode != null) + objs.add(repositoriesNode); + return objs.toArray(); + } + + public Object[] getChildren(Object parentElement) { + if (parentElement instanceof TreeParent) { + if (sortChildren) { + Object[] tmpArr = ((TreeParent) parentElement).getChildren(); + if (tmpArr == null) + return new Object[0]; + TreeParent[] arr = new TreeParent[tmpArr.length]; + for (int i = 0; i < tmpArr.length; i++) + arr[i] = (TreeParent) tmpArr[i]; + Arrays.sort(arr, itemComparator); + return arr; + } else + return ((TreeParent) parentElement).getChildren(); + } else + return new Object[0]; + } + + /** + * Sets whether the content provider should order the children nodes or not. + * It is user duty to call a full refresh of the tree after changing this + * parameter. + */ + public void setSortChildren(boolean sortChildren) { + this.sortChildren = sortChildren; + } + + public Object getParent(Object element) { + if (element instanceof TreeParent) { + return ((TreeParent) element).getParent(); + } else + return null; + } + + public boolean hasChildren(Object element) { + if (element instanceof RepositoriesElem) { + RepositoryRegister rr = ((RepositoriesElem) element) + .getRepositoryRegister(); + return rr.getRepositories().size() > 0; + } else if (element instanceof TreeParent) { + TreeParent tp = (TreeParent) element; + return tp.hasChildren(); + } + return false; + } + + public void dispose() { + if (homeNode != null) + homeNode.dispose(); + if (repositoriesNode != null) { + // logs out open sessions + // see https://bugzilla.argeo.org/show_bug.cgi?id=23 + repositoriesNode.dispose(); + } + } + + /** + * Specific comparator for this view. See specification here: + * https://www.argeo.org/bugzilla/show_bug.cgi?id=139 + */ + private class TreeBrowserComparator implements Comparator { + + public int category(TreeParent element) { + if (element instanceof SingleJcrNodeElem) { + Node node = ((SingleJcrNodeElem) element).getNode(); + try { + if (node.isNodeType(NodeType.NT_FOLDER)) + return 5; + } catch (RepositoryException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + return 10; + } + + public int compare(TreeParent o1, TreeParent o2) { + int cat1 = category(o1); + int cat2 = category(o2); + + if (cat1 != cat2) { + return cat1 - cat2; + } + return o1.getName().compareTo(o2.getName()); + } + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/NodeLabelProvider.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/NodeLabelProvider.java new file mode 100644 index 0000000..a5751c0 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/NodeLabelProvider.java @@ -0,0 +1,113 @@ +package org.argeo.cms.ui.jcr; + +import javax.jcr.NamespaceException; +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.RepositoryException; +import javax.jcr.nodetype.NodeType; + +import org.argeo.api.cms.CmsLog; +import org.argeo.cms.ui.jcr.model.RemoteRepositoryElem; +import org.argeo.cms.ui.jcr.model.RepositoriesElem; +import org.argeo.cms.ui.jcr.model.RepositoryElem; +import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; +import org.argeo.cms.ui.jcr.model.WorkspaceElem; +import org.argeo.eclipse.ui.EclipseUiException; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.swt.graphics.Image; + +/** Provides reasonable defaults for know JCR types. */ +public class NodeLabelProvider extends ColumnLabelProvider { + private static final long serialVersionUID = -3662051696443321843L; + + private final static CmsLog log = CmsLog.getLog(NodeLabelProvider.class); + + public String getText(Object element) { + try { + if (element instanceof SingleJcrNodeElem) { + SingleJcrNodeElem sjn = (SingleJcrNodeElem) element; + return getText(sjn.getNode()); + } else if (element instanceof Node) { + return getText((Node) element); + } else + return super.getText(element); + } catch (RepositoryException e) { + throw new EclipseUiException("Unexpected JCR error while getting node name."); + } + } + + protected String getText(Node node) throws RepositoryException { + String label = node.getName(); + StringBuffer mixins = new StringBuffer(""); + for (NodeType type : node.getMixinNodeTypes()) + mixins.append(' ').append(type.getName()); + + return label + " [" + node.getPrimaryNodeType().getName() + mixins + "]"; + } + + @Override + public Image getImage(Object element) { + if (element instanceof RemoteRepositoryElem) { + if (((RemoteRepositoryElem) element).isConnected()) + return JcrImages.REMOTE_CONNECTED; + else + return JcrImages.REMOTE_DISCONNECTED; + } else if (element instanceof RepositoryElem) { + if (((RepositoryElem) element).isConnected()) + return JcrImages.REPOSITORY_CONNECTED; + else + return JcrImages.REPOSITORY_DISCONNECTED; + } else if (element instanceof WorkspaceElem) { + if (((WorkspaceElem) element).isConnected()) + return JcrImages.WORKSPACE_CONNECTED; + else + return JcrImages.WORKSPACE_DISCONNECTED; + } else if (element instanceof RepositoriesElem) { + return JcrImages.REPOSITORIES; + } else if (element instanceof SingleJcrNodeElem) { + Node nodeElem = ((SingleJcrNodeElem) element).getNode(); + return getImage(nodeElem); + + // if (element instanceof Node) { + // return getImage((Node) element); + // } else if (element instanceof WrappedNode) { + // return getImage(((WrappedNode) element).getNode()); + // } else if (element instanceof NodesWrapper) { + // return getImage(((NodesWrapper) element).getNode()); + // } + } + // try { + // return super.getImage(); + // } catch (RepositoryException e) { + // return null; + // } + return super.getImage(element); + } + + protected Image getImage(Node node) { + try { + if (node.getPrimaryNodeType().isNodeType(NodeType.NT_FILE)) + return JcrImages.FILE; + else if (node.getPrimaryNodeType().isNodeType(NodeType.NT_FOLDER)) + return JcrImages.FOLDER; + else if (node.getPrimaryNodeType().isNodeType(NodeType.NT_RESOURCE)) + return JcrImages.BINARY; + try { + // TODO check workspace type? + if (node.getDepth() == 1 && node.hasProperty(Property.JCR_ID)) + return JcrImages.HOME; + + // optimizes +// if (node.hasProperty(LdapAttrs.uid.property()) && node.isNodeType(NodeTypes.NODE_USER_HOME)) +// return JcrImages.HOME; + } catch (NamespaceException e) { + // node namespace is not registered in this repo + } + return JcrImages.NODE; + } catch (RepositoryException e) { + log.warn("Error while retrieving type for " + node + " in order to display corresponding image"); + e.printStackTrace(); + return null; + } + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/OsgiRepositoryRegister.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/OsgiRepositoryRegister.java new file mode 100644 index 0000000..444350a --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/OsgiRepositoryRegister.java @@ -0,0 +1,52 @@ +package org.argeo.cms.ui.jcr; + +import java.util.HashMap; +import java.util.Map; + +import javax.jcr.Repository; + +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; + +public class OsgiRepositoryRegister extends DefaultRepositoryRegister { + private final static BundleContext bc = FrameworkUtil.getBundle(OsgiRepositoryRegister.class).getBundleContext(); + private final ServiceTracker repositoryTracker; + + public OsgiRepositoryRegister() { + repositoryTracker = new ServiceTracker(bc, Repository.class, null) { + + @Override + public Repository addingService(ServiceReference reference) { + + Repository repository = super.addingService(reference); + Map props = new HashMap<>(); + for (String key : reference.getPropertyKeys()) { + props.put(key, reference.getProperty(key)); + } + register(repository, props); + return repository; + } + + @Override + public void removedService(ServiceReference reference, Repository service) { + Map props = new HashMap<>(); + for (String key : reference.getPropertyKeys()) { + props.put(key, reference.getProperty(key)); + } + unregister(service, props); + super.removedService(reference, service); + } + + }; + } + + public void init() { + repositoryTracker.open(); + } + + public void destroy() { + repositoryTracker.close(); + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/PropertiesContentProvider.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/PropertiesContentProvider.java new file mode 100644 index 0000000..fd544bb --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/PropertiesContentProvider.java @@ -0,0 +1,42 @@ +package org.argeo.cms.ui.jcr; + +import java.util.Set; +import java.util.TreeSet; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.PropertyIterator; +import javax.jcr.RepositoryException; + +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.eclipse.ui.jcr.util.JcrItemsComparator; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.Viewer; + +/** Simple content provider that displays all properties of a given Node */ +public class PropertiesContentProvider implements IStructuredContentProvider { + private static final long serialVersionUID = 5227554668841613078L; + private JcrItemsComparator itemComparator = new JcrItemsComparator(); + + public void dispose() { + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + + public Object[] getElements(Object inputElement) { + try { + if (inputElement instanceof Node) { + Set props = new TreeSet(itemComparator); + PropertyIterator pit = ((Node) inputElement).getProperties(); + while (pit.hasNext()) + props.add(pit.nextProperty()); + return props.toArray(); + } + return new Object[] {}; + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot get element for " + + inputElement, e); + } + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/PropertyLabelProvider.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/PropertyLabelProvider.java new file mode 100644 index 0000000..37b90f7 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/PropertyLabelProvider.java @@ -0,0 +1,101 @@ +package org.argeo.cms.ui.jcr; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; + +import javax.jcr.Property; +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; +import javax.jcr.Value; + +import org.argeo.cms.ui.CmsUiConstants; +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.jcr.JcrUtils; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.jface.viewers.ViewerCell; + +/** Default basic label provider for a given JCR Node's properties */ +public class PropertyLabelProvider extends ColumnLabelProvider { + private static final long serialVersionUID = -5405794508731390147L; + + // To be able to change column order easily + public static final int COLUMN_PROPERTY = 0; + public static final int COLUMN_VALUE = 1; + public static final int COLUMN_TYPE = 2; + public static final int COLUMN_ATTRIBUTES = 3; + + // Utils + protected DateFormat timeFormatter = new SimpleDateFormat(CmsUiConstants.DATE_TIME_FORMAT); + + public void update(ViewerCell cell) { + Object element = cell.getElement(); + cell.setText(getColumnText(element, cell.getColumnIndex())); + } + + public String getColumnText(Object element, int columnIndex) { + try { + if (element instanceof Property) { + Property prop = (Property) element; + if (prop.isMultiple()) { + switch (columnIndex) { + case COLUMN_PROPERTY: + return prop.getName(); + case COLUMN_VALUE: + // Corresponding values are listed on children + return ""; + case COLUMN_TYPE: + return JcrBrowserUtils.getPropertyTypeAsString(prop); + case COLUMN_ATTRIBUTES: + return JcrUtils.getPropertyDefinitionAsString(prop); + } + } else { + switch (columnIndex) { + case COLUMN_PROPERTY: + return prop.getName(); + case COLUMN_VALUE: + return formatValueAsString(prop.getValue()); + case COLUMN_TYPE: + return JcrBrowserUtils.getPropertyTypeAsString(prop); + case COLUMN_ATTRIBUTES: + return JcrUtils.getPropertyDefinitionAsString(prop); + } + } + } else if (element instanceof Value) { + Value val = (Value) element; + switch (columnIndex) { + case COLUMN_PROPERTY: + // Nothing to show + return ""; + case COLUMN_VALUE: + return formatValueAsString(val); + case COLUMN_TYPE: + // listed on the parent + return ""; + case COLUMN_ATTRIBUTES: + // Corresponding attributes are listed on the parent + return ""; + } + } + } catch (RepositoryException re) { + throw new EclipseUiException("Cannot retrieve prop value on " + element, re); + } + return null; + } + + private String formatValueAsString(Value value) { + // TODO enhance this method + try { + String strValue; + + if (value.getType() == PropertyType.BINARY) + strValue = ""; + else if (value.getType() == PropertyType.DATE) + strValue = timeFormatter.format(value.getDate().getTime()); + else + strValue = value.getString(); + return strValue; + } catch (RepositoryException e) { + throw new EclipseUiException("unexpected error while formatting value", e); + } + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/RepositoryRegister.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/RepositoryRegister.java new file mode 100644 index 0000000..802c756 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/RepositoryRegister.java @@ -0,0 +1,16 @@ +package org.argeo.cms.ui.jcr; + +import java.util.Map; + +import javax.jcr.Repository; +import javax.jcr.RepositoryFactory; + +/** Allows to register repositories by name. */ +public interface RepositoryRegister extends RepositoryFactory { + /** + * The registered {@link Repository} as a read-only map. Note that this + * method should be called for each access in order to be sure to be up to + * date in case repositories have registered/unregistered + */ + public Map getRepositories(); +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/VersionLabelProvider.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/VersionLabelProvider.java new file mode 100644 index 0000000..37dfe2b --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/VersionLabelProvider.java @@ -0,0 +1,33 @@ +package org.argeo.cms.ui.jcr; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.version.Version; + +import org.argeo.eclipse.ui.EclipseUiException; +import org.eclipse.jface.viewers.ColumnLabelProvider; + +/** + * Simple wrapping of the ColumnLabelProvider class to provide text display in + * order to build a tree for version. The getText() method does not assume that + * {@link Version} extends {@link Node} class to respect JCR 2.0 specification + * + */ +public class VersionLabelProvider extends ColumnLabelProvider { + private static final long serialVersionUID = 5270739851193688238L; + + public String getText(Object element) { + try { + if (element instanceof Version) { + Version version = (Version) element; + return version.getName(); + } else if (element instanceof Node) { + return ((Node) element).getName(); + } + } catch (RepositoryException re) { + throw new EclipseUiException( + "Unexpected error while getting element name", re); + } + return super.getText(element); + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/MaintainedRepositoryElem.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/MaintainedRepositoryElem.java new file mode 100644 index 0000000..d33b33f --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/MaintainedRepositoryElem.java @@ -0,0 +1,21 @@ +package org.argeo.cms.ui.jcr.model; + +import javax.jcr.Repository; + +import org.argeo.cms.ux.widgets.TreeParent; + +/** Wrap a MaintainedRepository */ +public class MaintainedRepositoryElem extends RepositoryElem { + + public MaintainedRepositoryElem(String alias, Repository repository, TreeParent parent) { + super(alias, repository, parent); + // if (!(repository instanceof MaintainedRepository)) { + // throw new ArgeoException("Repository " + alias + // + " is not a maintained repository"); + // } + } + + // protected MaintainedRepository getMaintainedRepository() { + // return (MaintainedRepository) getRepository(); + // } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/RemoteRepositoryElem.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/RemoteRepositoryElem.java new file mode 100644 index 0000000..908d1b1 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/RemoteRepositoryElem.java @@ -0,0 +1,76 @@ +package org.argeo.cms.ui.jcr.model; + +import java.util.Arrays; + +import javax.jcr.Node; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.RepositoryFactory; +import javax.jcr.Session; +import javax.jcr.SimpleCredentials; + +import org.argeo.cms.ArgeoNames; +import org.argeo.cms.jcr.CmsJcrUtils; +import org.argeo.cms.security.Keyring; +import org.argeo.cms.ux.widgets.TreeParent; +import org.argeo.eclipse.ui.EclipseUiException; + +/** Root of a remote repository */ +public class RemoteRepositoryElem extends RepositoryElem { + private final Keyring keyring; + /** + * A session of the logged in user on the default workspace of the node + * repository. + */ + private final Session userSession; + private final String remoteNodePath; + + private final RepositoryFactory repositoryFactory; + private final String uri; + + public RemoteRepositoryElem(String alias, RepositoryFactory repositoryFactory, String uri, TreeParent parent, + Session userSession, Keyring keyring, String remoteNodePath) { + super(alias, null, parent); + this.repositoryFactory = repositoryFactory; + this.uri = uri; + this.keyring = keyring; + this.userSession = userSession; + this.remoteNodePath = remoteNodePath; + } + + @Override + protected Session repositoryLogin(String workspaceName) throws RepositoryException { + Node remoteRepository = userSession.getNode(remoteNodePath); + String userID = remoteRepository.getProperty(ArgeoNames.ARGEO_USER_ID).getString(); + if (userID.trim().equals("")) { + return getRepository().login(workspaceName); + } else { + String pwdPath = remoteRepository.getPath() + '/' + ArgeoNames.ARGEO_PASSWORD; + char[] password = keyring.getAsChars(pwdPath); + try { + SimpleCredentials credentials = new SimpleCredentials(userID, password); + return getRepository().login(credentials, workspaceName); + } finally { + Arrays.fill(password, 0, password.length, ' '); + } + } + } + + @Override + public Repository getRepository() { + if (repository == null) + repository = CmsJcrUtils.getRepositoryByUri(repositoryFactory, uri); + return super.getRepository(); + } + + public void remove() { + try { + Node remoteNode = userSession.getNode(remoteNodePath); + remoteNode.remove(); + remoteNode.getSession().save(); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot remove " + remoteNodePath, e); + } + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/RepositoriesElem.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/RepositoriesElem.java new file mode 100644 index 0000000..8c40f8b --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/RepositoriesElem.java @@ -0,0 +1,112 @@ +package org.argeo.cms.ui.jcr.model; + +import java.util.Map; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.RepositoryFactory; +import javax.jcr.Session; + +import org.argeo.cms.ArgeoNames; +import org.argeo.cms.jcr.CmsJcrUtils; +import org.argeo.cms.security.Keyring; +import org.argeo.cms.ui.jcr.RepositoryRegister; +import org.argeo.cms.ux.widgets.TreeParent; +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.eclipse.ui.dialogs.ErrorFeedback; + +/** + * UI Tree component that implements the Argeo abstraction of a + * {@link RepositoryFactory} that enable a user to "mount" various repositories + * in a single Tree like View. It is usually meant to be at the root of the UI + * Tree and thus {@link #getParent()} method will return null. + * + * The {@link RepositoryFactory} is injected at instantiation time and must be + * use get or register new {@link Repository} objects upon which a reference is + * kept here. + */ + +public class RepositoriesElem extends TreeParent implements ArgeoNames { + private final RepositoryRegister repositoryRegister; + private final RepositoryFactory repositoryFactory; + + /** + * A session of the logged in user on the default workspace of the node + * repository. + */ + private final Session userSession; + private final Keyring keyring; + + public RepositoriesElem(String name, RepositoryRegister repositoryRegister, RepositoryFactory repositoryFactory, + TreeParent parent, Session userSession, Keyring keyring) { + super(name); + this.repositoryRegister = repositoryRegister; + this.repositoryFactory = repositoryFactory; + this.userSession = userSession; + this.keyring = keyring; + } + + /** + * Override normal behavior to initialize the various repositories only at + * request time + */ + @Override + public synchronized Object[] getChildren() { + if (isLoaded()) { + return super.getChildren(); + } else { + // initialize current object + Map refRepos = repositoryRegister.getRepositories(); + for (String name : refRepos.keySet()) { + Repository repository = refRepos.get(name); + // if (repository instanceof MaintainedRepository) + // super.addChild(new MaintainedRepositoryElem(name, + // repository, this)); + // else + super.addChild(new RepositoryElem(name, repository, this)); + } + + // remote + if (keyring != null) { + try { + addRemoteRepositories(keyring); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot browse remote repositories", e); + } + } + return super.getChildren(); + } + } + + protected void addRemoteRepositories(Keyring jcrKeyring) throws RepositoryException { + Node userHome = CmsJcrUtils.getUserHome(userSession); + if (userHome != null && userHome.hasNode(ARGEO_REMOTE)) { + NodeIterator it = userHome.getNode(ARGEO_REMOTE).getNodes(); + while (it.hasNext()) { + Node remoteNode = it.nextNode(); + String uri = remoteNode.getProperty(ARGEO_URI).getString(); + try { + RemoteRepositoryElem remoteRepositoryNode = new RemoteRepositoryElem(remoteNode.getName(), + repositoryFactory, uri, this, userSession, jcrKeyring, remoteNode.getPath()); + super.addChild(remoteRepositoryNode); + } catch (Exception e) { + ErrorFeedback.show("Cannot add remote repository " + remoteNode, e); + } + } + } + } + + public void registerNewRepository(String alias, Repository repository) { + // TODO: implement this + // Create a new RepositoryNode Object + // add it + // super.addChild(new RepositoriesNode(...)); + } + + /** Returns the {@link RepositoryRegister} wrapped by this object. */ + public RepositoryRegister getRepositoryRegister() { + return repositoryRegister; + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/RepositoryElem.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/RepositoryElem.java new file mode 100644 index 0000000..296c369 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/RepositoryElem.java @@ -0,0 +1,98 @@ +package org.argeo.cms.ui.jcr.model; + +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.cms.ux.widgets.TreeParent; +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.jcr.JcrUtils; + +/** + * UI Tree component that wraps a JCR {@link Repository}. It also keeps a + * reference to its parent Tree Ui component; typically the unique + * {@link RepositoriesElem} object of the current view to enable bi-directionnal + * browsing in the tree. + */ + +public class RepositoryElem extends TreeParent { + private String alias; + protected Repository repository; + private Session defaultSession = null; + + /** Create a new repository with distinct name and alias */ + public RepositoryElem(String alias, Repository repository, TreeParent parent) { + super(alias); + this.repository = repository; + setParent(parent); + this.alias = alias; + } + + public void login() { + try { + defaultSession = repositoryLogin(CmsConstants.SYS_WORKSPACE); + String[] wkpNames = defaultSession.getWorkspace().getAccessibleWorkspaceNames(); + for (String wkpName : wkpNames) { + if (wkpName.equals(defaultSession.getWorkspace().getName())) + addChild(new WorkspaceElem(this, wkpName, defaultSession)); + else + addChild(new WorkspaceElem(this, wkpName)); + } + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot connect to repository " + alias, e); + } + } + + public synchronized void logout() { + for (Object child : getChildren()) { + if (child instanceof WorkspaceElem) + ((WorkspaceElem) child).logout(); + } + clearChildren(); + JcrUtils.logoutQuietly(defaultSession); + defaultSession = null; + } + + /** + * Actual call to the {@link Repository#login(javax.jcr.Credentials, String)} + * method. To be overridden. + */ + protected Session repositoryLogin(String workspaceName) throws RepositoryException { + return repository.login(workspaceName); + } + + public String[] getAccessibleWorkspaceNames() { + try { + return defaultSession.getWorkspace().getAccessibleWorkspaceNames(); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot retrieve workspace names", e); + } + } + + public void createWorkspace(String workspaceName) { + if (!isConnected()) + login(); + try { + defaultSession.getWorkspace().createWorkspace(workspaceName); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot create workspace", e); + } + } + + /** returns the {@link Repository} referenced by the current UI Node */ + public Repository getRepository() { + return repository; + } + + public String getAlias() { + return alias; + } + + public Boolean isConnected() { + if (defaultSession != null && defaultSession.isLive()) + return true; + else + return false; + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/SingleJcrNodeElem.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/SingleJcrNodeElem.java new file mode 100644 index 0000000..a2584a5 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/SingleJcrNodeElem.java @@ -0,0 +1,84 @@ +package org.argeo.cms.ui.jcr.model; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.RepositoryException; +import javax.jcr.Workspace; + +import org.argeo.cms.ux.widgets.TreeParent; +import org.argeo.eclipse.ui.EclipseUiException; + +/** + * UI Tree component. Wraps a node of a JCR {@link Workspace}. It also keeps a + * reference to its parent node that can either be a {@link WorkspaceElem}, a + * {@link SingleJcrNodeElem} or null if the node is "mounted" as the root of the + * UI tree. + */ +public class SingleJcrNodeElem extends TreeParent { + + private final Node node; + private String alias = null; + + /** Creates a new UiNode in the UI Tree */ + public SingleJcrNodeElem(TreeParent parent, Node node, String name) { + super(name); + setParent(parent); + this.node = node; + } + + /** + * Creates a new UiNode in the UI Tree, keeping a reference to the alias of + * the corresponding repository in the current UI environment. It is useful + * to be able to mount nodes as roots of the UI tree. + */ + public SingleJcrNodeElem(TreeParent parent, Node node, String name, String alias) { + super(name); + setParent(parent); + this.node = node; + this.alias = alias; + } + + /** Returns the node wrapped by the current UI object */ + public Node getNode() { + return node; + } + + protected String getRepositoryAlias() { + return alias; + } + + /** + * Overrides normal behaviour to initialise children only when first + * requested + */ + @Override + public synchronized Object[] getChildren() { + if (isLoaded()) { + return super.getChildren(); + } else { + // initialize current object + try { + NodeIterator ni = node.getNodes(); + while (ni.hasNext()) { + Node curNode = ni.nextNode(); + addChild(new SingleJcrNodeElem(this, curNode, curNode.getName())); + } + return super.getChildren(); + } catch (RepositoryException re) { + throw new EclipseUiException("Cannot initialize SingleJcrNode children", re); + } + } + } + + @Override + public boolean hasChildren() { + try { + if (node.getSession().isLive()) + return node.hasNodes(); + else + return false; + } catch (RepositoryException re) { + throw new EclipseUiException("Cannot check children node existence", re); + } + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/WorkspaceElem.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/WorkspaceElem.java new file mode 100644 index 0000000..2d78666 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/WorkspaceElem.java @@ -0,0 +1,117 @@ +package org.argeo.cms.ui.jcr.model; + +import javax.jcr.AccessDeniedException; +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +// import javax.jcr.Workspace; +import javax.jcr.Workspace; + +import org.argeo.cms.ux.widgets.TreeParent; +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.jcr.JcrUtils; + +/** + * UI Tree component. Wraps the root node of a JCR {@link Workspace}. It also + * keeps a reference to its parent {@link RepositoryElem}, to be able to + * retrieve alias of the current used repository + */ +public class WorkspaceElem extends TreeParent { + private Session session = null; + + public WorkspaceElem(RepositoryElem parent, String name) { + this(parent, name, null); + } + + public WorkspaceElem(RepositoryElem parent, String name, Session session) { + super(name); + this.session = session; + setParent(parent); + } + + public synchronized Session getSession() { + return session; + } + + public synchronized Node getRootNode() { + try { + if (session != null) + return session.getRootNode(); + else + return null; + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot get root node of workspace " + getName(), e); + } + } + + public synchronized void login() { + try { + session = ((RepositoryElem) getParent()).repositoryLogin(getName()); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot connect to repository " + getName(), e); + } + } + + public Boolean isConnected() { + if (session != null && session.isLive()) + return true; + else + return false; + } + + @Override + public synchronized void dispose() { + logout(); + super.dispose(); + } + + /** Logouts the session, does not nothing if there is no live session. */ + public synchronized void logout() { + clearChildren(); + JcrUtils.logoutQuietly(session); + session = null; + } + + @Override + public synchronized boolean hasChildren() { + try { + if (isConnected()) + try { + return session.getRootNode().hasNodes(); + } catch (AccessDeniedException e) { + // current user may not have access to the root node + return false; + } + else + return false; + } catch (RepositoryException re) { + throw new EclipseUiException("Unexpected error while checking children node existence", re); + } + } + + /** Override normal behaviour to initialize display of the workspace */ + @Override + public synchronized Object[] getChildren() { + if (isLoaded()) { + return super.getChildren(); + } else { + // initialize current object + try { + Node rootNode; + if (session == null) + return null; + else + rootNode = session.getRootNode(); + NodeIterator ni = rootNode.getNodes(); + while (ni.hasNext()) { + Node node = ni.nextNode(); + addChild(new SingleJcrNodeElem(this, node, node.getName())); + } + return super.getChildren(); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot initialize WorkspaceNode UI object." + getName(), e); + } + } + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/package-info.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/package-info.java new file mode 100644 index 0000000..8f54744 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/package-info.java @@ -0,0 +1,2 @@ +/** Model for SWT/JFace JCR components. */ +package org.argeo.cms.ui.jcr.model; \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/package-info.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/package-info.java new file mode 100644 index 0000000..26ae330 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/package-info.java @@ -0,0 +1,2 @@ +/** SWT/JFace JCR components. */ +package org.argeo.cms.ui.jcr; \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/package-info.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/package-info.java new file mode 100644 index 0000000..82fdee7 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/package-info.java @@ -0,0 +1,2 @@ +/** SWT/JFace components for Argeo CMS. */ +package org.argeo.cms.ui; \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/CmsLink.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/CmsLink.java new file mode 100644 index 0000000..e91f9ba --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/CmsLink.java @@ -0,0 +1,282 @@ +package org.argeo.cms.ui.util; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.api.cms.CmsLog; +import org.argeo.api.cms.ux.CmsStyle; +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.jcr.CmsJcrUtils; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ui.CmsUiProvider; +import org.argeo.jcr.JcrException; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.rap.rwt.service.ResourceManager; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.osgi.framework.BundleContext; + +/** A link to an internal or external location. */ +public class CmsLink implements CmsUiProvider { + private final static CmsLog log = CmsLog.getLog(CmsLink.class); + private BundleContext bundleContext; + + private String label; + private String style; + private String target; + private String image; + private boolean openNew = false; + private MouseListener mouseListener; + + private int horizontalAlignment = SWT.CENTER; + private int verticalAlignment = SWT.CENTER; + + private String loggedInLabel = null; + private String loggedInTarget = null; + + // internal + // private Boolean isUrl = false; + private Integer imageWidth, imageHeight; + + public CmsLink() { + super(); + } + + public CmsLink(String label, String target) { + this(label, target, (String) null); + } + + public CmsLink(String label, String target, CmsStyle style) { + this(label, target, style != null ? style.style() : null); + } + + public CmsLink(String label, String target, String style) { + super(); + this.label = label; + this.target = target; + this.style = style; + init(); + } + + public void init() { + if (image != null) { + ImageData image = loadImage(); + if (imageHeight == null && imageWidth == null) { + imageWidth = image.width; + imageHeight = image.height; + } else if (imageHeight == null) { + imageHeight = (imageWidth * image.height) / image.width; + } else if (imageWidth == null) { + imageWidth = (imageHeight * image.width) / image.height; + } + } + } + + /** @return {@link Composite} with a single {@link Label} child. */ + @Override + public Control createUi(final Composite parent, Node context) { +// if (image != null && (imageWidth == null || imageHeight == null)) { +// throw new CmsException("Image is not properly configured." +// + " Make sure bundleContext property is set and init() method has been called."); +// } + + Composite comp = new Composite(parent, SWT.NONE); + comp.setLayout(CmsSwtUtils.noSpaceGridLayout()); + + Label link = new Label(comp, SWT.NONE); + CmsSwtUtils.markup(link); + GridData layoutData = new GridData(horizontalAlignment, verticalAlignment, false, false); + if (image != null) { + if (imageHeight != null) + layoutData.heightHint = imageHeight; + if (label == null) + if (imageWidth != null) + layoutData.widthHint = imageWidth; + } + + link.setLayoutData(layoutData); + CmsSwtUtils.style(comp, style != null ? style : getDefaultStyle()); + CmsSwtUtils.style(link, style != null ? style : getDefaultStyle()); + + // label + StringBuilder labelText = new StringBuilder(); + if (loggedInTarget != null && isLoggedIn()) { + labelText.append(""); + } else if (target != null) { + labelText.append(""); + } + if (image != null) { + registerImageIfNeeded(); + String imageLocation = RWT.getResourceManager().getLocation(image); + labelText.append(""); + + } + + if (loggedInLabel != null && isLoggedIn()) { + labelText.append(' ').append(loggedInLabel); + } else if (label != null) { + labelText.append(' ').append(label); + } + + if ((loggedInTarget != null && isLoggedIn()) || target != null) + labelText.append(""); + + link.setText(labelText.toString()); + + if (mouseListener != null) + link.addMouseListener(mouseListener); + + return comp; + } + + private void registerImageIfNeeded() { + ResourceManager resourceManager = RWT.getResourceManager(); + if (!resourceManager.isRegistered(image)) { + URL res = getImageUrl(); + try (InputStream inputStream = res.openStream()) { + resourceManager.register(image, inputStream); + if (log.isTraceEnabled()) + log.trace("Registered image " + image); + } catch (IOException e) { + throw new RuntimeException("Cannot load image " + image, e); + } + } + } + + private ImageData loadImage() { + URL url = getImageUrl(); + ImageData result = null; + try (InputStream inputStream = url.openStream()) { + result = new ImageData(inputStream); + if (log.isTraceEnabled()) + log.trace("Loaded image " + image); + } catch (IOException e) { + throw new RuntimeException("Cannot load image " + image, e); + } + return result; + } + + private URL getImageUrl() { + URL url; + try { + // pure URL + url = new URL(image); + } catch (MalformedURLException e1) { + url = bundleContext.getBundle().getResource(image); + } + + if (url == null) + throw new IllegalStateException("No image " + image + " available."); + + return url; + } + + public void setBundleContext(BundleContext bundleContext) { + this.bundleContext = bundleContext; + } + + public void setLabel(String label) { + this.label = label; + } + + public void setStyle(String style) { + this.style = style; + } + + /** @deprecated Use {@link #setStyle(String)} instead. */ + @Deprecated + public void setCustom(String custom) { + this.style = custom; + } + + public void setTarget(String target) { + this.target = target; + // try { + // new URL(target); + // isUrl = true; + // } catch (MalformedURLException e1) { + // isUrl = false; + // } + } + + public void setImage(String image) { + this.image = image; + } + + public void setLoggedInLabel(String loggedInLabel) { + this.loggedInLabel = loggedInLabel; + } + + public void setLoggedInTarget(String loggedInTarget) { + this.loggedInTarget = loggedInTarget; + } + + public void setMouseListener(MouseListener mouseListener) { + this.mouseListener = mouseListener; + } + + public void setvAlign(String vAlign) { + if ("bottom".equals(vAlign)) { + verticalAlignment = SWT.BOTTOM; + } else if ("top".equals(vAlign)) { + verticalAlignment = SWT.TOP; + } else if ("center".equals(vAlign)) { + verticalAlignment = SWT.CENTER; + } else { + throw new IllegalArgumentException( + "Unsupported vertical alignment " + vAlign + " (must be: top, bottom or center)"); + } + } + + protected boolean isLoggedIn() { + return !CurrentUser.isAnonymous(); + } + + public void setImageWidth(Integer imageWidth) { + this.imageWidth = imageWidth; + } + + public void setImageHeight(Integer imageHeight) { + this.imageHeight = imageHeight; + } + + public void setOpenNew(boolean openNew) { + this.openNew = openNew; + } + + protected String getDefaultStyle() { + return SimpleStyle.link.name(); + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/CmsPane.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/CmsPane.java new file mode 100644 index 0000000..fc0c821 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/CmsPane.java @@ -0,0 +1,49 @@ +package org.argeo.cms.ui.util; + +import org.argeo.cms.swt.CmsSwtUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.layout.RowLayout; +import org.eclipse.swt.widgets.Composite; + +/** The main pane of a CMS display, with QA and support areas. */ +public class CmsPane { + + private Composite mainArea; + private Composite qaArea; + private Composite supportArea; + + public CmsPane(Composite parent, int style) { + parent.setLayout(CmsSwtUtils.noSpaceGridLayout()); + +// qaArea = new Composite(parent, SWT.NONE); +// qaArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); +// RowLayout qaLayout = new RowLayout(); +// qaLayout.spacing = 0; +// qaArea.setLayout(qaLayout); + + mainArea = new Composite(parent, SWT.NONE); + mainArea.setLayout(new GridLayout()); + mainArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + +// supportArea = new Composite(parent, SWT.NONE); +// supportArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); +// RowLayout supportLayout = new RowLayout(); +// supportLayout.spacing = 0; +// supportArea.setLayout(supportLayout); + } + + public Composite getMainArea() { + return mainArea; + } + + public Composite getQaArea() { + return qaArea; + } + + public Composite getSupportArea() { + return supportArea; + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/CmsUiUtils.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/CmsUiUtils.java new file mode 100644 index 0000000..3522f1b --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/CmsUiUtils.java @@ -0,0 +1,192 @@ +package org.argeo.cms.ui.util; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.servlet.http.HttpServletRequest; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.api.cms.ux.Cms2DSize; +import org.argeo.api.cms.ux.CmsView; +import org.argeo.cms.jcr.CmsJcrUtils; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ui.CmsUiConstants; +import org.argeo.cms.ux.AbstractImageManager; +import org.argeo.cms.ux.CmsUxUtils; +import org.argeo.jcr.JcrUtils; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.rap.rwt.service.ResourceManager; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.layout.RowData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Table; + +/** Static utilities for the CMS framework. */ +public class CmsUiUtils { + // private final static Log log = LogFactory.getLog(CmsUiUtils.class); + + /* + * CMS VIEW + */ + + /** + * The CMS view related to this display, or null if none is available from this + * call. + * + * @deprecated Use {@link CmsSwtUtils#getCmsView(Composite)} instead. + */ + @Deprecated + public static CmsView getCmsView() { +// return UiContext.getData(CmsView.class.getName()); + return CmsSwtUtils.getCmsView(Display.getCurrent().getActiveShell()); + } + + public static StringBuilder getServerBaseUrl(HttpServletRequest request) { + try { + URL url = new URL(request.getRequestURL().toString()); + StringBuilder buf = new StringBuilder(); + buf.append(url.getProtocol()).append("://").append(url.getHost()); + if (url.getPort() != -1) + buf.append(':').append(url.getPort()); + return buf; + } catch (MalformedURLException e) { + throw new IllegalArgumentException("Cannot extract server base URL from " + request.getRequestURL(), e); + } + } + + // + public static String getDataUrl(Node node, HttpServletRequest request) { + try { + StringBuilder buf = getServerBaseUrl(request); + buf.append(getDataPath(node)); + return new URL(buf.toString()).toString(); + } catch (MalformedURLException e) { + throw new IllegalArgumentException("Cannot build data URL for " + node, e); + } + } + + /** A path in the node repository */ + public static String getDataPath(Node node) { + return getDataPath(CmsConstants.EGO_REPOSITORY, node); + } + + public static String getDataPath(String cn, Node node) { + return CmsJcrUtils.getDataPath(cn, node); + } + + /** Clean reserved URL characters for use in HTTP links. */ + public static String getDataPathForUrl(Node node) { + return CmsSwtUtils.cleanPathForUrl(getDataPath(node)); + } + + /** @deprecated Use rowData16px() instead. GridData should not be reused. */ + @Deprecated + public static RowData ROW_DATA_16px = new RowData(16, 16); + + + + /* + * FORM LAYOUT + */ + + + + @Deprecated + public static void setItemHeight(Table table, int height) { + table.setData(CmsUiConstants.ITEM_HEIGHT, height); + } + + // + // JCR + // + public static Node getOrAddEmptyFile(Node parent, Enum child) throws RepositoryException { + if (has(parent, child)) + return child(parent, child); + return JcrUtils.copyBytesAsFile(parent, child.name(), new byte[0]); + } + + public static Node child(Node parent, Enum en) throws RepositoryException { + return parent.getNode(en.name()); + } + + public static Boolean has(Node parent, Enum en) throws RepositoryException { + return parent.hasNode(en.name()); + } + + public static Node getOrAdd(Node parent, Enum en) throws RepositoryException { + return getOrAdd(parent, en, null); + } + + public static Node getOrAdd(Node parent, Enum en, String primaryType) throws RepositoryException { + if (has(parent, en)) + return child(parent, en); + else if (primaryType == null) + return parent.addNode(en.name()); + else + return parent.addNode(en.name(), primaryType); + } + + // IMAGES + + public static String img(Node fileNode, String width, String height) { + return img(null, fileNode, width, height); + } + + public static String img(String serverBase, Node fileNode, String width, String height) { +// String src = (serverBase != null ? serverBase : "") + NodeUtils.getDataPath(fileNode); + String src; + src = (serverBase != null ? serverBase : "") + getDataPathForUrl(fileNode); + return CmsUxUtils.imgBuilder(src, width, height).append("/>").toString(); + } + + public static String noImg(Cms2DSize size) { + ResourceManager rm = RWT.getResourceManager(); + return CmsUxUtils.img(rm.getLocation(AbstractImageManager.NO_IMAGE), size); + } + + public static String noImg() { + return noImg(AbstractImageManager.NO_IMAGE_SIZE); + } + +// public static Image noImage(Cms2DSize size) { +// ResourceManager rm = RWT.getResourceManager(); +// InputStream in = null; +// try { +// in = rm.getRegisteredContent(AbstractImageManager.NO_IMAGE); +// ImageData id = new ImageData(in); +// ImageData scaled = id.scaledTo(size.getWidth(), size.getHeight()); +// Image image = new Image(Display.getCurrent(), scaled); +// return image; +// } finally { +// try { +// in.close(); +// } catch (IOException e) { +// // silent +// } +// } +// } + + /** Lorem ipsum text to be used during development. */ + public final static String LOREM_IPSUM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit." + + " Etiam eleifend hendrerit sem, ac ultricies massa ornare ac." + + " Cras aliquam sodales risus, vitae varius lacus molestie quis." + + " Vivamus consequat, leo id lacinia volutpat, eros diam efficitur urna, finibus interdum risus turpis at nisi." + + " Curabitur vulputate nulla quis scelerisque fringilla. Integer consectetur turpis id lobortis accumsan." + + " Pellentesque commodo turpis ac diam ultricies dignissim." + + " Curabitur sit amet dolor volutpat lacus aliquam ornare quis sed velit." + + " Integer varius quis est et tristique." + + " Suspendisse pharetra porttitor purus, eget condimentum magna." + + " Duis vitae turpis eros. Sed tincidunt lacinia rutrum." + + " Aliquam velit velit, rutrum ut augue sed, condimentum lacinia augue."; + + /** Singleton. */ + private CmsUiUtils() { + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/DefaultImageManager.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/DefaultImageManager.java new file mode 100644 index 0000000..b431fc3 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/DefaultImageManager.java @@ -0,0 +1,133 @@ +package org.argeo.cms.ui.util; + +import static javax.jcr.Node.JCR_CONTENT; +import static javax.jcr.Property.JCR_DATA; +import static javax.jcr.nodetype.NodeType.NT_FILE; +import static javax.jcr.nodetype.NodeType.NT_RESOURCE; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; + +import javax.jcr.Binary; +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.RepositoryException; + +import org.apache.commons.io.IOUtils; +import org.argeo.api.cms.CmsLog; +import org.argeo.api.cms.ux.Cms2DSize; +import org.argeo.cms.swt.AbstractSwtImageManager; +import org.argeo.jcr.JcrException; +import org.argeo.jcr.JcrUtils; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.rap.rwt.service.ResourceManager; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.widgets.Display; + +/** Manages only public images so far. */ +public class DefaultImageManager extends AbstractSwtImageManager { + private final static CmsLog log = CmsLog.getLog(DefaultImageManager.class); + + /** @return null if not available */ + @Override + public String getImageUrl(Node node) { + return CmsUiUtils.getDataPathForUrl(node); + } + + protected String getResourceName(Node node) { + try { + String workspace = node.getSession().getWorkspace().getName(); + if (node.hasNode(JCR_CONTENT)) + return workspace + '_' + node.getNode(JCR_CONTENT).getIdentifier(); + else + return workspace + '_' + node.getIdentifier(); + } catch (RepositoryException e) { + throw new JcrException(e); + } + } + + public Binary getImageBinary(Node node) { + try { + if (node.isNodeType(NT_FILE)) { + return node.getNode(JCR_CONTENT).getProperty(JCR_DATA).getBinary(); + } else { + return null; + } + } catch (RepositoryException e) { + throw new JcrException(e); + } + } + + public Image getSwtImage(Node node) { + InputStream inputStream = null; + Binary binary = getImageBinary(node); + if (binary == null) + return null; + try { + inputStream = binary.getStream(); + return new Image(Display.getCurrent(), inputStream); + } catch (RepositoryException e) { + throw new JcrException(e); + } finally { + IOUtils.closeQuietly(inputStream); + JcrUtils.closeQuietly(binary); + } + } + + @Override + public String uploadImage(Node context, Node parentNode, String fileName, InputStream in, String contentType) { + InputStream inputStream = null; + try { + String previousResourceName = null; + if (parentNode.hasNode(fileName)) { + Node node = parentNode.getNode(fileName); + previousResourceName = getResourceName(node); + if (node.hasNode(JCR_CONTENT)) { + node.getNode(JCR_CONTENT).remove(); + node.addNode(JCR_CONTENT, NT_RESOURCE); + } + } + + byte[] arr = IOUtils.toByteArray(in); + Node fileNode = JcrUtils.copyBytesAsFile(parentNode, fileName, arr); + inputStream = new ByteArrayInputStream(arr); + ImageData id = new ImageData(inputStream); + processNewImageFile(context, fileNode, id); + + String mime = contentType != null ? contentType : Files.probeContentType(Paths.get(fileName)); + if (mime != null) { + fileNode.getNode(JCR_CONTENT).setProperty(Property.JCR_MIMETYPE, mime); + } + fileNode.getSession().save(); + + // reset resource manager + ResourceManager resourceManager = RWT.getResourceManager(); + if (previousResourceName != null && resourceManager.isRegistered(previousResourceName)) { + resourceManager.unregister(previousResourceName); + if (log.isDebugEnabled()) + log.debug("Unregistered image " + previousResourceName); + } + return CmsUiUtils.getDataPath(fileNode); + } catch (IOException e) { + throw new RuntimeException("Cannot upload image " + fileName + " in " + parentNode, e); + } catch (RepositoryException e) { + throw new JcrException(e); + } finally { + IOUtils.closeQuietly(inputStream); + } + } + + /** Does nothing by default. */ + protected void processNewImageFile(Node context, Node fileNode, ImageData id) + throws RepositoryException, IOException { + } + + @Override + protected String noImg(Cms2DSize size) { + return CmsUiUtils.noImg(size); + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/MenuLink.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/MenuLink.java new file mode 100644 index 0000000..284d2bd --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/MenuLink.java @@ -0,0 +1,22 @@ +package org.argeo.cms.ui.util; + +import org.argeo.cms.swt.CmsStyles; + +/** + * Convenience class setting the custom style {@link CmsStyles#CMS_MENU_LINK} on + * a {@link CmsLink} when simple menus are used. + */ +public class MenuLink extends CmsLink { + public MenuLink() { + setCustom(CmsStyles.CMS_MENU_LINK); + } + + public MenuLink(String label, String target, String custom) { + super(label, target, custom); + } + + public MenuLink(String label, String target) { + super(label, target, CmsStyles.CMS_MENU_LINK); + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleCmsHeader.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleCmsHeader.java new file mode 100644 index 0000000..ab6a29f --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleCmsHeader.java @@ -0,0 +1,97 @@ +package org.argeo.cms.ui.util; + +import java.util.ArrayList; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.swt.CmsException; +import org.argeo.cms.swt.CmsStyles; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ui.CmsUiProvider; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +/** A header in three parts */ +public class SimpleCmsHeader implements CmsUiProvider { + private List lead = new ArrayList(); + private List center = new ArrayList(); + private List end = new ArrayList(); + + private Boolean subPartsSameWidth = false; + + @Override + public Control createUi(Composite parent, Node context) throws RepositoryException { + Composite header = new Composite(parent, SWT.NONE); + header.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_HEADER); + header.setBackgroundMode(SWT.INHERIT_DEFAULT); + header.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(3, false))); + + configurePart(context, header, lead); + configurePart(context, header, center); + configurePart(context, header, end); + return header; + } + + protected void configurePart(Node context, Composite parent, List partProviders) + throws RepositoryException { + final int style; + final String custom; + if (lead == partProviders) { + style = SWT.LEAD; + custom = CmsStyles.CMS_HEADER_LEAD; + } else if (center == partProviders) { + style = SWT.CENTER; + custom = CmsStyles.CMS_HEADER_CENTER; + } else if (end == partProviders) { + style = SWT.END; + custom = CmsStyles.CMS_HEADER_END; + } else { + throw new CmsException("Unsupported part providers " + partProviders); + } + + Composite part = new Composite(parent, SWT.NONE); + part.setData(RWT.CUSTOM_VARIANT, custom); + GridData gridData = new GridData(style, SWT.FILL, true, true); + part.setLayoutData(gridData); + part.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(partProviders.size(), subPartsSameWidth))); + for (CmsUiProvider uiProvider : partProviders) { + Control subPart = uiProvider.createUi(part, context); + subPart.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + } + } + + public void setLead(List lead) { + this.lead = lead; + } + + public void setCenter(List center) { + this.center = center; + } + + public void setEnd(List end) { + this.end = end; + } + + public void setSubPartsSameWidth(Boolean subPartsSameWidth) { + this.subPartsSameWidth = subPartsSameWidth; + } + + public List getLead() { + return lead; + } + + public List getCenter() { + return center; + } + + public List getEnd() { + return end; + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleDynamicPages.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleDynamicPages.java new file mode 100644 index 0000000..c61a2fc --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleDynamicPages.java @@ -0,0 +1,118 @@ +package org.argeo.cms.ui.util; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.Property; +import javax.jcr.PropertyIterator; +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; +import javax.jcr.Value; + +import org.argeo.cms.swt.CmsException; +import org.argeo.cms.ui.CmsUiProvider; +import org.argeo.jcr.JcrUtils; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; + +public class SimpleDynamicPages implements CmsUiProvider { + + @Override + public Control createUi(Composite parent, Node context) + throws RepositoryException { + if (context == null) + throw new CmsException("Context cannot be null"); + parent.setLayout(new GridLayout(2, false)); + + // parent + if (!context.getPath().equals("/")) { + new CmsLink("..", context.getParent().getPath()).createUi(parent, + context); + new Label(parent, SWT.NONE).setText(context.getParent() + .getPrimaryNodeType().getName()); + } + + // context + Label contextL = new Label(parent, SWT.NONE); + contextL.setData(RWT.MARKUP_ENABLED, true); + contextL.setText("" + context.getName() + ""); + new Label(parent, SWT.NONE).setText(context.getPrimaryNodeType() + .getName()); + + // children + // Label childrenL = new Label(parent, SWT.NONE); + // childrenL.setData(RWT.MARKUP_ENABLED, true); + // childrenL.setText("Children:"); + // childrenL.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, + // false, 2, 1)); + + for (NodeIterator nIt = context.getNodes(); nIt.hasNext();) { + Node child = nIt.nextNode(); + new CmsLink(child.getName(), child.getPath()).createUi(parent, + context); + + new Label(parent, SWT.NONE).setText(child.getPrimaryNodeType() + .getName()); + } + + // properties + // Label propsL = new Label(parent, SWT.NONE); + // propsL.setData(RWT.MARKUP_ENABLED, true); + // propsL.setText("Properties:"); + // propsL.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false, + // 2, 1)); + for (PropertyIterator pIt = context.getProperties(); pIt.hasNext();) { + Property property = pIt.nextProperty(); + + Label label = new Label(parent, SWT.NONE); + label.setText(property.getName()); + label.setToolTipText(JcrUtils + .getPropertyDefinitionAsString(property)); + + new Label(parent, SWT.NONE).setText(getPropAsString(property)); + } + + return null; + } + + private String getPropAsString(Property property) + throws RepositoryException { + String result = ""; + DateFormat timeFormatter = new SimpleDateFormat(""); + if (property.isMultiple()) { + result = getMultiAsString(property, ", "); + } else { + Value value = property.getValue(); + if (value.getType() == PropertyType.BINARY) + result = ""; + else if (value.getType() == PropertyType.DATE) + result = timeFormatter.format(value.getDate().getTime()); + else + result = value.getString(); + } + return result; + } + + private String getMultiAsString(Property property, String separator) + throws RepositoryException { + if (separator == null) + separator = "; "; + Value[] values = property.getValues(); + StringBuilder builder = new StringBuilder(); + for (Value val : values) { + String currStr = val.getString(); + if (!"".equals(currStr.trim())) + builder.append(currStr).append(separator); + } + if (builder.lastIndexOf(separator) >= 0) + return builder.substring(0, builder.length() - separator.length()); + else + return builder.toString(); + } +} \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleStaticPage.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleStaticPage.java new file mode 100644 index 0000000..63e504b --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleStaticPage.java @@ -0,0 +1,32 @@ +package org.argeo.cms.ui.util; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.swt.CmsStyles; +import org.argeo.cms.ui.CmsUiProvider; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; + +public class SimpleStaticPage implements CmsUiProvider { + private String text; + + @Override + public Control createUi(Composite parent, Node context) + throws RepositoryException { + Label textC = new Label(parent, SWT.WRAP); + textC.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_STATIC_TEXT); + textC.setData(RWT.MARKUP_ENABLED, Boolean.TRUE); + textC.setText(text); + + return textC; + } + + public void setText(String text) { + this.text = text; + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleStyle.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleStyle.java new file mode 100644 index 0000000..b5fca26 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleStyle.java @@ -0,0 +1,8 @@ +package org.argeo.cms.ui.util; + +import org.argeo.api.cms.ux.CmsStyle; + +/** Simple styles used by the CMS UI utilities. */ +public enum SimpleStyle implements CmsStyle { + link; +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/StyleSheetResourceLoader.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/StyleSheetResourceLoader.java new file mode 100644 index 0000000..1e17dc9 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/StyleSheetResourceLoader.java @@ -0,0 +1,71 @@ +package org.argeo.cms.ui.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.apache.commons.io.IOUtils; +import org.argeo.cms.swt.CmsException; +import org.eclipse.rap.rwt.service.ResourceLoader; +import org.osgi.framework.Bundle; + +/** {@link ResourceLoader} caching stylesheets. */ +public class StyleSheetResourceLoader implements ResourceLoader { + private Bundle themeBundle; + private Map stylesheets = new LinkedHashMap(); + + public StyleSheetResourceLoader(Bundle themeBundle) { + this.themeBundle = themeBundle; + } + + @Override + public InputStream getResourceAsStream(String resourceName) throws IOException { + if (!stylesheets.containsKey(resourceName)) { + // TODO deal with other bundles + // Bundle bundle = bundleContext.getBundle(); + // String location = + // bundle.getLocation().substring("initial@reference:".length()); + // if (location.startsWith("file:")) { + // Path path = null; + // try { + // path = Paths.get(new URI(location)); + // } catch (URISyntaxException e) { + // e.printStackTrace(); + // } + // if (path != null) { + // Path resourcePath = path.resolve(resourceName); + // if (Files.exists(resourcePath)) + // return Files.newInputStream(resourcePath); + // } + // } + + URL res = themeBundle.getEntry(resourceName); + if (res == null) + throw new CmsException( + "Entry " + resourceName + " not found in bundle " + themeBundle.getSymbolicName()); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + IOUtils.copy(res.openStream(), out); + stylesheets.put(resourceName, new StyleSheet(out.toByteArray())); + } + return new ByteArrayInputStream(stylesheets.get(resourceName).getData()); + // return res.openStream(); + } + + private class StyleSheet { + private byte[] data; + + public StyleSheet(byte[] data) { + super(); + this.data = data; + } + + public byte[] getData() { + return data; + } + + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SystemNotifications.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SystemNotifications.java new file mode 100644 index 0000000..5a00781 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SystemNotifications.java @@ -0,0 +1,129 @@ +package org.argeo.cms.ui.util; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.apache.commons.io.IOUtils; +import org.argeo.cms.swt.CmsException; +import org.argeo.cms.swt.CmsStyles; +import org.argeo.cms.swt.CmsSwtUtils; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.events.ShellAdapter; +import org.eclipse.swt.events.ShellEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; + +/** Shell displaying system notifications such as exceptions */ +public class SystemNotifications extends Shell implements CmsStyles, + MouseListener { + private static final long serialVersionUID = -8129377525216022683L; + + private Control source; + + public SystemNotifications(Control source) { + super(source.getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP); + setData(RWT.CUSTOM_VARIANT, CMS_USER_MENU); + + this.source = source; + + // TODO UI + // setLocation(source.toDisplay(source.getSize().x - getSize().x, + // source.getSize().y)); + setLayout(new GridLayout()); + addMouseListener(this); + + addShellListener(new ShellAdapter() { + private static final long serialVersionUID = 5178980294808435833L; + + @Override + public void shellDeactivated(ShellEvent e) { + close(); + dispose(); + } + }); + + } + + public void notifyException(Throwable exception) { + Composite pane = this; + + Label lbl = new Label(pane, SWT.NONE); + lbl.setText(exception.getLocalizedMessage() + + (exception instanceof CmsException ? "" : "(" + + exception.getClass().getName() + ")") + "\n"); + lbl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + lbl.addMouseListener(this); + if (exception.getCause() != null) + appendCause(pane, exception.getCause()); + + StringBuilder mailToUrl = new StringBuilder("mailto:?"); + try { + mailToUrl.append("subject=").append( + URLEncoder.encode( + "Exception " + + new SimpleDateFormat("yyyy-MM-dd hh:mm") + .format(new Date()), "UTF-8") + .replace("+", "%20")); + + StringWriter sw = new StringWriter(); + exception.printStackTrace(new PrintWriter(sw)); + IOUtils.closeQuietly(sw); + + // see + // http://stackoverflow.com/questions/4737841/urlencoder-not-able-to-translate-space-character + String encoded = URLEncoder.encode(sw.toString(), "UTF-8").replace( + "+", "%20"); + mailToUrl.append("&body=").append(encoded); + } catch (UnsupportedEncodingException e) { + mailToUrl.append("&body=").append("Could not encode: ") + .append(e.getMessage()); + } + Label mailTo = new Label(pane, SWT.NONE); + CmsSwtUtils.markup(mailTo); + mailTo.setText("Send details"); + mailTo.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false)); + + pack(); + layout(); + + setLocation(source.toDisplay(source.getSize().x - getSize().x, + source.getSize().y - getSize().y)); + open(); + } + + private void appendCause(Composite parent, Throwable e) { + Label lbl = new Label(parent, SWT.NONE); + lbl.setText(" caused by: " + e.getLocalizedMessage() + " (" + + e.getClass().getName() + ")" + "\n"); + lbl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + lbl.addMouseListener(this); + if (e.getCause() != null) + appendCause(parent, e.getCause()); + } + + @Override + public void mouseDoubleClick(MouseEvent e) { + } + + @Override + public void mouseDown(MouseEvent e) { + close(); + dispose(); + } + + @Override + public void mouseUp(MouseEvent e) { + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/UserMenu.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/UserMenu.java new file mode 100644 index 0000000..09aeff6 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/UserMenu.java @@ -0,0 +1,56 @@ +package org.argeo.cms.ui.util; + +import javax.jcr.Node; + +import org.argeo.cms.swt.CmsException; +import org.argeo.cms.swt.auth.CmsLoginShell; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ShellAdapter; +import org.eclipse.swt.events.ShellEvent; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +/** The site-related user menu */ +public class UserMenu extends CmsLoginShell { + private final Control source; + private final Node context; + + public UserMenu(Control source, Node context) { + // FIXME pass CMS context + super(CmsUiUtils.getCmsView(), null); + this.context = context; + createUi(); + if (source == null) + throw new CmsException("Source control cannot be null."); + this.source = source; + open(); + } + + @Override + protected Shell createShell() { + return new Shell(Display.getCurrent(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP); + } + + @Override + public void open() { + Shell shell = getShell(); + shell.pack(); + shell.layout(); + shell.setLocation(source.toDisplay(source.getSize().x - shell.getSize().x, source.getSize().y)); + shell.addShellListener(new ShellAdapter() { + private static final long serialVersionUID = 5178980294808435833L; + + @Override + public void shellDeactivated(ShellEvent e) { + closeShell(); + } + }); + super.open(); + } + + protected Node getContext() { + return context; + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/UserMenuLink.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/UserMenuLink.java new file mode 100644 index 0000000..317a7b5 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/UserMenuLink.java @@ -0,0 +1,84 @@ +package org.argeo.cms.ui.util; + +import javax.jcr.Node; + +import org.argeo.cms.CmsMsg; +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.swt.CmsStyles; +import org.argeo.cms.swt.auth.CmsLoginShell; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; + +/** Open the user menu when clicked */ +public class UserMenuLink extends MenuLink { + + public UserMenuLink() { + setCustom(CmsStyles.CMS_USER_MENU_LINK); + } + + @Override + public Control createUi(Composite parent, Node context) { + if (CurrentUser.isAnonymous()) + setLabel(CmsMsg.login.lead()); + else { + setLabel(CurrentUser.getDisplayName()); + } + Label link = (Label) ((Composite) super.createUi(parent, context)).getChildren()[0]; + link.addMouseListener(new UserMenuLinkController(context)); + return link.getParent(); + } + + protected CmsLoginShell createUserMenu(Control source, Node context) { + return new UserMenu(source.getParent(), context); + } + + private class UserMenuLinkController implements MouseListener, DisposeListener { + private static final long serialVersionUID = 3634864186295639792L; + + private CmsLoginShell userMenu = null; + private long lastDisposeTS = 0l; + + private final Node context; + + public UserMenuLinkController(Node context) { + this.context = context; + } + + // + // MOUSE LISTENER + // + @Override + public void mouseDown(MouseEvent e) { + if (e.button == 1) { + Control source = (Control) e.getSource(); + if (userMenu == null) { + long durationSinceLastDispose = System.currentTimeMillis() - lastDisposeTS; + // avoid to reopen the menu, if one has clicked gain + if (durationSinceLastDispose > 200) { + userMenu = createUserMenu(source, context); + userMenu.getShell().addDisposeListener(this); + } + } + } + } + + @Override + public void mouseDoubleClick(MouseEvent e) { + } + + @Override + public void mouseUp(MouseEvent e) { + } + + @Override + public void widgetDisposed(DisposeEvent event) { + userMenu = null; + lastDisposeTS = System.currentTimeMillis(); + } + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/VerticalMenu.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/VerticalMenu.java new file mode 100644 index 0000000..7f846c9 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/VerticalMenu.java @@ -0,0 +1,44 @@ +package org.argeo.cms.ui.util; + +import java.util.ArrayList; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ui.CmsUiProvider; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +public class VerticalMenu implements CmsUiProvider { + private List items = new ArrayList(); + + @Override + public Control createUi(Composite parent, Node context) throws RepositoryException { + Composite part = new Composite(parent, SWT.NONE); + part.setLayoutData(new GridData(SWT.LEAD, SWT.TOP, false, false)); +// part.setData(RWT.CUSTOM_VARIANT, custom); + part.setLayout(CmsSwtUtils.noSpaceGridLayout()); + for (CmsUiProvider uiProvider : items) { + Control subPart = uiProvider.createUi(part, context); + subPart.setLayoutData(new GridData(SWT.LEAD, SWT.TOP, false, false)); + } + return part; + } + + public void add(CmsUiProvider uiProvider) { + items.add(uiProvider); + } + + public List getItems() { + return items; + } + + public void setItems(List items) { + this.items = items; + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/package-info.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/package-info.java new file mode 100644 index 0000000..566df88 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/package-info.java @@ -0,0 +1,2 @@ +/** Argeo CMS UI utilities. */ +package org.argeo.cms.ui.util; \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/AbstractPageViewer.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/AbstractPageViewer.java new file mode 100644 index 0000000..e23846e --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/AbstractPageViewer.java @@ -0,0 +1,351 @@ +package org.argeo.cms.ui.viewers; + +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Observable; +import java.util.Observer; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.security.auth.Subject; + +import org.argeo.api.cms.CmsLog; +import org.argeo.api.cms.ux.CmsEditable; +import org.argeo.cms.swt.SwtEditablePart; +import org.argeo.cms.swt.widgets.ScrolledPage; +import org.argeo.jcr.JcrException; +import org.eclipse.jface.viewers.ContentViewer; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.MouseAdapter; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Widget; +import org.xml.sax.SAXParseException; + +/** Base class for viewers related to a page */ +public abstract class AbstractPageViewer extends ContentViewer implements Observer { + private static final long serialVersionUID = 5438688173410341485L; + + private final static CmsLog log = CmsLog.getLog(AbstractPageViewer.class); + + private final boolean readOnly; + /** The basis for the layouts, typically a ScrolledPage. */ + private final Composite page; + private final CmsEditable cmsEditable; + + private MouseListener mouseListener; + private FocusListener focusListener; + + private SwtEditablePart edited; + private ISelection selection = StructuredSelection.EMPTY; + + private AccessControlContext accessControlContext; + + protected AbstractPageViewer(Section parent, int style, CmsEditable cmsEditable) { + // read only at UI level + readOnly = SWT.READ_ONLY == (style & SWT.READ_ONLY); + + this.cmsEditable = cmsEditable == null ? CmsEditable.NON_EDITABLE : cmsEditable; + if (this.cmsEditable instanceof Observable) + ((Observable) this.cmsEditable).addObserver(this); + + if (cmsEditable.canEdit()) { + mouseListener = createMouseListener(); + focusListener = createFocusListener(); + } + page = findPage(parent); + accessControlContext = AccessController.getContext(); + } + + /** + * Can be called to simplify the called to isModelInitialized() and initModel() + */ + protected void initModelIfNeeded(Node node) { + try { + if (!isModelInitialized(node)) + if (getCmsEditable().canEdit()) { + initModel(node); + node.getSession().save(); + } + } catch (RepositoryException e) { + throw new JcrException("Cannot initialize model", e); + } + } + + /** Called if user can edit and model is not initialized */ + protected Boolean isModelInitialized(Node node) throws RepositoryException { + return true; + } + + /** Called if user can edit and model is not initialized */ + protected void initModel(Node node) throws RepositoryException { + } + + /** Create (retrieve) the MouseListener to use. */ + protected MouseListener createMouseListener() { + return new MouseAdapter() { + private static final long serialVersionUID = 1L; + }; + } + + /** Create (retrieve) the FocusListener to use. */ + protected FocusListener createFocusListener() { + return new FocusListener() { + private static final long serialVersionUID = 1L; + + @Override + public void focusLost(FocusEvent event) { + } + + @Override + public void focusGained(FocusEvent event) { + } + }; + } + + protected Composite findPage(Composite composite) { + if (composite instanceof ScrolledPage) { + return (ScrolledPage) composite; + } else { + if (composite.getParent() == null) + return composite; + return findPage(composite.getParent()); + } + } + + public void layoutPage() { + if (page != null) + page.layout(true, true); + } + + protected void showControl(Control control) { + if (page != null && (page instanceof ScrolledPage)) + ((ScrolledPage) page).showControl(control); + } + + @Override + public void update(Observable o, Object arg) { + if (o == cmsEditable) + editingStateChanged(cmsEditable); + } + + /** To be overridden in order to provide the actual refresh */ + protected void refresh(Control control) throws RepositoryException { + } + + /** To be overridden.Save the edited part. */ + protected void save(SwtEditablePart part) throws RepositoryException { + } + + /** Prepare the edited part */ + protected void prepare(SwtEditablePart part, Object caretPosition) { + } + + /** Notified when the editing state changed. Does nothing, to be overridden */ + protected void editingStateChanged(CmsEditable cmsEditable) { + } + + @Override + public void refresh() { + // TODO check actual context in order to notice a discrepancy + Subject viewerSubject = getViewerSubject(); + Subject.doAs(viewerSubject, (PrivilegedAction) () -> { + try { + if (cmsEditable.canEdit() && !readOnly) + mouseListener = createMouseListener(); + else + mouseListener = null; + refresh(getControl()); + // layout(getControl()); + if (!getControl().isDisposed()) + layoutPage(); + } catch (RepositoryException e) { + throw new JcrException("Cannot refresh", e); + } + return null; + }); + } + + @Override + public void setSelection(ISelection selection, boolean reveal) { + this.selection = selection; + } + + protected void updateContent(SwtEditablePart part) throws RepositoryException { + } + + // LOW LEVEL EDITION + protected void edit(SwtEditablePart part, Object caretPosition) { + try { + if (edited == part) + return; + + if (edited != null && edited != part) { + SwtEditablePart previouslyEdited = edited; + try { + stopEditing(true); + } catch (Exception e) { + notifyEditionException(e); + edit(previouslyEdited, caretPosition); + return; + } + } + + part.startEditing(); + edited = part; + updateContent(part); + prepare(part, caretPosition); + edited.getControl().addFocusListener(new FocusListener() { + private static final long serialVersionUID = 6883521812717097017L; + + @Override + public void focusLost(FocusEvent event) { + stopEditing(true); + } + + @Override + public void focusGained(FocusEvent event) { + } + }); + + layout(part.getControl()); + showControl(part.getControl()); + } catch (RepositoryException e) { + throw new JcrException("Cannot edit " + part, e); + } + } + + protected void stopEditing(Boolean save) { + if (edited instanceof Widget && ((Widget) edited).isDisposed()) { + edited = null; + return; + } + + assert edited != null; + if (edited == null) { + if (log.isTraceEnabled()) + log.warn("Told to stop editing while not editing anything"); + return; + } + + try { + if (save) + save(edited); + + edited.stopEditing(); + SwtEditablePart editablePart = edited; + Control control = ((SwtEditablePart) edited).getControl(); + edited = null; + // TODO make edited state management more robust + updateContent(editablePart); + layout(control); + } catch (RepositoryException e) { + throw new JcrException("Cannot stop editing", e); + } finally { + edited = null; + } + } + + // METHODS AVAILABLE TO EXTENDING CLASSES + protected void saveEdit() { + if (edited != null) + stopEditing(true); + } + + protected void cancelEdit() { + if (edited != null) + stopEditing(false); + } + + /** Layout this controls from the related base page. */ + public void layout(Control... controls) { + page.layout(controls); + } + + /** + * Find the first {@link SwtEditablePart} in the parents hierarchy of this control + */ + protected SwtEditablePart findDataParent(Control parent) { + if (parent instanceof SwtEditablePart) { + return (SwtEditablePart) parent; + } + if (parent.getParent() != null) + return findDataParent(parent.getParent()); + else + throw new IllegalStateException("No data parent found"); + } + + // UTILITIES + /** Check whether the edited part is in a proper state */ + protected void checkEdited() { + if (edited == null || (edited instanceof Widget) && ((Widget) edited).isDisposed()) + throw new IllegalStateException("Edited should not be null or disposed at this stage"); + } + + /** Persist all changes. */ + protected void persistChanges(Session session) throws RepositoryException { + session.save(); + session.refresh(false); + // TODO notify that changes have been persisted + } + + /** Convenience method using a Node in order to save the underlying session. */ + protected void persistChanges(Node anyNode) throws RepositoryException { + persistChanges(anyNode.getSession()); + } + + /** Notify edition exception */ + protected void notifyEditionException(Throwable e) { + Throwable eToLog = e; + if (e instanceof IllegalArgumentException) + if (e.getCause() instanceof SAXParseException) + eToLog = e.getCause(); + log.error(eToLog.getMessage(), eToLog); +// if (log.isTraceEnabled()) +// log.trace("Full stack of " + eToLog.getMessage(), e); + // TODO Light error notification popup + } + + protected Subject getViewerSubject() { + Subject res = null; + if (accessControlContext != null) { + res = Subject.getSubject(accessControlContext); + } + if (res == null) + throw new IllegalStateException("No subject associated with this viewer"); + return res; + } + + // GETTERS / SETTERS + public boolean isReadOnly() { + return readOnly; + } + + protected SwtEditablePart getEdited() { + return edited; + } + + public MouseListener getMouseListener() { + return mouseListener; + } + + public FocusListener getFocusListener() { + return focusListener; + } + + public CmsEditable getCmsEditable() { + return cmsEditable; + } + + @Override + public ISelection getSelection() { + return selection; + } +} \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/ItemPart.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/ItemPart.java new file mode 100644 index 0000000..4ca45d1 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/ItemPart.java @@ -0,0 +1,9 @@ +package org.argeo.cms.ui.viewers; + +import javax.jcr.Item; +import javax.jcr.RepositoryException; + +/** An editable part related to a JCR Item */ +public interface ItemPart { + public Item getItem() throws RepositoryException; +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/JcrVersionCmsEditable.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/JcrVersionCmsEditable.java new file mode 100644 index 0000000..298fbde --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/JcrVersionCmsEditable.java @@ -0,0 +1,94 @@ +package org.argeo.cms.ui.viewers; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.nodetype.NodeType; +import javax.jcr.version.VersionManager; + +import org.argeo.api.cms.ux.CmsEditionEvent; +import org.argeo.cms.ux.AbstractCmsEditable; +import org.argeo.jcr.JcrException; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; + +/** Provides the CmsEditable semantic based on JCR versioning. */ +public class JcrVersionCmsEditable extends AbstractCmsEditable { + private final String nodePath;// cache + private final VersionManager versionManager; + private final Boolean canEdit; + + public JcrVersionCmsEditable(Node node) throws RepositoryException { + this.nodePath = node.getPath(); + if (node.getSession().hasPermission(node.getPath(), Session.ACTION_SET_PROPERTY)) { + // was Session.ACTION_ADD_NODE + canEdit = true; + if (!node.isNodeType(NodeType.MIX_VERSIONABLE)) { + node.addMixin(NodeType.MIX_VERSIONABLE); + node.getSession().save(); + } + versionManager = node.getSession().getWorkspace().getVersionManager(); + } else { + canEdit = false; + versionManager = null; + } + + // bind keys + if (canEdit) { + Display display = Display.getCurrent(); + display.setData(RWT.ACTIVE_KEYS, new String[] { "CTRL+RETURN", "CTRL+E" }); + display.addFilter(SWT.KeyDown, new Listener() { + private static final long serialVersionUID = -4378653870463187318L; + + public void handleEvent(Event e) { + boolean ctrlPressed = (e.stateMask & SWT.CTRL) != 0; + if (ctrlPressed && e.keyCode == '\r') + stopEditing(); + else if (ctrlPressed && e.keyCode == 'E') + stopEditing(); + } + }); + } + } + + @Override + public Boolean canEdit() { + return canEdit; + } + + public Boolean isEditing() { + try { + if (!canEdit()) + return false; + return versionManager.isCheckedOut(nodePath); + } catch (RepositoryException e) { + throw new JcrException("Cannot check whether " + nodePath + " is editing", e); + } + } + + @Override + public void startEditing() { + try { + versionManager.checkout(nodePath); +// setChanged(); + } catch (RepositoryException e1) { + throw new JcrException("Cannot publish " + nodePath, e1); + } + notifyListeners(new CmsEditionEvent(nodePath, CmsEditionEvent.START_EDITING, this)); + } + + @Override + public void stopEditing() { + try { + versionManager.checkin(nodePath); +// setChanged(); + } catch (RepositoryException e1) { + throw new JcrException("Cannot publish " + nodePath, e1); + } + notifyListeners(new CmsEditionEvent(nodePath, CmsEditionEvent.STOP_EDITING, this)); + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/NodePart.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/NodePart.java new file mode 100644 index 0000000..b51d4fc --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/NodePart.java @@ -0,0 +1,8 @@ +package org.argeo.cms.ui.viewers; + +import javax.jcr.Node; + +/** An editable part related to a node */ +public interface NodePart extends ItemPart { + public Node getNode(); +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/PropertyPart.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/PropertyPart.java new file mode 100644 index 0000000..793079e --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/PropertyPart.java @@ -0,0 +1,8 @@ +package org.argeo.cms.ui.viewers; + +import javax.jcr.Property; + +/** An editable part related to a JCR Property */ +public interface PropertyPart extends ItemPart { + public Property getProperty(); +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/Section.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/Section.java new file mode 100644 index 0000000..b27fa38 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/Section.java @@ -0,0 +1,166 @@ +package org.argeo.cms.ui.viewers; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.swt.SwtEditablePart; +import org.argeo.cms.ui.widgets.JcrComposite; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +/** A structured UI related to a JCR context. */ +public class Section extends JcrComposite { + private static final long serialVersionUID = -5933796173755739207L; + + private final Section parentSection; + private Composite sectionHeader; + private final Integer relativeDepth; + + public Section(Composite parent, int style, Node node) { + this(parent, findSection(parent), style, node); + } + + public Section(Section section, int style, Node node) { + this(section, section, style, node); + } + + protected Section(Composite parent, Section parentSection, int style, Node node) { + super(parent, style, node); + try { + this.parentSection = parentSection; + if (parentSection != null) { + relativeDepth = getNode().getDepth() - parentSection.getNode().getDepth(); + } else { + relativeDepth = 0; + } + setLayout(CmsSwtUtils.noSpaceGridLayout()); + } catch (RepositoryException e) { + throw new IllegalStateException("Cannot create section from " + node, e); + } + } + + public Map getSubSections() throws RepositoryException { + LinkedHashMap result = new LinkedHashMap(); + for (Control child : getChildren()) { + if (child instanceof Composite) { + collectDirectSubSections((Composite) child, result); + } + } + return Collections.unmodifiableMap(result); + } + + private void collectDirectSubSections(Composite composite, LinkedHashMap subSections) + throws RepositoryException { + if (composite == sectionHeader || composite instanceof SwtEditablePart) + return; + if (composite instanceof Section) { + Section section = (Section) composite; + subSections.put(section.getNodeId(), section); + return; + } + + for (Control child : composite.getChildren()) + if (child instanceof Composite) + collectDirectSubSections((Composite) child, subSections); + } + + public Composite createHeader() { + return createHeader(this); + } + + public Composite createHeader(Composite parent) { + if (sectionHeader != null) + sectionHeader.dispose(); + + sectionHeader = new Composite(parent, SWT.NONE); + sectionHeader.setLayoutData(CmsSwtUtils.fillWidth()); + sectionHeader.setLayout(CmsSwtUtils.noSpaceGridLayout()); + // sectionHeader.moveAbove(null); + // layout(); + return sectionHeader; + } + + public Composite getHeader() { + if (sectionHeader != null && sectionHeader.isDisposed()) + sectionHeader = null; + return sectionHeader; + } + + // SECTION PARTS + public SectionPart getSectionPart(String partId) { + for (Control child : getChildren()) { + if (child instanceof SectionPart) { + SectionPart sectionPart = (SectionPart) child; + if (sectionPart.getPartId().equals(partId)) + return sectionPart; + } + } + return null; + } + + public SectionPart nextSectionPart(SectionPart sectionPart) { + Control[] children = getChildren(); + for (int i = 0; i < children.length; i++) { + if (sectionPart == children[i]) { + for (int j = i + 1; j < children.length; j++) { + if (children[i + 1] instanceof SectionPart) { + return (SectionPart) children[i + 1]; + } + } + +// if (i + 1 < children.length) { +// Composite next = (Composite) children[i + 1]; +// return (SectionPart) next; +// } else { +// // next section +// } + } + } + return null; + } + + public SectionPart previousSectionPart(SectionPart sectionPart) { + Control[] children = getChildren(); + for (int i = 0; i < children.length; i++) { + if (sectionPart == children[i]) + if (i != 0) { + Composite previous = (Composite) children[i - 1]; + return (SectionPart) previous; + } else { + // previous section + } + } + return null; + } + + @Override + public String toString() { + if (parentSection == null) + return "Main section " + getNode(); + return "Section " + getNode(); + } + + public Section getParentSection() { + return parentSection; + } + + public Integer getRelativeDepth() { + return relativeDepth; + } + + /** Recursively finds the related section in the parents (can be itself) */ + public static Section findSection(Control control) { + if (control == null) + return null; + if (control instanceof Section) + return (Section) control; + else + return findSection(control.getParent()); + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/SectionPart.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/SectionPart.java new file mode 100644 index 0000000..4278c83 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/SectionPart.java @@ -0,0 +1,10 @@ +package org.argeo.cms.ui.viewers; + +import org.argeo.cms.swt.SwtEditablePart; + +/** An editable part dynamically related to a Section */ +public interface SectionPart extends SwtEditablePart, NodePart { + public String getPartId(); + + public Section getSection(); +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/package-info.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/package-info.java new file mode 100644 index 0000000..2f07931 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/package-info.java @@ -0,0 +1,2 @@ +/** Argeo CMS generic viewers, based on JFace. */ +package org.argeo.cms.ui.viewers; \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/EditableImage.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/EditableImage.java new file mode 100644 index 0000000..95d9e8e --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/EditableImage.java @@ -0,0 +1,112 @@ +package org.argeo.cms.ui.widgets; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.api.cms.CmsLog; +import org.argeo.api.cms.ux.Cms2DSize; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +/** A stylable and editable image. */ +public abstract class EditableImage extends StyledControl { + private static final long serialVersionUID = -5689145523114022890L; + private final static CmsLog log = CmsLog.getLog(EditableImage.class); + + private Cms2DSize preferredImageSize; + private Boolean loaded = false; + + public EditableImage(Composite parent, int swtStyle) { + super(parent, swtStyle); + } + + public EditableImage(Composite parent, int swtStyle, Cms2DSize preferredImageSize) { + super(parent, swtStyle); + this.preferredImageSize = preferredImageSize; + } + + public EditableImage(Composite parent, int style, Node node, boolean cacheImmediately, Cms2DSize preferredImageSize) + throws RepositoryException { + super(parent, style, node, cacheImmediately); + this.preferredImageSize = preferredImageSize; + } + + @Override + protected void setContainerLayoutData(Composite composite) { + // composite.setLayoutData(fillWidth()); + } + + @Override + protected void setControlLayoutData(Control control) { + // control.setLayoutData(fillWidth()); + } + + /** To be overriden. */ + protected String createImgTag() throws RepositoryException { + return CmsUiUtils + .noImg(preferredImageSize != null ? preferredImageSize : new Cms2DSize(getSize().x, getSize().y)); + } + + protected Label createLabel(Composite box, String style) { + Label lbl = new Label(box, getStyle()); + // lbl.setLayoutData(CmsUiUtils.fillWidth()); + CmsSwtUtils.markup(lbl); + CmsSwtUtils.style(lbl, style); + if (mouseListener != null) + lbl.addMouseListener(mouseListener); + load(lbl); + return lbl; + } + + /** To be overriden. */ + protected synchronized Boolean load(Control control) { + String imgTag; + try { + imgTag = createImgTag(); + } catch (Exception e) { + // throw new CmsException("Cannot retrieve image", e); + log.error("Cannot retrieve image", e); + imgTag = CmsUiUtils.noImg(preferredImageSize); + loaded = false; + } + + if (imgTag == null) { + loaded = false; + imgTag = CmsUiUtils.noImg(preferredImageSize); + } else + loaded = true; + if (control != null) { + ((Label) control).setText(imgTag); + control.setSize(preferredImageSize != null + ? new Point(preferredImageSize.getWidth(), preferredImageSize.getHeight()) + : getSize()); + } else { + loaded = false; + } + getParent().layout(); + return loaded; + } + + public void setPreferredSize(Cms2DSize size) { + this.preferredImageSize = size; + if (!loaded) { + load((Label) getControl()); + } + } + + protected Text createText(Composite box, String style) { + Text text = new Text(box, getStyle()); + CmsSwtUtils.style(text, style); + return text; + } + + public Cms2DSize getPreferredImageSize() { + return preferredImageSize; + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/EditableText.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/EditableText.java new file mode 100644 index 0000000..e3499ac --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/EditableText.java @@ -0,0 +1,145 @@ +package org.argeo.cms.ui.widgets; + +import javax.jcr.Item; +import javax.jcr.RepositoryException; + +import org.argeo.cms.swt.CmsSwtUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +/** Editable text part displaying styled text. */ +public class EditableText extends StyledControl { + private static final long serialVersionUID = -6372283442330912755L; + + private boolean editable = true; + + private Color highlightColor; + private Composite highlight; + + private boolean useTextAsLabel = false; + + public EditableText(Composite parent, int style) { + super(parent, style); + editable = !(SWT.READ_ONLY == (style & SWT.READ_ONLY)); + highlightColor = parent.getDisplay().getSystemColor(SWT.COLOR_GRAY); + } + + public EditableText(Composite parent, int style, Item item) throws RepositoryException { + this(parent, style, item, false); + } + + public EditableText(Composite parent, int style, Item item, boolean cacheImmediately) throws RepositoryException { + super(parent, style, item, cacheImmediately); + editable = !(SWT.READ_ONLY == (style & SWT.READ_ONLY)); + highlightColor = parent.getDisplay().getSystemColor(SWT.COLOR_GRAY); + } + + @Override + protected Control createControl(Composite box, String style) { + if (isEditing() && getEditable()) { + return createText(box, style, true); + } else { + if (useTextAsLabel) { + return createTextLabel(box, style); + } else { + return createLabel(box, style); + } + } + } + + protected Label createLabel(Composite box, String style) { + Label lbl = new Label(box, getStyle() | SWT.WRAP); + lbl.setLayoutData(CmsSwtUtils.fillWidth()); + if (style != null) + CmsSwtUtils.style(lbl, style); + CmsSwtUtils.markup(lbl); + if (mouseListener != null) + lbl.addMouseListener(mouseListener); + return lbl; + } + + protected Text createTextLabel(Composite box, String style) { + Text lbl = new Text(box, getStyle() | SWT.MULTI); + lbl.setEditable(false); + lbl.setLayoutData(CmsSwtUtils.fillWidth()); + if (style != null) + CmsSwtUtils.style(lbl, style); + CmsSwtUtils.markup(lbl); + if (mouseListener != null) + lbl.addMouseListener(mouseListener); + return lbl; + } + + protected Text createText(Composite box, String style, boolean editable) { + highlight = new Composite(box, SWT.NONE); + highlight.setBackground(highlightColor); + GridData highlightGd = new GridData(SWT.FILL, SWT.FILL, false, false); + highlightGd.widthHint = 5; + highlightGd.heightHint = 3; + highlight.setLayoutData(highlightGd); + + final Text text = new Text(box, getStyle() | SWT.MULTI | SWT.WRAP); + text.setEditable(editable); + GridData textLayoutData = CmsSwtUtils.fillWidth(); + // textLayoutData.heightHint = preferredHeight; + text.setLayoutData(textLayoutData); + if (style != null) + CmsSwtUtils.style(text, style); + text.setFocus(); + return text; + } + + @Override + protected void clear(boolean deep) { + if (highlight != null) + highlight.dispose(); + super.clear(deep); + } + + public void setText(String text) { + Control child = getControl(); + if (child instanceof Label) + ((Label) child).setText(text); + else if (child instanceof Text) + ((Text) child).setText(text); + } + + public Text getAsText() { + return (Text) getControl(); + } + + public Label getAsLabel() { + return (Label) getControl(); + } + + public String getText() { + Control child = getControl(); + + if (child instanceof Label) + return ((Label) child).getText(); + else if (child instanceof Text) + return ((Text) child).getText(); + else + throw new IllegalStateException("Unsupported control " + child.getClass()); + } + + /** @deprecated Use {@link #isEditable()} instead. */ + @Deprecated + public boolean getEditable() { + return isEditable(); + } + + public boolean isEditable() { + return editable; + } + + public void setUseTextAsLabel(boolean useTextAsLabel) { + this.useTextAsLabel = useTextAsLabel; + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/Img.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/Img.java new file mode 100644 index 0000000..41063fa --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/Img.java @@ -0,0 +1,155 @@ +package org.argeo.cms.ui.widgets; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.api.cms.ux.Cms2DSize; +import org.argeo.api.cms.ux.CmsImageManager; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ui.internal.JcrFileUploadReceiver; +import org.argeo.cms.ui.viewers.NodePart; +import org.argeo.cms.ui.viewers.Section; +import org.argeo.cms.ui.viewers.SectionPart; +import org.argeo.jcr.Jcr; +import org.argeo.jcr.JcrException; +import org.eclipse.rap.fileupload.FileUploadHandler; +import org.eclipse.rap.fileupload.FileUploadListener; +import org.eclipse.rap.fileupload.FileUploadReceiver; +import org.eclipse.rap.rwt.service.ServerPushSession; +import org.eclipse.rap.rwt.widgets.FileUpload; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +/** An image within the Argeo Text framework */ +public class Img extends EditableImage implements SectionPart, NodePart { + private static final long serialVersionUID = 6233572783968188476L; + + private final Section section; + + private final CmsImageManager imageManager; + private FileUploadHandler currentUploadHandler = null; + private FileUploadListener fileUploadListener; + + public Img(Composite parent, int swtStyle, Node imgNode, Cms2DSize preferredImageSize) throws RepositoryException { + this(Section.findSection(parent), parent, swtStyle, imgNode, preferredImageSize, null); + setStyle(TextStyles.TEXT_IMAGE); + } + + public Img(Composite parent, int swtStyle, Node imgNode) throws RepositoryException { + this(Section.findSection(parent), parent, swtStyle, imgNode, null, null); + setStyle(TextStyles.TEXT_IMAGE); + } + + public Img(Composite parent, int swtStyle, Node imgNode, CmsImageManager imageManager) + throws RepositoryException { + this(Section.findSection(parent), parent, swtStyle, imgNode, null, imageManager); + setStyle(TextStyles.TEXT_IMAGE); + } + + Img(Section section, Composite parent, int swtStyle, Node imgNode, Cms2DSize preferredImageSize, + CmsImageManager imageManager) throws RepositoryException { + super(parent, swtStyle, imgNode, false, preferredImageSize); + this.section = section; + this.imageManager = imageManager != null ? imageManager + : (CmsImageManager) CmsSwtUtils.getCmsView(section).getImageManager(); + CmsSwtUtils.style(this, TextStyles.TEXT_IMG); + } + + @Override + protected Control createControl(Composite box, String style) { + if (isEditing()) { + try { + return createImageChooser(box, style); + } catch (RepositoryException e) { + throw new JcrException("Cannot create image chooser", e); + } + } else { + return createLabel(box, style); + } + } + + @Override + public synchronized void stopEditing() { + super.stopEditing(); + fileUploadListener = null; + } + + @Override + protected synchronized Boolean load(Control lbl) { + Node imgNode = getNode(); + boolean loaded = imageManager.load(imgNode, lbl, getPreferredImageSize()); + // getParent().layout(); + return loaded; + } + + protected Node getUploadFolder() { + return Jcr.getParent(getNode()); + } + + protected String getUploadName() { + Node node = getNode(); + return Jcr.getName(node) + '[' + Jcr.getIndex(node) + ']'; + } + + protected CmsImageManager getImageManager() { + return imageManager; + } + + protected Control createImageChooser(Composite box, String style) throws RepositoryException { + JcrFileUploadReceiver receiver = new JcrFileUploadReceiver(this, getUploadFolder(), getUploadName(), + imageManager); + if (currentUploadHandler != null) + currentUploadHandler.dispose(); + currentUploadHandler = prepareUpload(receiver); + final ServerPushSession pushSession = new ServerPushSession(); + final FileUpload fileUpload = new FileUpload(box, SWT.NONE); + CmsSwtUtils.style(fileUpload, style); + fileUpload.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = -9158471843941668562L; + + @Override + public void widgetSelected(SelectionEvent e) { + pushSession.start(); + fileUpload.submit(currentUploadHandler.getUploadUrl()); + } + }); + return fileUpload; + } + + protected FileUploadHandler prepareUpload(FileUploadReceiver receiver) { + final FileUploadHandler uploadHandler = new FileUploadHandler(receiver); + if (fileUploadListener != null) + uploadHandler.addUploadListener(fileUploadListener); + return uploadHandler; + } + + @Override + public Section getSection() { + return section; + } + + public void setFileUploadListener(FileUploadListener fileUploadListener) { + this.fileUploadListener = fileUploadListener; + if (currentUploadHandler != null) + currentUploadHandler.addUploadListener(fileUploadListener); + } + + @Override + public Node getItem() throws RepositoryException { + return getNode(); + } + + @Override + public String getPartId() { + return getNodeId(); + } + + @Override + public String toString() { + return "Img #" + getPartId(); + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/JcrComposite.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/JcrComposite.java new file mode 100644 index 0000000..6b54c0a --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/JcrComposite.java @@ -0,0 +1,213 @@ +package org.argeo.cms.ui.widgets; + +import javax.jcr.Item; +import javax.jcr.ItemNotFoundException; +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.jcr.JcrException; +import org.eclipse.swt.widgets.Composite; + +/** A composite which can (optionally) manage a JCR Item. */ +public class JcrComposite extends Composite { + private static final long serialVersionUID = -1447009015451153367L; + + private Session session; + + private String nodeId; + private String property = null; + private Node cache; + + /** Regular composite constructor. No layout is set. */ + public JcrComposite(Composite parent, int style) { + super(parent, style); + session = null; + nodeId = null; + } + + public JcrComposite(Composite parent, int style, Item item) { + this(parent, style, item, false); + } + + public JcrComposite(Composite parent, int style, Item item, boolean cacheImmediately) { + super(parent, style); + if (item != null) + try { + this.session = item.getSession(); +// if (!cacheImmediately && (SWT.READ_ONLY == (style & SWT.READ_ONLY))) { +// // (useless?) optimization: we only save a pointer to the session, +// // not even a reference to the item +// this.nodeId = null; +// } else { + Node node; + Property property = null; + if (item instanceof Node) { + node = (Node) item; + } else {// Property + property = (Property) item; + if (property.isMultiple())// TODO manage property index + throw new UnsupportedOperationException("Multiple properties not supported yet."); + this.property = property.getName(); + node = property.getParent(); + } + this.nodeId = node.getIdentifier(); + if (cacheImmediately) + this.cache = node; +// } + setLayout(CmsSwtUtils.noSpaceGridLayout()); + } catch (RepositoryException e) { + throw new IllegalStateException("Cannot create composite from " + item, e); + } + } + + public synchronized Node getNode() { + try { + if (!itemIsNode()) + throw new IllegalStateException("Item is not a Node"); + return getNodeInternal(); + } catch (RepositoryException e) { + throw new JcrException("Cannot get node " + nodeId, e); + } + } + + private synchronized Node getNodeInternal() throws RepositoryException { + if (cache != null) + return cache; + else if (session != null) + if (nodeId != null) + return session.getNodeByIdentifier(nodeId); + else + return null; + else + return null; + } + + public synchronized String getPropertyName() { + try { + return getProperty().getName(); + } catch (RepositoryException e) { + throw new JcrException("Cannot get property name", e); + } + } + + public synchronized Node getPropertyNode() { + try { + return getProperty().getNode(); + } catch (RepositoryException e) { + throw new JcrException("Cannot get property name", e); + } + } + + public synchronized Property getProperty() { + try { + if (itemIsNode()) + throw new IllegalStateException("Item is not a Property"); + Node node = getNodeInternal(); + if (!node.hasProperty(property)) + throw new IllegalStateException("Property " + property + " is not set on " + node); + return node.getProperty(property); + } catch (RepositoryException e) { + throw new JcrException("Cannot get property " + property + " from node " + nodeId, e); + } + } + + public synchronized boolean itemIsNode() { + return property == null; + } + + public synchronized boolean itemExists() { + if (session == null) + return false; + try { + Node n = session.getNodeByIdentifier(nodeId); + if (!itemIsNode()) + return n.hasProperty(property); + else + return true; + } catch (ItemNotFoundException e) { + return false; + } catch (RepositoryException e) { + throw new JcrException("Cannot check whether node exists", e); + } + } + + /** Set/update the cache or change the node */ + public synchronized void setNode(Node node) { + if (!itemIsNode()) + throw new IllegalArgumentException("Cannot set a Node on a Property"); + + if (node == null) {// clear cache + this.cache = null; + return; + } + + try { +// if (session != null || session != node.getSession())// check session +// throw new IllegalArgumentException("Uncompatible session"); +// if (session == null) + session = node.getSession(); + if (nodeId == null || !nodeId.equals(node.getIdentifier())) { + nodeId = node.getIdentifier(); + cache = node; + itemUpdated(); + } else { + cache = node;// set/update cache + } + } catch (RepositoryException e) { + throw new IllegalStateException(e); + } + } + + /** Set/update the cache or change the property */ + public synchronized void setProperty(Property prop) { + if (itemIsNode()) + throw new IllegalArgumentException("Cannot set a Property on a Node"); + + if (prop == null) {// clear cache + this.cache = null; + return; + } + + try { + if (session == null || session != prop.getSession())// check session + throw new IllegalArgumentException("Uncompatible session"); + + Node node = prop.getNode(); + if (nodeId == null || !nodeId.equals(node.getIdentifier()) || !property.equals(prop.getName())) { + nodeId = node.getIdentifier(); + property = prop.getName(); + cache = node; + itemUpdated(); + } else { + cache = node;// set/update cache + } + } catch (RepositoryException e) { + throw new IllegalStateException(e); + } + } + + public synchronized String getNodeId() { + return nodeId; + } + + /** Change the node, does nothing if same. */ + public synchronized void setNodeId(String nodeId) throws RepositoryException { + if (this.nodeId != null && this.nodeId.equals(nodeId)) + return; + this.nodeId = nodeId; + if (cache != null) + cache = session.getNodeByIdentifier(this.nodeId); + itemUpdated(); + } + + protected synchronized void itemUpdated() { + layout(); + } + +// public Session getSession() { +// return session; +// } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/StyledControl.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/StyledControl.java new file mode 100644 index 0000000..e3a5cb4 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/StyledControl.java @@ -0,0 +1,153 @@ +package org.argeo.cms.ui.widgets; + +import javax.jcr.Item; + +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ui.CmsUiConstants; +import org.argeo.eclipse.ui.specific.EclipseUiSpecificUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +/** Editable text part displaying styled text. */ +public abstract class StyledControl extends JcrComposite implements CmsUiConstants { + private static final long serialVersionUID = -6372283442330912755L; + private Control control; + + private Composite container; + private Composite box; + + protected MouseListener mouseListener; + protected FocusListener focusListener; + + private Boolean editing = Boolean.FALSE; + + private Composite ancestorToLayout; + + public StyledControl(Composite parent, int swtStyle) { + super(parent, swtStyle); + setLayout(CmsSwtUtils.noSpaceGridLayout()); + } + + public StyledControl(Composite parent, int style, Item item) { + super(parent, style, item); + } + + public StyledControl(Composite parent, int style, Item item, boolean cacheImmediately) { + super(parent, style, item, cacheImmediately); + } + + protected abstract Control createControl(Composite box, String style); + + protected Composite createBox() { + Composite box = new Composite(container, SWT.INHERIT_DEFAULT); + setContainerLayoutData(box); + box.setLayout(CmsSwtUtils.noSpaceGridLayout(3)); + return box; + } + + protected Composite createContainer() { + Composite container = new Composite(this, SWT.INHERIT_DEFAULT); + setContainerLayoutData(container); + container.setLayout(CmsSwtUtils.noSpaceGridLayout()); + return container; + } + + public Control getControl() { + return control; + } + + protected synchronized Boolean isEditing() { + return editing; + } + + public synchronized void startEditing() { + assert !isEditing(); + editing = true; + // int height = control.getSize().y; + String style = (String) EclipseUiSpecificUtils.getStyleData(control); + clear(false); + refreshControl(style); + + // add the focus listener to the newly created edition control + if (focusListener != null) + control.addFocusListener(focusListener); + } + + public synchronized void stopEditing() { + assert isEditing(); + editing = false; + String style = (String) EclipseUiSpecificUtils.getStyleData(control); + clear(false); + refreshControl(style); + } + + protected void refreshControl(String style) { + control = createControl(box, style); + setControlLayoutData(control); + if (ancestorToLayout != null) + ancestorToLayout.layout(true, true); + else + getParent().layout(true, true); + } + + public void setStyle(String style) { + Object currentStyle = null; + if (control != null) + currentStyle = EclipseUiSpecificUtils.getStyleData(control); + if (currentStyle != null && currentStyle.equals(style)) + return; + + clear(true); + refreshControl(style); + + if (style != null) { + CmsSwtUtils.style(box, style + "_box"); + CmsSwtUtils.style(container, style + "_container"); + } + } + + /** To be overridden */ + protected void setControlLayoutData(Control control) { + control.setLayoutData(CmsSwtUtils.fillWidth()); + } + + /** To be overridden */ + protected void setContainerLayoutData(Composite composite) { + composite.setLayoutData(CmsSwtUtils.fillWidth()); + } + + protected void clear(boolean deep) { + if (deep) { + for (Control control : getChildren()) + control.dispose(); + container = createContainer(); + box = createBox(); + } else { + control.dispose(); + } + } + + public void setMouseListener(MouseListener mouseListener) { + if (this.mouseListener != null && control != null) + control.removeMouseListener(this.mouseListener); + this.mouseListener = mouseListener; + if (control != null && this.mouseListener != null) + control.addMouseListener(mouseListener); + } + + public void setFocusListener(FocusListener focusListener) { + if (this.focusListener != null && control != null) + control.removeFocusListener(this.focusListener); + this.focusListener = focusListener; + if (control != null && this.focusListener != null) + control.addFocusListener(focusListener); + } + + public void setAncestorToLayout(Composite ancestorToLayout) { + this.ancestorToLayout = ancestorToLayout; + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/TextStyles.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/TextStyles.java new file mode 100644 index 0000000..e461ed0 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/TextStyles.java @@ -0,0 +1,37 @@ +package org.argeo.cms.ui.widgets; + +/** Styles references in the CSS. */ +public interface TextStyles { + /** The whole page area */ + public final static String TEXT_AREA = "text_area"; + /** Area providing controls for editing text */ + public final static String TEXT_EDITOR_HEADER = "text_editor_header"; + /** The styled composite for editing the text */ + public final static String TEXT_STYLED_COMPOSITE = "text_styled_composite"; + /** A section */ + public final static String TEXT_SECTION = "text_section"; + /** A paragraph */ + public final static String TEXT_PARAGRAPH = "text_paragraph"; + /** An image */ + public final static String TEXT_IMG = "text_img"; + /** The dialog to edit styled paragraph */ + public final static String TEXT_STYLED_TOOLS_DIALOG = "text_styled_tools_dialog"; + + /* + * DEFAULT TEXT STYLES + */ + /** Default style for text body */ + public final static String TEXT_DEFAULT = "text_default"; + /** Fixed-width, typically code */ + public final static String TEXT_PRE = "text_pre"; + /** Quote */ + public final static String TEXT_QUOTE = "text_quote"; + /** Title */ + public final static String TEXT_TITLE = "text_title"; + /** Header (to be dynamically completed with the depth, e.g. text_h1) */ + public final static String TEXT_H = "text_h"; + + /** Default style for images */ + public final static String TEXT_IMAGE = "text_image"; + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/package-info.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/package-info.java new file mode 100644 index 0000000..514f753 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/package-info.java @@ -0,0 +1,2 @@ +/** Argeo CMS generic widgets, based on SWT. */ +package org.argeo.cms.ui.widgets; \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/AbstractNodeContentProvider.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/AbstractNodeContentProvider.java new file mode 100644 index 0000000..fdafa98 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/AbstractNodeContentProvider.java @@ -0,0 +1,138 @@ +package org.argeo.eclipse.ui.jcr; + +import java.util.ArrayList; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.argeo.api.cms.CmsLog; +import org.argeo.eclipse.ui.AbstractTreeContentProvider; +import org.argeo.eclipse.ui.EclipseUiException; + +/** Canonical implementation of tree content provider manipulating JCR nodes. */ +public abstract class AbstractNodeContentProvider extends + AbstractTreeContentProvider { + private static final long serialVersionUID = -4905836490027272569L; + + private final static CmsLog log = CmsLog + .getLog(AbstractNodeContentProvider.class); + + private Session session; + + public AbstractNodeContentProvider(Session session) { + this.session = session; + } + + /** + * Whether this path is a base path (and thus has no parent). By default it + * returns true if path is '/' (root node) + */ + protected Boolean isBasePath(String path) { + // root node + return path.equals("/"); + } + + @Override + public Object[] getChildren(Object element) { + Object[] children; + if (element instanceof Node) { + try { + Node node = (Node) element; + children = getChildren(node); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot get children of " + element, e); + } + } else if (element instanceof WrappedNode) { + WrappedNode wrappedNode = (WrappedNode) element; + try { + children = getChildren(wrappedNode.getNode()); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot get children of " + + wrappedNode, e); + } + } else if (element instanceof NodesWrapper) { + NodesWrapper node = (NodesWrapper) element; + children = node.getChildren(); + } else { + children = super.getChildren(element); + } + + children = sort(element, children); + return children; + } + + /** Do not sort by default. To be overidden to provide custom sort. */ + protected Object[] sort(Object parent, Object[] children) { + return children; + } + + /** + * To be overridden in order to filter out some nodes. Does nothing by + * default. The provided list is a temporary one and can thus be modified + * directly . (e.g. via an iterator) + */ + protected List filterChildren(List children) + throws RepositoryException { + return children; + } + + protected Object[] getChildren(Node node) throws RepositoryException { + List nodes = new ArrayList(); + for (NodeIterator nit = node.getNodes(); nit.hasNext();) + nodes.add(nit.nextNode()); + nodes = filterChildren(nodes); + return nodes.toArray(); + } + + @Override + public Object getParent(Object element) { + if (element instanceof Node) { + Node node = (Node) element; + try { + String path = node.getPath(); + if (isBasePath(path)) + return null; + else + return node.getParent(); + } catch (RepositoryException e) { + log.warn("Cannot get parent of " + element + ": " + e); + return null; + } + } else if (element instanceof WrappedNode) { + WrappedNode wrappedNode = (WrappedNode) element; + return wrappedNode.getParent(); + } else if (element instanceof NodesWrapper) { + NodesWrapper nodesWrapper = (NodesWrapper) element; + return this.getParent(nodesWrapper.getNode()); + } + return super.getParent(element); + } + + @Override + public boolean hasChildren(Object element) { + try { + if (element instanceof Node) { + Node node = (Node) element; + return node.hasNodes(); + } else if (element instanceof WrappedNode) { + WrappedNode wrappedNode = (WrappedNode) element; + return wrappedNode.getNode().hasNodes(); + } else if (element instanceof NodesWrapper) { + NodesWrapper nodesWrapper = (NodesWrapper) element; + return nodesWrapper.hasChildren(); + } + + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot check whether " + element + + " has children", e); + } + return super.hasChildren(element); + } + + public Session getSession() { + return session; + } +} \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/AsyncUiEventListener.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/AsyncUiEventListener.java new file mode 100644 index 0000000..b880a63 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/AsyncUiEventListener.java @@ -0,0 +1,83 @@ +package org.argeo.eclipse.ui.jcr; + +import java.util.ArrayList; +import java.util.List; + +import javax.jcr.RepositoryException; +import javax.jcr.observation.Event; +import javax.jcr.observation.EventIterator; +import javax.jcr.observation.EventListener; + +import org.argeo.api.cms.CmsLog; +import org.argeo.eclipse.ui.EclipseUiException; +import org.eclipse.swt.widgets.Display; + +/** + * {@link EventListener} which simplifies running actions within the UI thread. + */ +public abstract class AsyncUiEventListener implements EventListener { + // private final static Log logSuper = LogFactory + // .getLog(AsyncUiEventListener.class); + private final CmsLog logThis = CmsLog.getLog(getClass()); + + private final Display display; + + public AsyncUiEventListener(Display display) { + super(); + this.display = display; + } + + /** Called asynchronously in the UI thread. */ + protected abstract void onEventInUiThread(List events) throws RepositoryException; + + /** + * Whether these events should be processed in the UI or skipped with no UI + * job created. + */ + protected Boolean willProcessInUiThread(List events) throws RepositoryException { + return true; + } + + protected CmsLog getLog() { + return logThis; + } + + public final void onEvent(final EventIterator eventIterator) { + final List events = new ArrayList(); + while (eventIterator.hasNext()) + events.add(eventIterator.nextEvent()); + + if (logThis.isTraceEnabled()) + logThis.trace("Received " + events.size() + " events"); + + try { + if (!willProcessInUiThread(events)) + return; + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot test skip events " + events, e); + } + + // Job job = new Job("JCR Events") { + // protected IStatus run(IProgressMonitor monitor) { + // if (display.isDisposed()) { + // logSuper.warn("Display is disposed cannot update UI"); + // return Status.CANCEL_STATUS; + // } + + if (!display.isDisposed()) + display.asyncExec(new Runnable() { + public void run() { + try { + onEventInUiThread(events); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot process events " + events, e); + } + } + }); + + // return Status.OK_STATUS; + // } + // }; + // job.schedule(); + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/DefaultNodeLabelProvider.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/DefaultNodeLabelProvider.java new file mode 100644 index 0000000..22ffeaf --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/DefaultNodeLabelProvider.java @@ -0,0 +1,82 @@ +package org.argeo.eclipse.ui.jcr; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.RepositoryException; +import javax.jcr.nodetype.NodeType; + +import org.argeo.eclipse.ui.EclipseUiException; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.swt.graphics.Image; + +/** + * Default label provider to manage node and corresponding UI objects. It + * provides reasonable overwrite-able default for known JCR types. + */ +public class DefaultNodeLabelProvider extends ColumnLabelProvider { + private static final long serialVersionUID = 1216182332792151235L; + + public String getText(Object element) { + try { + if (element instanceof Node) { + return getText((Node) element); + } else if (element instanceof WrappedNode) { + return getText(((WrappedNode) element).getNode()); + } else if (element instanceof NodesWrapper) { + return getText(((NodesWrapper) element).getNode()); + } + return super.getText(element); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot get text for of " + element, e); + } + } + + protected String getText(Node node) throws RepositoryException { + if (node.isNodeType(NodeType.MIX_TITLE) + && node.hasProperty(Property.JCR_TITLE)) + return node.getProperty(Property.JCR_TITLE).getString(); + else + return node.getName(); + } + + @Override + public Image getImage(Object element) { + try { + if (element instanceof Node) { + return getImage((Node) element); + } else if (element instanceof WrappedNode) { + return getImage(((WrappedNode) element).getNode()); + } else if (element instanceof NodesWrapper) { + return getImage(((NodesWrapper) element).getNode()); + } + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot retrieve image for " + element, e); + } + return super.getImage(element); + } + + protected Image getImage(Node node) throws RepositoryException { + // FIXME who uses that? + return null; + } + + @Override + public String getToolTipText(Object element) { + try { + if (element instanceof Node) { + return getToolTipText((Node) element); + } else if (element instanceof WrappedNode) { + return getToolTipText(((WrappedNode) element).getNode()); + } else if (element instanceof NodesWrapper) { + return getToolTipText(((NodesWrapper) element).getNode()); + } + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot get tooltip for " + element, e); + } + return super.getToolTipText(element); + } + + protected String getToolTipText(Node node) throws RepositoryException { + return null; + } +} \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/JcrUiUtils.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/JcrUiUtils.java new file mode 100644 index 0000000..420154b --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/JcrUiUtils.java @@ -0,0 +1,149 @@ +package org.argeo.eclipse.ui.jcr; + +import java.util.Calendar; + +import javax.jcr.Node; +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; + +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.eclipse.ui.jcr.lists.NodeViewerComparator; +import org.argeo.eclipse.ui.jcr.lists.RowViewerComparator; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.widgets.Table; + +/** Utility methods to simplify UI development using SWT (or RWT), jface and JCR. */ +public class JcrUiUtils { + + /** + * Centralizes management of updating property value. Among other to avoid + * infinite loop when the new value is the same as the ones that is already + * stored in JCR. + * + * @return true if the value as changed + */ + public static boolean setJcrProperty(Node node, String propName, + int propertyType, Object value) { + try { + switch (propertyType) { + case PropertyType.STRING: + if ("".equals((String) value) + && (!node.hasProperty(propName) || node + .hasProperty(propName) + && "".equals(node.getProperty(propName) + .getString()))) + // workaround the fact that the Text widget value cannot be + // set to null + return false; + else if (node.hasProperty(propName) + && node.getProperty(propName).getString() + .equals((String) value)) + // nothing changed yet + return false; + else { + node.setProperty(propName, (String) value); + return true; + } + case PropertyType.BOOLEAN: + if (node.hasProperty(propName) + && node.getProperty(propName).getBoolean() == (Boolean) value) + // nothing changed yet + return false; + else { + node.setProperty(propName, (Boolean) value); + return true; + } + case PropertyType.DATE: + if (node.hasProperty(propName) + && node.getProperty(propName).getDate() + .equals((Calendar) value)) + // nothing changed yet + return false; + else { + node.setProperty(propName, (Calendar) value); + return true; + } + case PropertyType.LONG: + Long lgValue = (Long) value; + + if (lgValue == null) + lgValue = 0L; + + if (node.hasProperty(propName) + && node.getProperty(propName).getLong() == lgValue) + // nothing changed yet + return false; + else { + node.setProperty(propName, lgValue); + return true; + } + + default: + throw new EclipseUiException("Unimplemented property save"); + } + } catch (RepositoryException re) { + throw new EclipseUiException("Unexpected error while setting property", + re); + } + } + + /** + * Creates a new selection adapter in order to provide sorting abitily on a + * SWT Table that display a row list + **/ + public static SelectionAdapter getRowSelectionAdapter(final int index, + final int propertyType, final String selectorName, + final String propertyName, final RowViewerComparator comparator, + final TableViewer viewer) { + SelectionAdapter selectionAdapter = new SelectionAdapter() { + private static final long serialVersionUID = -5738918304901437720L; + + @Override + public void widgetSelected(SelectionEvent e) { + Table table = viewer.getTable(); + comparator.setColumn(propertyType, selectorName, propertyName); + int dir = table.getSortDirection(); + if (table.getSortColumn() == table.getColumn(index)) { + dir = dir == SWT.UP ? SWT.DOWN : SWT.UP; + } else { + dir = SWT.DOWN; + } + table.setSortDirection(dir); + table.setSortColumn(table.getColumn(index)); + viewer.refresh(); + } + }; + return selectionAdapter; + } + + /** + * Creates a new selection adapter in order to provide sorting abitily on a + * swt table that display a row list + **/ + public static SelectionAdapter getNodeSelectionAdapter(final int index, + final int propertyType, final String propertyName, + final NodeViewerComparator comparator, final TableViewer viewer) { + SelectionAdapter selectionAdapter = new SelectionAdapter() { + private static final long serialVersionUID = -1683220869195484625L; + + @Override + public void widgetSelected(SelectionEvent e) { + Table table = viewer.getTable(); + comparator.setColumn(propertyType, propertyName); + int dir = table.getSortDirection(); + if (table.getSortColumn() == table.getColumn(index)) { + dir = dir == SWT.UP ? SWT.DOWN : SWT.UP; + } else { + dir = SWT.DOWN; + } + table.setSortDirection(dir); + table.setSortColumn(table.getColumn(index)); + viewer.refresh(); + } + }; + return selectionAdapter; + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodeColumnLabelProvider.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodeColumnLabelProvider.java new file mode 100644 index 0000000..7e12bec --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodeColumnLabelProvider.java @@ -0,0 +1,123 @@ +package org.argeo.eclipse.ui.jcr; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.Image; + +/** Simplifies writing JCR-based column label provider. */ +public class NodeColumnLabelProvider extends ColumnLabelProvider { + private static final long serialVersionUID = -6586692836928505358L; + + protected String getNodeText(Node node) throws RepositoryException { + return super.getText(node); + } + + protected String getNodeToolTipText(Node node) throws RepositoryException { + return super.getToolTipText(node); + } + + protected Image getNodeImage(Node node) throws RepositoryException { + return super.getImage(node); + } + + protected Font getNodeFont(Node node) throws RepositoryException { + return super.getFont(node); + } + + public Color getNodeBackground(Node node) throws RepositoryException { + return super.getBackground(node); + } + + public Color getNodeForeground(Node node) throws RepositoryException { + return super.getForeground(node); + } + + @Override + public String getText(Object element) { + try { + if (element instanceof Node) + return getNodeText((Node) element); + else if (element instanceof NodeElement) + return getNodeText(((NodeElement) element).getNode()); + else + throw new IllegalArgumentException("Unsupported element type " + element.getClass()); + } catch (RepositoryException e) { + throw new IllegalStateException("Repository exception when accessing " + element, e); + } + } + + @Override + public Image getImage(Object element) { + try { + if (element instanceof Node) + return getNodeImage((Node) element); + else if (element instanceof NodeElement) + return getNodeImage(((NodeElement) element).getNode()); + else + throw new IllegalArgumentException("Unsupported element type " + element.getClass()); + } catch (RepositoryException e) { + throw new IllegalStateException("Repository exception when accessing " + element, e); + } + } + + @Override + public String getToolTipText(Object element) { + try { + if (element instanceof Node) + return getNodeToolTipText((Node) element); + else if (element instanceof NodeElement) + return getNodeToolTipText(((NodeElement) element).getNode()); + else + throw new IllegalArgumentException("Unsupported element type " + element.getClass()); + } catch (RepositoryException e) { + throw new IllegalStateException("Repository exception when accessing " + element, e); + } + } + + @Override + public Font getFont(Object element) { + try { + if (element instanceof Node) + return getNodeFont((Node) element); + else if (element instanceof NodeElement) + return getNodeFont(((NodeElement) element).getNode()); + else + throw new IllegalArgumentException("Unsupported element type " + element.getClass()); + } catch (RepositoryException e) { + throw new IllegalStateException("Repository exception when accessing " + element, e); + } + } + + @Override + public Color getBackground(Object element) { + try { + if (element instanceof Node) + return getNodeBackground((Node) element); + else if (element instanceof NodeElement) + return getNodeBackground(((NodeElement) element).getNode()); + else + throw new IllegalArgumentException("Unsupported element type " + element.getClass()); + } catch (RepositoryException e) { + throw new IllegalStateException("Repository exception when accessing " + element, e); + } + } + + @Override + public Color getForeground(Object element) { + try { + if (element instanceof Node) + return getNodeForeground((Node) element); + else if (element instanceof NodeElement) + return getNodeForeground(((NodeElement) element).getNode()); + else + throw new IllegalArgumentException("Unsupported element type " + element.getClass()); + } catch (RepositoryException e) { + throw new IllegalStateException("Repository exception when accessing " + element, e); + } + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodeElement.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodeElement.java new file mode 100644 index 0000000..787c92e --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodeElement.java @@ -0,0 +1,8 @@ +package org.argeo.eclipse.ui.jcr; + +import javax.jcr.Node; + +/** An element which is related to a JCR {@link Node}. */ +public interface NodeElement { + Node getNode(); +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodeElementComparer.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodeElementComparer.java new file mode 100644 index 0000000..2f3d64d --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodeElementComparer.java @@ -0,0 +1,36 @@ +package org.argeo.eclipse.ui.jcr; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.eclipse.ui.EclipseUiException; +import org.eclipse.jface.viewers.IElementComparer; + +/** Element comparer for JCR node, to be used in JFace viewers. */ +public class NodeElementComparer implements IElementComparer { + + public boolean equals(Object a, Object b) { + try { + if ((a instanceof Node) && (b instanceof Node)) { + Node nodeA = (Node) a; + Node nodeB = (Node) b; + return nodeA.getIdentifier().equals(nodeB.getIdentifier()); + } else { + return a.equals(b); + } + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot compare nodes", e); + } + } + + public int hashCode(Object element) { + try { + if (element instanceof Node) + return ((Node) element).getIdentifier().hashCode(); + return element.hashCode(); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot get hash code", e); + } + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodesWrapper.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodesWrapper.java new file mode 100644 index 0000000..2f808a5 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodesWrapper.java @@ -0,0 +1,71 @@ +package org.argeo.eclipse.ui.jcr; + +import java.util.ArrayList; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.RepositoryException; + +import org.argeo.eclipse.ui.EclipseUiException; + +/** + * Element of tree which is based on a node, but whose children are not + * necessarily this node children. + */ +public class NodesWrapper { + private final Node node; + + public NodesWrapper(Node node) { + super(); + this.node = node; + } + + protected NodeIterator getNodeIterator() throws RepositoryException { + return node.getNodes(); + } + + protected List getWrappedNodes() throws RepositoryException { + List nodes = new ArrayList(); + for (NodeIterator nit = getNodeIterator(); nit.hasNext();) + nodes.add(new WrappedNode(this, nit.nextNode())); + return nodes; + } + + public Object[] getChildren() { + try { + return getWrappedNodes().toArray(); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot get wrapped children", e); + } + } + + /** + * @return true by default because we don't want to compute the wrapped + * nodes twice + */ + public Boolean hasChildren() { + return true; + } + + public Node getNode() { + return node; + } + + @Override + public int hashCode() { + return node.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof NodesWrapper) + return node.equals(((NodesWrapper) obj).getNode()); + else + return false; + } + + public String toString() { + return "nodes wrapper based on " + node; + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/QueryTableContentProvider.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/QueryTableContentProvider.java new file mode 100644 index 0000000..934fa67 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/QueryTableContentProvider.java @@ -0,0 +1,35 @@ +package org.argeo.eclipse.ui.jcr; + +import javax.jcr.NodeIterator; +import javax.jcr.RepositoryException; +import javax.jcr.query.Query; + +import org.argeo.jcr.JcrException; +import org.argeo.jcr.JcrUtils; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.Viewer; + +/** Content provider based on a JCR {@link Query}. */ +public class QueryTableContentProvider implements IStructuredContentProvider { + private static final long serialVersionUID = 760371460907204722L; + + @Override + public void dispose() { + } + + @Override + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + + @Override + public Object[] getElements(Object inputElement) { + Query query = (Query) inputElement; + try { + NodeIterator nit = query.execute().getNodes(); + return JcrUtils.nodeIteratorToList(nit).toArray(); + } catch (RepositoryException e) { + throw new JcrException(e); + } + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/RowColumnLabelProvider.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/RowColumnLabelProvider.java new file mode 100644 index 0000000..70c71ef --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/RowColumnLabelProvider.java @@ -0,0 +1,111 @@ +package org.argeo.eclipse.ui.jcr; + +import javax.jcr.RepositoryException; +import javax.jcr.query.Row; + +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.Image; + +/** Simplifies writing JCR-based column label provider. */ +public class RowColumnLabelProvider extends ColumnLabelProvider { + private static final long serialVersionUID = -6586692836928505358L; + + protected String getRowText(Row row) throws RepositoryException { + return super.getText(row); + } + + protected String getRowToolTipText(Row row) throws RepositoryException { + return super.getToolTipText(row); + } + + protected Image getRowImage(Row row) throws RepositoryException { + return super.getImage(row); + } + + protected Font getRowFont(Row row) throws RepositoryException { + return super.getFont(row); + } + + public Color getRowBackground(Row row) throws RepositoryException { + return super.getBackground(row); + } + + public Color getRowForeground(Row row) throws RepositoryException { + return super.getForeground(row); + } + + @Override + public String getText(Object element) { + try { + if (element instanceof Row) + return getRowText((Row) element); + else + throw new IllegalArgumentException("Unsupported element type " + element.getClass()); + } catch (RepositoryException e) { + throw new IllegalStateException("Repository exception when accessing " + element, e); + } + } + + @Override + public Image getImage(Object element) { + try { + if (element instanceof Row) + return getRowImage((Row) element); + else + throw new IllegalArgumentException("Unsupported element type " + element.getClass()); + } catch (RepositoryException e) { + throw new IllegalStateException("Repository exception when accessing " + element, e); + } + } + + @Override + public String getToolTipText(Object element) { + try { + if (element instanceof Row) + return getRowToolTipText((Row) element); + else + throw new IllegalArgumentException("Unsupported element type " + element.getClass()); + } catch (RepositoryException e) { + throw new IllegalStateException("Repository exception when accessing " + element, e); + } + } + + @Override + public Font getFont(Object element) { + try { + if (element instanceof Row) + return getRowFont((Row) element); + else + throw new IllegalArgumentException("Unsupported element type " + element.getClass()); + } catch (RepositoryException e) { + throw new IllegalStateException("Repository exception when accessing " + element, e); + } + } + + @Override + public Color getBackground(Object element) { + try { + if (element instanceof Row) + return getRowBackground((Row) element); + else + throw new IllegalArgumentException("Unsupported element type " + element.getClass()); + } catch (RepositoryException e) { + throw new IllegalStateException("Repository exception when accessing " + element, e); + } + } + + @Override + public Color getForeground(Object element) { + try { + if (element instanceof Row) + return getRowForeground((Row) element); + else + throw new IllegalArgumentException("Unsupported element type " + element.getClass()); + } catch (RepositoryException e) { + throw new IllegalStateException("Repository exception when accessing " + element, e); + } + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/SimpleNodeContentProvider.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/SimpleNodeContentProvider.java new file mode 100644 index 0000000..cb235d7 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/SimpleNodeContentProvider.java @@ -0,0 +1,59 @@ +package org.argeo.eclipse.ui.jcr; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.jcr.JcrUtils; + +/** Simple JCR node content provider taking a list of String as base path. */ +public class SimpleNodeContentProvider extends AbstractNodeContentProvider { + private static final long serialVersionUID = -8245193308831384269L; + private final List basePaths; + private Boolean mkdirs = false; + + public SimpleNodeContentProvider(Session session, String... basePaths) { + this(session, Arrays.asList(basePaths)); + } + + public SimpleNodeContentProvider(Session session, List basePaths) { + super(session); + this.basePaths = basePaths; + } + + @Override + protected Boolean isBasePath(String path) { + if (basePaths.contains(path)) + return true; + return super.isBasePath(path); + } + + public Object[] getElements(Object inputElement) { + try { + List baseNodes = new ArrayList(); + for (String basePath : basePaths) + if (mkdirs && !getSession().itemExists(basePath)) + baseNodes.add(JcrUtils.mkdirs(getSession(), basePath)); + else + baseNodes.add(getSession().getNode(basePath)); + return baseNodes.toArray(); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot get base nodes for " + basePaths, + e); + } + } + + public List getBasePaths() { + return basePaths; + } + + public void setMkdirs(Boolean mkdirs) { + this.mkdirs = mkdirs; + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/VersionColumnLabelProvider.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/VersionColumnLabelProvider.java new file mode 100644 index 0000000..1ce3154 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/VersionColumnLabelProvider.java @@ -0,0 +1,80 @@ +package org.argeo.eclipse.ui.jcr; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.RepositoryException; +import javax.jcr.version.Version; + +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.swt.graphics.Image; + +/** Simplifies writing JCR-based column label provider. */ +public class VersionColumnLabelProvider extends ColumnLabelProvider { + private static final long serialVersionUID = -6117690082313161159L; + + protected String getVersionText(Version version) throws RepositoryException { + return super.getText(version); + } + + protected String getVersionToolTipText(Version version) throws RepositoryException { + return super.getToolTipText(version); + } + + protected Image getVersionImage(Version version) throws RepositoryException { + return super.getImage(version); + } + + protected String getUserName(Version version) throws RepositoryException { + Node node = version.getFrozenNode(); + if(node.hasProperty(Property.JCR_LAST_MODIFIED_BY)) + return node.getProperty(Property.JCR_LAST_MODIFIED_BY).getString(); + if(node.hasProperty(Property.JCR_CREATED_BY)) + return node.getProperty(Property.JCR_CREATED_BY).getString(); + return null; + } + +// protected String getActivityTitle(Version version) throws RepositoryException { +// Node activity = getActivity(version); +// if (activity == null) +// return null; +// if (activity.hasProperty("jcr:activityTitle")) +// return activity.getProperty("jcr:activityTitle").getString(); +// else +// return activity.getName(); +// } +// +// protected Node getActivity(Version version) throws RepositoryException { +// if (version.hasProperty(Property.JCR_ACTIVITY)) { +// return version.getProperty(Property.JCR_ACTIVITY).getNode(); +// } else +// return null; +// } + + @Override + public String getText(Object element) { + try { + return getVersionText((Version) element); + } catch (RepositoryException e) { + throw new RuntimeException("Runtime repository exception when accessing " + element, e); + } + } + + @Override + public Image getImage(Object element) { + try { + return getVersionImage((Version) element); + } catch (RepositoryException e) { + throw new RuntimeException("Runtime repository exception when accessing " + element, e); + } + } + + @Override + public String getToolTipText(Object element) { + try { + return getVersionToolTipText((Version) element); + } catch (RepositoryException e) { + throw new RuntimeException("Runtime repository exception when accessing " + element, e); + } + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/VersionHistoryContentProvider.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/VersionHistoryContentProvider.java new file mode 100644 index 0000000..32e5d30 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/VersionHistoryContentProvider.java @@ -0,0 +1,27 @@ +package org.argeo.eclipse.ui.jcr; + +import javax.jcr.version.VersionHistory; + +import org.argeo.jcr.Jcr; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.Viewer; + +/** Content provider based on a {@link VersionHistory}. */ +public class VersionHistoryContentProvider implements IStructuredContentProvider { + private static final long serialVersionUID = -4921107883428887012L; + + @Override + public void dispose() { + } + + @Override + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + + @Override + public Object[] getElements(Object inputElement) { + VersionHistory versionHistory = (VersionHistory) inputElement; + return Jcr.getLinearVersions(versionHistory).toArray(); + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/WrappedNode.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/WrappedNode.java new file mode 100644 index 0000000..43df1fe --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/WrappedNode.java @@ -0,0 +1,41 @@ +package org.argeo.eclipse.ui.jcr; + +import javax.jcr.Node; + +/** Wrap a node (created from a {@link NodesWrapper}) */ +public class WrappedNode { + private final NodesWrapper parent; + private final Node node; + + public WrappedNode(NodesWrapper parent, Node node) { + super(); + this.parent = parent; + this.node = node; + } + + public NodesWrapper getParent() { + return parent; + } + + public Node getNode() { + return node; + } + + public String toString() { + return "wrapped " + node; + } + + @Override + public int hashCode() { + return node.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof WrappedNode) + return node.equals(((WrappedNode) obj).getNode()); + else + return false; + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/JcrColumnDefinition.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/JcrColumnDefinition.java new file mode 100644 index 0000000..c5dd733 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/JcrColumnDefinition.java @@ -0,0 +1,116 @@ +package org.argeo.eclipse.ui.jcr.lists; + +import javax.jcr.Node; +import javax.jcr.query.Row; + +import org.argeo.eclipse.ui.ColumnDefinition; + +/** + * Utility object to manage column in various tables and extracts displaying + * data from JCR + */ +public class JcrColumnDefinition extends ColumnDefinition { + private final static int DEFAULT_COLUMN_SIZE = 120; + + private String selectorName; + private String propertyName; + private int propertyType; + private int columnSize; + + /** + * Use this kind of columns to configure a table that displays JCR + * {@link Row} + * + * @param selectorName + * @param propertyName + * @param propertyType + * @param headerLabel + */ + public JcrColumnDefinition(String selectorName, String propertyName, + int propertyType, String headerLabel) { + super(new SimpleJcrRowLabelProvider(selectorName, propertyName), + headerLabel); + this.selectorName = selectorName; + this.propertyName = propertyName; + this.propertyType = propertyType; + this.columnSize = DEFAULT_COLUMN_SIZE; + } + + /** + * Use this kind of columns to configure a table that displays JCR + * {@link Row} + * + * @param selectorName + * @param propertyName + * @param propertyType + * @param headerLabel + * @param columnSize + */ + public JcrColumnDefinition(String selectorName, String propertyName, + int propertyType, String headerLabel, int columnSize) { + super(new SimpleJcrRowLabelProvider(selectorName, propertyName), + headerLabel, columnSize); + this.selectorName = selectorName; + this.propertyName = propertyName; + this.propertyType = propertyType; + this.columnSize = columnSize; + } + + /** + * Use this kind of columns to configure a table that displays JCR + * {@link Node} + * + * @param propertyName + * @param propertyType + * @param headerLabel + * @param columnSize + */ + public JcrColumnDefinition(String propertyName, int propertyType, + String headerLabel, int columnSize) { + super(new SimpleJcrNodeLabelProvider(propertyName), headerLabel, + columnSize); + this.propertyName = propertyName; + this.propertyType = propertyType; + this.columnSize = columnSize; + } + + public String getSelectorName() { + return selectorName; + } + + public void setSelectorName(String selectorName) { + this.selectorName = selectorName; + } + + public String getPropertyName() { + return propertyName; + } + + public void setPropertyName(String propertyName) { + this.propertyName = propertyName; + } + + public int getPropertyType() { + return propertyType; + } + + public void setPropertyType(int propertyType) { + this.propertyType = propertyType; + } + + public int getColumnSize() { + return columnSize; + } + + public void setColumnSize(int columnSize) { + this.columnSize = columnSize; + } + + public String getHeaderLabel() { + return super.getLabel(); + } + + public void setHeaderLabel(String headerLabel) { + super.setLabel(headerLabel); + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/NodeViewerComparator.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/NodeViewerComparator.java new file mode 100644 index 0000000..d990460 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/NodeViewerComparator.java @@ -0,0 +1,190 @@ +package org.argeo.eclipse.ui.jcr.lists; + +import java.math.BigDecimal; +import java.util.Calendar; + +import javax.jcr.Node; +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; +import javax.jcr.Value; +import javax.jcr.ValueFormatException; + +import org.argeo.eclipse.ui.EclipseUiException; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerComparator; + +/** + * Base comparator to enable ordering on Table or Tree viewer that display Jcr + * Nodes. + * + * Note that the following snippet must be added before setting the comparator + * to the corresponding control: + * // IMPORTANT: initialize comparator before setting it + * JcrColumnDefinition firstCol = colDefs.get(0); + * comparator.setColumn(firstCol.getPropertyType(), + * firstCol.getPropertyName()); + * viewer.setComparator(comparator); + */ +public class NodeViewerComparator extends ViewerComparator { + private static final long serialVersionUID = -7782916140737279027L; + + protected String propertyName; + + protected int propertyType; + public static final int ASCENDING = 0, DESCENDING = 1; + protected int direction = DESCENDING; + + public NodeViewerComparator() { + } + + /** + * e1 and e2 must both be Jcr nodes. + * + * @param viewer + * @param e1 + * @param e2 + * @return the comparison + */ + @Override + public int compare(Viewer viewer, Object e1, Object e2) { + int rc = 0; + long lc = 0; + + try { + Node n1 = (Node) e1; + Node n2 = (Node) e2; + + Value v1 = null; + Value v2 = null; + if (n1.hasProperty(propertyName)) + v1 = n1.getProperty(propertyName).getValue(); + if (n2.hasProperty(propertyName)) + v2 = n2.getProperty(propertyName).getValue(); + + if (v2 == null && v1 == null) + return 0; + else if (v2 == null) + return -1; + else if (v1 == null) + return 1; + + switch (propertyType) { + case PropertyType.STRING: + rc = v1.getString().compareTo(v2.getString()); + break; + case PropertyType.BOOLEAN: + boolean b1 = v1.getBoolean(); + boolean b2 = v2.getBoolean(); + if (b1 == b2) + rc = 0; + else + // we assume true is greater than false + rc = b1 ? 1 : -1; + break; + case PropertyType.DATE: + Calendar c1 = v1.getDate(); + Calendar c2 = v2.getDate(); + if (c1 == null || c2 == null) + // log.trace("undefined date"); + ; + lc = c1.getTimeInMillis() - c2.getTimeInMillis(); + if (lc < Integer.MIN_VALUE) + rc = -1; + else if (lc > Integer.MAX_VALUE) + rc = 1; + else + rc = (int) lc; + break; + case PropertyType.LONG: + long l1; + long l2; + // TODO Sometimes an empty string is set instead of a long + try { + l1 = v1.getLong(); + } catch (ValueFormatException ve) { + l1 = 0; + } + try { + l2 = v2.getLong(); + } catch (ValueFormatException ve) { + l2 = 0; + } + + lc = l1 - l2; + if (lc < Integer.MIN_VALUE) + rc = -1; + else if (lc > Integer.MAX_VALUE) + rc = 1; + else + rc = (int) lc; + break; + case PropertyType.DECIMAL: + BigDecimal bd1 = v1.getDecimal(); + BigDecimal bd2 = v2.getDecimal(); + rc = bd1.compareTo(bd2); + break; + case PropertyType.DOUBLE: + Double d1 = v1.getDouble(); + Double d2 = v2.getDouble(); + rc = d1.compareTo(d2); + break; + default: + throw new EclipseUiException( + "Unimplemented comparaison for PropertyType " + + propertyType); + } + // If descending order, flip the direction + if (direction == DESCENDING) { + rc = -rc; + } + + } catch (RepositoryException re) { + throw new EclipseUiException("Unexpected error " + + "while comparing nodes", re); + } + return rc; + } + + /** + * @param propertyType + * Corresponding JCR type + * @param propertyName + * name of the property to use. + */ + public void setColumn(int propertyType, String propertyName) { + if (this.propertyName != null && this.propertyName.equals(propertyName)) { + // Same column as last sort; toggle the direction + direction = 1 - direction; + } else { + // New column; do an ascending sort + this.propertyType = propertyType; + this.propertyName = propertyName; + direction = ASCENDING; + } + } + + // Getters and setters + protected String getPropertyName() { + return propertyName; + } + + protected void setPropertyName(String propertyName) { + this.propertyName = propertyName; + } + + protected int getPropertyType() { + return propertyType; + } + + protected void setPropertyType(int propertyType) { + this.propertyType = propertyType; + } + + protected int getDirection() { + return direction; + } + + protected void setDirection(int direction) { + this.direction = direction; + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/RowViewerComparator.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/RowViewerComparator.java new file mode 100644 index 0000000..60d637d --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/RowViewerComparator.java @@ -0,0 +1,62 @@ +package org.argeo.eclipse.ui.jcr.lists; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.query.Row; + +import org.argeo.eclipse.ui.EclipseUiException; +import org.eclipse.jface.viewers.Viewer; + +/** + * Base comparator to enable ordering on Table or Tree viewer that display Jcr + * rows + */ +public class RowViewerComparator extends NodeViewerComparator { + private static final long serialVersionUID = 7020939505172625113L; + protected String selectorName; + + public RowViewerComparator() { + } + + /** + * e1 and e2 must both be Jcr rows. + * + * @param viewer + * @param e1 + * @param e2 + * @return the comparison + */ + @Override + public int compare(Viewer viewer, Object e1, Object e2) { + try { + Node n1 = ((Row) e1).getNode(selectorName); + Node n2 = ((Row) e2).getNode(selectorName); + return super.compare(viewer, n1, n2); + } catch (RepositoryException re) { + throw new EclipseUiException("Unexpected error " + + "while comparing nodes", re); + } + } + + /** + * @param propertyType + * Corresponding JCR type + * @param propertyName + * name of the property to use. + */ + public void setColumn(int propertyType, String selectorName, + String propertyName) { + if (this.selectorName != null && getPropertyName() != null + && this.selectorName.equals(selectorName) + && this.getPropertyName().equals(propertyName)) { + // Same column as last sort; toggle the direction + setDirection(1 - getDirection()); + } else { + // New column; do a descending sort + setPropertyType(propertyType); + setPropertyName(propertyName); + this.selectorName = selectorName; + setDirection(NodeViewerComparator.ASCENDING); + } + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/SimpleJcrNodeLabelProvider.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/SimpleJcrNodeLabelProvider.java new file mode 100644 index 0000000..aa2e337 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/SimpleJcrNodeLabelProvider.java @@ -0,0 +1,120 @@ +package org.argeo.eclipse.ui.jcr.lists; + +import java.text.DateFormat; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.text.SimpleDateFormat; + +import javax.jcr.Node; +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; +import javax.jcr.Value; + +import org.argeo.eclipse.ui.EclipseUiException; +import org.eclipse.jface.viewers.ColumnLabelProvider; + +/** Base implementation of a label provider for controls that display JCR Nodes */ +public class SimpleJcrNodeLabelProvider extends ColumnLabelProvider { + private static final long serialVersionUID = -5215787695436221993L; + + private final static String DEFAULT_DATE_FORMAT = "EEE, dd MMM yyyy"; + private final static String DEFAULT_NUMBER_FORMAT = "#,##0.0"; + + private DateFormat dateFormat; + private NumberFormat numberFormat; + + final private String propertyName; + + /** + * Default Label provider for a given property of a node. Using default + * pattern for date and number formating + */ + public SimpleJcrNodeLabelProvider(String propertyName) { + this.propertyName = propertyName; + dateFormat = new SimpleDateFormat(DEFAULT_DATE_FORMAT); + numberFormat = DecimalFormat.getInstance(); + ((DecimalFormat) numberFormat).applyPattern(DEFAULT_NUMBER_FORMAT); + } + + /** + * Label provider for a given property of a node optionally precising date + * and/or number format patterns + */ + public SimpleJcrNodeLabelProvider(String propertyName, + String dateFormatPattern, String numberFormatPattern) { + this.propertyName = propertyName; + dateFormat = new SimpleDateFormat( + dateFormatPattern == null ? DEFAULT_DATE_FORMAT + : dateFormatPattern); + numberFormat = DecimalFormat.getInstance(); + ((DecimalFormat) numberFormat) + .applyPattern(numberFormatPattern == null ? DEFAULT_NUMBER_FORMAT + : numberFormatPattern); + } + + @Override + public String getText(Object element) { + try { + Node currNode = (Node) element; + + if (currNode.hasProperty(propertyName)) { + if (currNode.getProperty(propertyName).isMultiple()) { + StringBuilder builder = new StringBuilder(); + for (Value value : currNode.getProperty(propertyName) + .getValues()) { + String currStr = getSingleValueAsString(value); + if (notEmptyString(currStr)) + builder.append(currStr).append("; "); + } + if (builder.length() > 0) + builder.deleteCharAt(builder.length() - 2); + + return builder.toString(); + } else + return getSingleValueAsString(currNode.getProperty( + propertyName).getValue()); + } else + return ""; + } catch (RepositoryException re) { + throw new EclipseUiException("Unable to get text from row", re); + } + } + + private String getSingleValueAsString(Value value) + throws RepositoryException { + switch (value.getType()) { + case PropertyType.STRING: + return value.getString(); + case PropertyType.BOOLEAN: + return "" + value.getBoolean(); + case PropertyType.DATE: + return dateFormat.format(value.getDate().getTime()); + case PropertyType.LONG: + return "" + value.getLong(); + case PropertyType.DECIMAL: + return numberFormat.format(value.getDecimal()); + case PropertyType.DOUBLE: + return numberFormat.format(value.getDouble()); + case PropertyType.NAME: + return value.getString(); + default: + throw new EclipseUiException("Unimplemented label provider " + + "for property type " + value.getType() + + " while getting property " + propertyName + " - value: " + + value.getString()); + + } + } + + private boolean notEmptyString(String string) { + return string != null && !"".equals(string.trim()); + } + + public void setDateFormat(String dateFormatPattern) { + dateFormat = new SimpleDateFormat(dateFormatPattern); + } + + public void setNumberFormat(String numberFormatPattern) { + ((DecimalFormat) numberFormat).applyPattern(numberFormatPattern); + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/SimpleJcrRowLabelProvider.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/SimpleJcrRowLabelProvider.java new file mode 100644 index 0000000..5d421f6 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/SimpleJcrRowLabelProvider.java @@ -0,0 +1,47 @@ +package org.argeo.eclipse.ui.jcr.lists; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.query.Row; + +import org.argeo.eclipse.ui.EclipseUiException; + +/** + * Base implementation of a label provider for widgets that display JCR Rows. + */ +public class SimpleJcrRowLabelProvider extends SimpleJcrNodeLabelProvider { + private static final long serialVersionUID = -3414654948197181740L; + + final private String selectorName; + + /** + * Default Label provider for a given property of a row. Using default + * pattern for date and number formating + */ + public SimpleJcrRowLabelProvider(String selectorName, String propertyName) { + super(propertyName); + this.selectorName = selectorName; + } + + /** + * Label provider for a given property of a node optionally precising date + * and/or number format patterns + */ + public SimpleJcrRowLabelProvider(String selectorName, String propertyName, + String dateFormatPattern, String numberFormatPattern) { + super(propertyName, dateFormatPattern, numberFormatPattern); + this.selectorName = selectorName; + } + + @Override + public String getText(Object element) { + try { + Row currRow = (Row) element; + Node currNode = currRow.getNode(selectorName); + return super.getText(currNode); + } catch (RepositoryException re) { + throw new EclipseUiException("Unable to get Node " + selectorName + + " from row " + element, re); + } + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/package-info.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/package-info.java new file mode 100644 index 0000000..3678aab --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/package-info.java @@ -0,0 +1,2 @@ +/** Generic SWT/JFace JCR utilities for lists. */ +package org.argeo.eclipse.ui.jcr.lists; \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/package-info.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/package-info.java new file mode 100644 index 0000000..19e3cc3 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/package-info.java @@ -0,0 +1,2 @@ +/** Generic SWT/JFace JCR utilities. */ +package org.argeo.eclipse.ui.jcr; \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/JcrFileProvider.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/JcrFileProvider.java new file mode 100644 index 0000000..c82e666 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/JcrFileProvider.java @@ -0,0 +1,129 @@ +package org.argeo.eclipse.ui.jcr.util; + +import java.io.InputStream; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.RepositoryException; +import javax.jcr.nodetype.NodeType; + +import org.apache.commons.io.IOUtils; +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.eclipse.ui.FileProvider; + +/** + * Implements a FileProvider for UI purposes. Note that it might not be very + * reliable as long as we have not fixed login and multi repository issues that + * will be addressed in the next version. + * + * NOTE: id used here is the real id of the JCR Node, not the JCR Path + * + * Relies on common approach for JCR file handling implementation. + * + */ +@SuppressWarnings("deprecation") +public class JcrFileProvider implements FileProvider { + + // private Object[] rootNodes; + private Node refNode; + + /** + * Must be set in order for the provider to be able to get current session + * and thus have the ability to get the file node corresponding to a given + * file ID + * + * @param refNode + */ + public void setReferenceNode(Node refNode) { + // FIXME : this introduces some concurrency ISSUES. + this.refNode = refNode; + } + + public byte[] getByteArrayFileFromId(String fileId) { + InputStream fis = null; + byte[] ba = null; + Node child = getFileNodeFromId(fileId); + try { + fis = (InputStream) child.getProperty(Property.JCR_DATA).getBinary().getStream(); + ba = IOUtils.toByteArray(fis); + + } catch (Exception e) { + throw new EclipseUiException("Stream error while opening file", e); + } finally { + IOUtils.closeQuietly(fis); + } + return ba; + } + + public InputStream getInputStreamFromFileId(String fileId) { + try { + InputStream fis = null; + + Node child = getFileNodeFromId(fileId); + fis = (InputStream) child.getProperty(Property.JCR_DATA).getBinary().getStream(); + return fis; + } catch (RepositoryException re) { + throw new EclipseUiException("Cannot get stream from file node for Id " + fileId, re); + } + } + + /** + * Throws an exception if the node is not found in the current repository (a + * bit like a FileNotFoundException) + * + * @param fileId + * @return Returns the child node of the nt:file node. It is the child node + * that have the jcr:data property where actual file is stored. + * never null + */ + private Node getFileNodeFromId(String fileId) { + try { + Node result = refNode.getSession().getNodeByIdentifier(fileId); + + // rootNodes: for (int j = 0; j < rootNodes.length; j++) { + // // in case we have a classic JCR Node + // if (rootNodes[j] instanceof Node) { + // Node curNode = (Node) rootNodes[j]; + // if (result != null) + // break rootNodes; + // } // Case of a repository Node + // else if (rootNodes[j] instanceof RepositoryNode) { + // Object[] nodes = ((RepositoryNode) rootNodes[j]) + // .getChildren(); + // for (int i = 0; i < nodes.length; i++) { + // Node node = (Node) nodes[i]; + // result = node.getSession().getNodeByIdentifier(fileId); + // if (result != null) + // break rootNodes; + // } + // } + // } + + // Sanity checks + if (result == null) + throw new EclipseUiException("File node not found for ID" + fileId); + + Node child = null; + + boolean isValid = true; + if (!result.isNodeType(NodeType.NT_FILE)) + // useless: mandatory child node + // || !result.hasNode(Property.JCR_CONTENT)) + isValid = false; + else { + child = result.getNode(Property.JCR_CONTENT); + if (!(child.isNodeType(NodeType.NT_RESOURCE) || child.hasProperty(Property.JCR_DATA))) + isValid = false; + } + + if (!isValid) + throw new EclipseUiException("ERROR: In the current implemented model, '" + NodeType.NT_FILE + + "' file node must have a child node named jcr:content " + + "that has a BINARY Property named jcr:data " + "where the actual data is stored"); + return child; + + } catch (RepositoryException re) { + throw new EclipseUiException("Erreur while getting file node of ID " + fileId, re); + } + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/JcrItemsComparator.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/JcrItemsComparator.java new file mode 100644 index 0000000..fb12399 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/JcrItemsComparator.java @@ -0,0 +1,21 @@ +package org.argeo.eclipse.ui.jcr.util; + +import java.util.Comparator; + +import javax.jcr.Item; +import javax.jcr.RepositoryException; + +import org.argeo.eclipse.ui.EclipseUiException; + +/** Compares two JCR items (node or properties) based on their names. */ +public class JcrItemsComparator implements Comparator { + public int compare(Item o1, Item o2) { + try { + // TODO: put folder before files + return o1.getName().toLowerCase().compareTo(o2.getName().toLowerCase()); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot compare " + o1 + " and " + o2, e); + } + } + +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/NodeViewerComparer.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/NodeViewerComparer.java new file mode 100644 index 0000000..54b795f --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/NodeViewerComparer.java @@ -0,0 +1,36 @@ +package org.argeo.eclipse.ui.jcr.util; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.eclipse.ui.EclipseUiException; +import org.eclipse.jface.viewers.IElementComparer; + +/** Compare JCR nodes based on their JCR identifiers, for use in JFace viewers. */ +public class NodeViewerComparer implements IElementComparer { + + // force comparison on Node IDs only. + public boolean equals(Object elementA, Object elementB) { + if (!(elementA instanceof Node) || !(elementB instanceof Node)) { + return elementA == null ? elementB == null : elementA + .equals(elementB); + } else { + + boolean result = false; + try { + String idA = ((Node) elementA).getIdentifier(); + String idB = ((Node) elementB).getIdentifier(); + result = idA == null ? idB == null : idA.equals(idB); + } catch (RepositoryException re) { + throw new EclipseUiException("cannot compare nodes", re); + } + + return result; + } + } + + public int hashCode(Object element) { + // TODO enhanced this method. + return element.getClass().toString().hashCode(); + } +} \ No newline at end of file diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/SingleSessionFileProvider.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/SingleSessionFileProvider.java new file mode 100644 index 0000000..291d579 --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/SingleSessionFileProvider.java @@ -0,0 +1,98 @@ +package org.argeo.eclipse.ui.jcr.util; + +import java.io.InputStream; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.nodetype.NodeType; + +import org.apache.commons.io.IOUtils; +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.eclipse.ui.FileProvider; + +/** + * Implements a FileProvider for UI purposes. Unlike the + * JcrFileProvider , it relies on a single session and manages + * nodes with path only. + * + * Note that considered id is the JCR path + * + * Relies on common approach for JCR file handling implementation. + */ +@SuppressWarnings("deprecation") +public class SingleSessionFileProvider implements FileProvider { + + private Session session; + + public SingleSessionFileProvider(Session session) { + this.session = session; + } + + public byte[] getByteArrayFileFromId(String fileId) { + InputStream fis = null; + byte[] ba = null; + Node child = getFileNodeFromId(fileId); + try { + fis = (InputStream) child.getProperty(Property.JCR_DATA) + .getBinary().getStream(); + ba = IOUtils.toByteArray(fis); + + } catch (Exception e) { + throw new EclipseUiException("Stream error while opening file", e); + } finally { + IOUtils.closeQuietly(fis); + } + return ba; + } + + public InputStream getInputStreamFromFileId(String fileId) { + try { + InputStream fis = null; + + Node child = getFileNodeFromId(fileId); + fis = (InputStream) child.getProperty(Property.JCR_DATA) + .getBinary().getStream(); + return fis; + } catch (RepositoryException re) { + throw new EclipseUiException("Cannot get stream from file node for Id " + + fileId, re); + } + } + + /** + * + * @param fileId + * @return Returns the child node of the nt:file node. It is the child node + * that have the jcr:data property where actual file is stored. + * never null + */ + private Node getFileNodeFromId(String fileId) { + try { + Node result = null; + result = session.getNode(fileId); + + // Sanity checks + if (result == null) + throw new EclipseUiException("File node not found for ID" + fileId); + + // Ensure that the node have the correct type. + if (!result.isNodeType(NodeType.NT_FILE)) + throw new EclipseUiException( + "Cannot open file children Node that are not of " + + NodeType.NT_RESOURCE + " type."); + + Node child = result.getNodes().nextNode(); + if (child == null || !child.isNodeType(NodeType.NT_RESOURCE)) + throw new EclipseUiException( + "ERROR: IN the current implemented model, " + + NodeType.NT_FILE + + " file node must have one and only one child of the nt:ressource, where actual data is stored"); + return child; + } catch (RepositoryException re) { + throw new EclipseUiException("Erreur while getting file node of ID " + + fileId, re); + } + } +} diff --git a/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/package-info.java b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/package-info.java new file mode 100644 index 0000000..016348c --- /dev/null +++ b/swt/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/package-info.java @@ -0,0 +1,2 @@ +/** Generic SWT/JFace JCR helpers. */ +package org.argeo.eclipse.ui.jcr.util; \ No newline at end of file