Introduce content layer.
authorMathieu Baudier <mbaudier@argeo.org>
Wed, 5 May 2021 09:14:46 +0000 (11:14 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Wed, 5 May 2021 09:14:46 +0000 (11:14 +0200)
48 files changed:
core/org.argeo.entity.api/src/org/argeo/entity/EntityType.java
core/org.argeo.entity.api/src/org/argeo/entity/entity.cnd
core/org.argeo.suite.ui/OSGI-INF/leadPane.xml
core/org.argeo.suite.ui/src/org/argeo/suite/ui/widgets/TreeOrSearchArea.java [new file with mode: 0644]
library/org.argeo.documents.ui/.classpath [deleted file]
library/org.argeo.documents.ui/.gitignore [deleted file]
library/org.argeo.documents.ui/.project [deleted file]
library/org.argeo.documents.ui/META-INF/.gitignore [deleted file]
library/org.argeo.documents.ui/OSGI-INF/documentsFolder.xml [deleted file]
library/org.argeo.documents.ui/OSGI-INF/documentsLayer.xml [deleted file]
library/org.argeo.documents.ui/OSGI-INF/entryArea.xml [deleted file]
library/org.argeo.documents.ui/OSGI-INF/l10n/bundle.properties [deleted file]
library/org.argeo.documents.ui/bnd.bnd [deleted file]
library/org.argeo.documents.ui/build.properties [deleted file]
library/org.argeo.documents.ui/config/documentsFolder.properties [deleted file]
library/org.argeo.documents.ui/config/documentsLayer.properties [deleted file]
library/org.argeo.documents.ui/config/entryArea.properties [deleted file]
library/org.argeo.documents.ui/pom.xml [deleted file]
library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsContextMenu.java [deleted file]
library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsFileComposite.java [deleted file]
library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsFolderComposite.java [deleted file]
library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsFolderUiProvider.java [deleted file]
library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsTreeUiProvider.java [deleted file]
library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsUiService.java [deleted file]
library/org.argeo.library.ui/.classpath [new file with mode: 0644]
library/org.argeo.library.ui/.gitignore [new file with mode: 0644]
library/org.argeo.library.ui/.project [new file with mode: 0644]
library/org.argeo.library.ui/META-INF/.gitignore [new file with mode: 0644]
library/org.argeo.library.ui/OSGI-INF/contentEntryArea.xml [new file with mode: 0644]
library/org.argeo.library.ui/OSGI-INF/contentLayer.xml [new file with mode: 0644]
library/org.argeo.library.ui/OSGI-INF/documentsFolder.xml [new file with mode: 0644]
library/org.argeo.library.ui/OSGI-INF/fsEntryArea.xml [new file with mode: 0644]
library/org.argeo.library.ui/OSGI-INF/l10n/bundle.properties [new file with mode: 0644]
library/org.argeo.library.ui/bnd.bnd [new file with mode: 0644]
library/org.argeo.library.ui/build.properties [new file with mode: 0644]
library/org.argeo.library.ui/config/contentEntryArea.properties [new file with mode: 0644]
library/org.argeo.library.ui/config/contentLayer.properties [new file with mode: 0644]
library/org.argeo.library.ui/config/documentsFolder.properties [new file with mode: 0644]
library/org.argeo.library.ui/config/fsEntryArea.properties [new file with mode: 0644]
library/org.argeo.library.ui/pom.xml [new file with mode: 0644]
library/org.argeo.library.ui/src/org/argeo/library/ui/ContentEntryArea.java [new file with mode: 0644]
library/org.argeo.library.ui/src/org/argeo/library/ui/DocumentsContextMenu.java [new file with mode: 0644]
library/org.argeo.library.ui/src/org/argeo/library/ui/DocumentsFileComposite.java [new file with mode: 0644]
library/org.argeo.library.ui/src/org/argeo/library/ui/DocumentsFolderComposite.java [new file with mode: 0644]
library/org.argeo.library.ui/src/org/argeo/library/ui/DocumentsFolderUiProvider.java [new file with mode: 0644]
library/org.argeo.library.ui/src/org/argeo/library/ui/DocumentsTreeUiProvider.java [new file with mode: 0644]
library/org.argeo.library.ui/src/org/argeo/library/ui/DocumentsUiService.java [new file with mode: 0644]
library/pom.xml

index ecd6330fed6252be79b537ac1e6573535050eccd..84fa16fdf3a9c453a9a0b9b1415c204642b09e43 100644 (file)
@@ -4,6 +4,8 @@ package org.argeo.entity;
 public enum EntityType implements JcrName {
        // entity
        entity, local, relatedTo,
+       // structure
+       space,
        // typology
        typologies, terms, term,
        // form
index b64a275a116788f9a5e1fbdf0f83d164e445d07d..cd3f327fde8a0331176665190b489827ec092cb8 100644 (file)
@@ -35,6 +35,12 @@ mixin
 //+ * (entity:reference)
 //+ * (entity:composite)
 
+//
+// STRUCTURE
+//
+[entity:space]
+mixin
+
 //
 // TYPOLOGY
 //
index 1aeec0ab8efef0d329f9f537b2354dd12d31dfd0..c43d9336ace5ff523d8ef982da56cc0424a7b6cb 100644 (file)
@@ -6,7 +6,7 @@
    </service>
    <properties entry="config/leadPane.properties"/>
    <property name="defaultLayers" type="String">argeo.suite.ui.dashboardLayer
-argeo.documents.ui.documentsLayer
+argeo.library.ui.contentLayer
 argeo.people.ui.peopleLayer
 argeo.geo.ui.mapLayer
    </property>
diff --git a/core/org.argeo.suite.ui/src/org/argeo/suite/ui/widgets/TreeOrSearchArea.java b/core/org.argeo.suite.ui/src/org/argeo/suite/ui/widgets/TreeOrSearchArea.java
new file mode 100644 (file)
index 0000000..3434ed5
--- /dev/null
@@ -0,0 +1,74 @@
+package org.argeo.suite.ui.widgets;
+
+import org.argeo.cms.ui.util.CmsUiUtils;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StackLayout;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Displays a tree by default, which becomes a list if the search text field is
+ * used.
+ */
+public class TreeOrSearchArea extends Composite {
+       private static final long serialVersionUID = -1302546480076719532L;
+
+       private Text searchT;
+       private StackLayout bodyLayout;
+
+       private TreeViewer treeViewer;
+       private TreeViewer searchResultsViewer;
+
+       public TreeOrSearchArea(Composite parent, int style) {
+               super(parent, style);
+               createUi(this);
+       }
+
+       protected void createUi(Composite parent) {
+               parent.setLayout(new GridLayout());
+               Composite searchC = new Composite(parent, SWT.NONE);
+               searchC.setLayout(new GridLayout());
+               searchC.setLayoutData(CmsUiUtils.fillWidth());
+               createSearchUi(searchC);
+
+               Composite bodyC = new Composite(parent, SWT.NONE);
+               bodyC.setLayoutData(CmsUiUtils.fillAll());
+               bodyLayout = new StackLayout();
+               bodyC.setLayout(bodyLayout);
+               Composite treeC = new Composite(bodyC, SWT.NONE);
+               createTreeUi(treeC);
+               Composite searchResultsC = new Composite(bodyC, SWT.NONE);
+               createSearchResultsUi(searchResultsC);
+
+               bodyLayout.topControl = treeC;
+       }
+
+       protected void createSearchUi(Composite parent) {
+               parent.setLayout(CmsUiUtils.noSpaceGridLayout());
+               searchT = new Text(parent, SWT.MULTI | SWT.BORDER);
+               searchT.setLayoutData(CmsUiUtils.fillWidth());
+       }
+
+       protected void createTreeUi(Composite parent) {
+               parent.setLayout(CmsUiUtils.noSpaceGridLayout());
+               treeViewer = new TreeViewer(parent);
+               treeViewer.getTree().setLayoutData(CmsUiUtils.fillAll());
+       }
+
+       protected void createSearchResultsUi(Composite parent) {
+               parent.setLayout(CmsUiUtils.noSpaceGridLayout());
+               searchResultsViewer = new TreeViewer(parent);
+               searchResultsViewer.getTree().setLayoutData(CmsUiUtils.fillAll());
+       }
+
+       public TreeViewer getTreeViewer() {
+               return treeViewer;
+       }
+
+       public TreeViewer getSearchResultsViewer() {
+               return searchResultsViewer;
+       }
+
+}
diff --git a/library/org.argeo.documents.ui/.classpath b/library/org.argeo.documents.ui/.classpath
deleted file mode 100644 (file)
index e801ebf..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
-       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
-       <classpathentry kind="src" path="src"/>
-       <classpathentry kind="output" path="bin"/>
-</classpath>
diff --git a/library/org.argeo.documents.ui/.gitignore b/library/org.argeo.documents.ui/.gitignore
deleted file mode 100644 (file)
index 09e3bc9..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/bin/
-/target/
diff --git a/library/org.argeo.documents.ui/.project b/library/org.argeo.documents.ui/.project
deleted file mode 100644 (file)
index c046a7d..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-       <name>org.argeo.documents.ui</name>
-       <comment></comment>
-       <projects>
-       </projects>
-       <buildSpec>
-               <buildCommand>
-                       <name>org.eclipse.jdt.core.javabuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.ManifestBuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.SchemaBuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.ds.core.builder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-       </buildSpec>
-       <natures>
-               <nature>org.eclipse.pde.PluginNature</nature>
-               <nature>org.eclipse.jdt.core.javanature</nature>
-       </natures>
-</projectDescription>
diff --git a/library/org.argeo.documents.ui/META-INF/.gitignore b/library/org.argeo.documents.ui/META-INF/.gitignore
deleted file mode 100644 (file)
index 4854a41..0000000
+++ /dev/null
@@ -1 +0,0 @@
-/MANIFEST.MF
diff --git a/library/org.argeo.documents.ui/OSGI-INF/documentsFolder.xml b/library/org.argeo.documents.ui/OSGI-INF/documentsFolder.xml
deleted file mode 100644 (file)
index a7c2e4b..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Documents Folder">
-   <implementation class="org.argeo.documents.ui.DocumentsFolderUiProvider"/>
-   <service>
-      <provide interface="org.argeo.cms.ui.CmsUiProvider"/>
-   </service>
-   <properties entry="config/documentsFolder.properties"/>
-   <reference bind="setNodeFileSystemProvider" cardinality="1..1" interface="java.nio.file.spi.FileSystemProvider" name="FileSystemProvider" policy="dynamic" target="(service.pid=org.argeo.api.fsProvider)"/>
-</scr:component>
diff --git a/library/org.argeo.documents.ui/OSGI-INF/documentsLayer.xml b/library/org.argeo.documents.ui/OSGI-INF/documentsLayer.xml
deleted file mode 100644 (file)
index 3833237..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Documents Layer">
-   <implementation class="org.argeo.suite.ui.DefaultEditionLayer"/>
-   <service>
-      <provide interface="org.argeo.suite.ui.SuiteLayer"/>
-   </service>
-   <reference bind="setEntryArea" cardinality="1..1" interface="org.argeo.cms.ui.CmsUiProvider" name="CmsUiProvider" policy="dynamic" target="(service.pid=argeo.documents.ui.entryArea)"/>
-   <properties entry="config/documentsLayer.properties"/>
-</scr:component>
diff --git a/library/org.argeo.documents.ui/OSGI-INF/entryArea.xml b/library/org.argeo.documents.ui/OSGI-INF/entryArea.xml
deleted file mode 100644 (file)
index 50fcfe3..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Documents Entry Area">
-   <implementation class="org.argeo.documents.ui.DocumentsTreeUiProvider"/>
-   <service>
-      <provide interface="org.argeo.cms.ui.CmsUiProvider"/>
-   </service>
-   <properties entry="config/entryArea.properties"/>
-   <reference bind="setNodeFileSystemProvider" cardinality="1..1" interface="java.nio.file.spi.FileSystemProvider" name="FileSystemProvider" policy="dynamic" target="(service.pid=org.argeo.api.fsProvider)"/>
-   <reference bind="setRepository" cardinality="1..1" interface="javax.jcr.Repository" name="Repository" policy="static" target="(cn=ego)"/>
-</scr:component>
diff --git a/library/org.argeo.documents.ui/OSGI-INF/l10n/bundle.properties b/library/org.argeo.documents.ui/OSGI-INF/l10n/bundle.properties
deleted file mode 100644 (file)
index 8015421..0000000
+++ /dev/null
@@ -1 +0,0 @@
-content=content
diff --git a/library/org.argeo.documents.ui/bnd.bnd b/library/org.argeo.documents.ui/bnd.bnd
deleted file mode 100644 (file)
index 9a3a99c..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-Service-Component:\
-OSGI-INF/entryArea.xml,\
-OSGI-INF/documentsLayer.xml,\
-OSGI-INF/documentsFolder.xml
-
-Import-Package:\
-org.eclipse.swt,\
-org.argeo.api,\
-org.argeo.suite.ui,\
-*
\ No newline at end of file
diff --git a/library/org.argeo.documents.ui/build.properties b/library/org.argeo.documents.ui/build.properties
deleted file mode 100644 (file)
index 0a508e2..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-output.. = bin/
-bin.includes = META-INF/,\
-               .,\
-               OSGI-INF/,\
-               OSGI-INF/documentsLayer.xml,\
-               OSGI-INF/documentsFolder.xml
-source.. = src/
diff --git a/library/org.argeo.documents.ui/config/documentsFolder.properties b/library/org.argeo.documents.ui/config/documentsFolder.properties
deleted file mode 100644 (file)
index 349e930..0000000
+++ /dev/null
@@ -1 +0,0 @@
-entity.type=nt:folder
\ No newline at end of file
diff --git a/library/org.argeo.documents.ui/config/documentsLayer.properties b/library/org.argeo.documents.ui/config/documentsLayer.properties
deleted file mode 100644 (file)
index 78382a6..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-service.pid=argeo.documents.ui.documentsLayer
-
-title=%content
-icon=documents
-
-entity.type=nt:folder
diff --git a/library/org.argeo.documents.ui/config/entryArea.properties b/library/org.argeo.documents.ui/config/entryArea.properties
deleted file mode 100644 (file)
index 43b08f0..0000000
+++ /dev/null
@@ -1 +0,0 @@
-service.pid=argeo.documents.ui.entryArea
diff --git a/library/org.argeo.documents.ui/pom.xml b/library/org.argeo.documents.ui/pom.xml
deleted file mode 100644 (file)
index 2c59ff3..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-       <modelVersion>4.0.0</modelVersion>
-       <parent>
-               <groupId>org.argeo.suite</groupId>
-               <artifactId>library</artifactId>
-               <version>2.3.1-SNAPSHOT</version>
-               <relativePath>..</relativePath>
-       </parent>
-       <artifactId>org.argeo.documents.ui</artifactId>
-       <name>Documents UI</name>
-       <packaging>jar</packaging>
-       <dependencies>
-               <dependency>
-                       <groupId>org.argeo.suite</groupId>
-                       <artifactId>org.argeo.suite.ui</artifactId>
-                       <version>2.3.1-SNAPSHOT</version>
-               </dependency>
-
-               <!-- Eclipse E4 -->
-               <dependency>
-                       <groupId>org.argeo.tp</groupId>
-                       <artifactId>argeo-tp-rap-e4</artifactId>
-                       <version>${version.argeo-tp}</version>
-                       <type>pom</type>
-                       <scope>provided</scope>
-               </dependency>
-               <!-- Specific -->
-               <dependency>
-                       <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.eclipse.ui.rap</artifactId>
-                       <version>${version.argeo-commons}</version>
-                       <scope>provided</scope>
-               </dependency>
-
-       </dependencies>
-</project>
diff --git a/library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsContextMenu.java b/library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsContextMenu.java
deleted file mode 100644 (file)
index 3757dc7..0000000
+++ /dev/null
@@ -1,177 +0,0 @@
-package org.argeo.documents.ui;
-
-import static org.argeo.documents.ui.DocumentsUiService.ACTION_ID_BOOKMARK_FOLDER;
-import static org.argeo.documents.ui.DocumentsUiService.ACTION_ID_CREATE_FOLDER;
-import static org.argeo.documents.ui.DocumentsUiService.ACTION_ID_DELETE;
-import static org.argeo.documents.ui.DocumentsUiService.ACTION_ID_DOWNLOAD_FOLDER;
-import static org.argeo.documents.ui.DocumentsUiService.ACTION_ID_RENAME;
-import static org.argeo.documents.ui.DocumentsUiService.ACTION_ID_SHARE_FOLDER;
-import static org.argeo.documents.ui.DocumentsUiService.ACTION_ID_UPLOAD_FILE;
-
-import java.nio.file.Files;
-import java.nio.file.Path;
-
-import org.argeo.suite.ui.widgets.AbstractConnectContextMenu;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.widgets.Control;
-
-/** Generic popup context menu to manage NIO Path in a Viewer. */
-public class DocumentsContextMenu extends AbstractConnectContextMenu {
-       // Local context
-       private final DocumentsFolderComposite browser;
-       private final DocumentsUiService uiService;
-//     private final Repository repository;
-
-       private final static String[] DEFAULT_ACTIONS = { ACTION_ID_CREATE_FOLDER, ACTION_ID_BOOKMARK_FOLDER,
-                       ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_RENAME,
-                       ACTION_ID_DELETE };
-
-       private Path currFolderPath;
-
-       public DocumentsContextMenu(DocumentsFolderComposite browser,
-                       DocumentsUiService documentsUiService) {
-               super(browser.getDisplay(), DEFAULT_ACTIONS);
-               this.browser = browser;
-               this.uiService = documentsUiService;
-//             this.repository = repository;
-
-               createControl();
-       }
-
-       public void setCurrFolderPath(Path currFolderPath) {
-               this.currFolderPath = currFolderPath;
-       }
-
-       protected boolean aboutToShow(Control source, Point location, IStructuredSelection selection) {
-               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, ACTION_ID_BOOKMARK_FOLDER);
-                       setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_RENAME, ACTION_ID_DELETE
-                               );
-               } else if (multiSel) {
-                       setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_DELETE,
-                                       ACTION_ID_BOOKMARK_FOLDER);
-                       setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_RENAME);
-               } else if (isFolder) {
-                       setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_RENAME, ACTION_ID_DELETE,
-                                       ACTION_ID_BOOKMARK_FOLDER);
-                       setVisible(false, 
-                                       // to be implemented
-                                       ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER);
-               } else {
-                       setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_RENAME,
-                                       ACTION_ID_DELETE);
-                       setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_BOOKMARK_FOLDER);
-               }
-               return true;
-       }
-
-       public void show(Control source, Point location, IStructuredSelection selection, Path currFolderPath) {
-               // 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;
-               super.show(source, location, selection);
-
-       }
-
-       @Override
-       protected boolean performAction(String actionId) {
-               switch (actionId) {
-               case ACTION_ID_CREATE_FOLDER:
-                       createFolder();
-                       break;
-               case ACTION_ID_BOOKMARK_FOLDER:
-                       bookmarkFolder();
-                       break;
-               case ACTION_ID_RENAME:
-                       renameItem();
-                       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";
-               }
-               browser.setFocus();
-               return false;
-       }
-
-       @Override
-       protected String getLabel(String actionId) {
-               return uiService.getLabel(actionId);
-       }
-
-       private void openFile() {
-               IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection());
-               if (selection.isEmpty() || selection.size() > 1)
-                       // Should never happen
-                       return;
-               Path toOpenPath = ((Path) selection.getFirstElement());
-               uiService.openFile(toOpenPath);
-       }
-
-       private void deleteItems() {
-               IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection());
-               if (selection.isEmpty())
-                       return;
-               else if (uiService.deleteItems(getParentShell(), selection))
-                       browser.refresh();
-       }
-
-       private void renameItem() {
-               IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection());
-               if (selection.isEmpty() || selection.size() > 1)
-                       // Should never happen
-                       return;
-               Path toRenamePath = ((Path) selection.getFirstElement());
-               if (uiService.renameItem(getParentShell(), currFolderPath, toRenamePath))
-                       browser.refresh();
-       }
-
-       private void createFolder() {
-               if (uiService.createFolder(getParentShell(), currFolderPath))
-                       browser.refresh();
-       }
-
-       private void bookmarkFolder() {
-               Path toBookmarkPath = null;
-               IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection());
-               if (selection.isEmpty())
-                       toBookmarkPath = currFolderPath;
-               else if (selection.size() > 1)
-                       toBookmarkPath = currFolderPath;
-               else if (selection.size() == 1) {
-                       Path currSelected = ((Path) selection.getFirstElement());
-                       if (Files.isDirectory(currSelected))
-                               toBookmarkPath = currSelected;
-                       else
-                               return;
-               }
-               //uiService.bookmarkFolder(toBookmarkPath, repository, null);
-       }
-
-       private void uploadFiles() {
-               if (uiService.uploadFiles(getParentShell(), currFolderPath))
-                       browser.refresh();
-       }
-}
diff --git a/library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsFileComposite.java b/library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsFileComposite.java
deleted file mode 100644 (file)
index 84c56a3..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-package org.argeo.documents.ui;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.spi.FileSystemProvider;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.fs.CmsFsUtils;
-import org.argeo.cms.ui.util.CmsUiUtils;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.argeo.eclipse.ui.fs.FsUiUtils;
-import org.argeo.eclipse.ui.specific.UiContext;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.browser.Browser;
-import org.eclipse.swt.custom.SashForm;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Label;
-
-/**
- * Default Documents file composite: a sashForm with a browser in the middle and
- * meta data at right hand side.
- */
-public class DocumentsFileComposite extends Composite {
-       private static final long serialVersionUID = -7567632342889241793L;
-
-       private final static Log log = LogFactory.getLog(DocumentsFileComposite.class);
-
-       private final Node currentBaseContext;
-
-       // UI Parts for the browser
-       private Composite rightPannelCmp;
-
-       public DocumentsFileComposite(Composite parent, int style, Node context,
-                       FileSystemProvider fsp) {
-               super(parent, style);
-               this.currentBaseContext = context;
-               this.setLayout(EclipseUiUtils.noSpaceGridLayout());
-               SashForm form = new SashForm(this, SWT.HORIZONTAL);
-
-               Composite centerCmp = new Composite(form, SWT.BORDER | SWT.NO_FOCUS);
-               createDisplay(centerCmp);
-
-               rightPannelCmp = new Composite(form, SWT.NO_FOCUS);
-
-               Path path = CmsFsUtils.getPath(fsp, context);
-               setOverviewInput(path);
-               form.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-               form.setWeights(new int[] { 55, 20 });
-       }
-
-       private void createDisplay(final Composite parent) {
-               parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
-               Browser browser = new Browser(parent, SWT.NONE);
-               // browser.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true,
-               // true));
-               browser.setLayoutData(EclipseUiUtils.fillAll());
-               try {
-                       // FIXME make it more robust
-                       String url = CmsUiUtils.getDataUrl(currentBaseContext, UiContext.getHttpRequest());
-                       // FIXME issue with the redirection to https
-                       if (url.startsWith("http://") && !url.startsWith("http://localhost"))
-                               url = "https://" + url.substring("http://".length(), url.length());
-                       if (log.isTraceEnabled())
-                               log.debug("Trying to display " + url);
-                       browser.setUrl(url);
-                       browser.layout(true, true);
-               } catch (RepositoryException re) {
-                       throw new IllegalStateException("Cannot open file at " + currentBaseContext, re);
-               }
-       }
-
-       /**
-        * 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 = "<i>Unknown</i>";
-                                       addProperty(rightPannelCmp, "Type", mimeType);
-                                       addProperty(rightPannelCmp, "Size", FsUiUtils.humanReadableByteCount(Files.size(path), false));
-                               }
-                       }
-                       rightPannelCmp.layout(true, true);
-               } catch (IOException e) {
-                       throw new IllegalStateException("Cannot display details for " + path.toString(), e);
-               }
-       }
-
-       // Simplify UI implementation
-       private void addProperty(Composite parent, String propName, String value) {
-               Label propLbl = new Label(parent, SWT.NONE);
-               //propLbl.setText(ConnectUtils.replaceAmpersand(propName + ": " + value));
-               propLbl.setText(value);
-               //CmsUiUtils.markup(propLbl);
-       }
-}
diff --git a/library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsFolderComposite.java b/library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsFolderComposite.java
deleted file mode 100644 (file)
index 289a0cd..0000000
+++ /dev/null
@@ -1,455 +0,0 @@
-package org.argeo.documents.ui;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.attribute.FileTime;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-import javax.jcr.Node;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.ui.fs.FileDrop;
-import org.argeo.cms.ui.fs.FsStyles;
-import org.argeo.cms.ui.util.CmsUiUtils;
-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.eclipse.ui.fs.ParentDir;
-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 Documents folder composite: a sashForm layout with a simple table in
- * the middle and an overview at right hand side.
- */
-public class DocumentsFolderComposite extends Composite {
-       private final static Log log = LogFactory.getLog(DocumentsFolderComposite.class);
-       private static final long serialVersionUID = -40347919096946585L;
-
-       private final Node currentBaseContext;
-
-       private final DocumentsUiService documentUiService = new DocumentsUiService();
-
-       // UI Parts for the browser
-       private Composite filterCmp;
-       private Composite breadCrumbCmp;
-       private Text filterTxt;
-       private FsTableViewer directoryDisplayViewer;
-       private Composite rightPanelCmp;
-
-       private DocumentsContextMenu contextMenu;
-       private DateFormat dateFormat = new SimpleDateFormat("YYYY-MM-dd HH:mm");
-
-       // Local context
-       private Path initialPath;
-       private Path currentFolder;
-
-       public DocumentsFolderComposite(Composite parent, int style, Node context) {
-               super(parent, style);
-               this.currentBaseContext = context;
-
-               this.setLayout(EclipseUiUtils.noSpaceGridLayout());
-
-               SashForm form = new SashForm(this, SWT.HORIZONTAL);
-
-               Composite centerCmp = new Composite(form, SWT.BORDER | SWT.NO_FOCUS);
-               createDisplay(centerCmp);
-
-               rightPanelCmp = new Composite(form, SWT.NO_FOCUS);
-
-               form.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-               form.setWeights(new int[] { 55, 20 });
-       }
-
-       public void populate(Path path) {
-               initialPath = path;
-               directoryDisplayViewer.setInitialPath(initialPath);
-               setInput(path);
-       }
-
-       void refresh() {
-               modifyFilter(false);
-       }
-
-       private void createDisplay(final Composite parent) {
-               parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
-
-               // top filter
-               filterCmp = new Composite(parent, SWT.NO_FOCUS);
-               filterCmp.setLayoutData(EclipseUiUtils.fillWidth());
-               RowLayout rl = new RowLayout(SWT.HORIZONTAL);
-               rl.wrap = true;
-               rl.center = true;
-               filterCmp.setLayout(rl);
-               // addFilterPanel(filterCmp);
-
-               // Main display
-               directoryDisplayViewer = new FsTableViewer(parent, SWT.MULTI);
-               List<ColumnDefinition> 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());
-
-               directoryDisplayViewer.addSelectionChangedListener(new ISelectionChangedListener() {
-
-                       @Override
-                       public void selectionChanged(SelectionChangedEvent event) {
-                               IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection();
-                               Path selected = null;
-                               if (selection.isEmpty())
-                                       setSelected(null);
-                               else {
-                                       Object o = selection.getFirstElement();
-                                       if (o instanceof Path)
-                                               selected = (Path) o;
-                                       else if (o instanceof ParentDir)
-                                               selected = ((ParentDir) o).getPath();
-                               }
-                               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()) {
-                                       Object o = selection.getFirstElement();
-                                       if (o instanceof Path)
-                                               selected = (Path) o;
-                                       else if (o instanceof ParentDir)
-                                               selected = ((ParentDir) o).getPath();
-                               }
-                               if (selected != null) {
-                                       if (Files.isDirectory(selected))
-                                               setInput(selected);
-                                       else
-                                               externalNavigateTo(selected);
-                               }
-                       }
-               });
-
-               // The context menu
-               contextMenu = new DocumentsContextMenu(this,  documentUiService);
-
-               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),
-                                                       (IStructuredSelection) directoryDisplayViewer.getSelection(), currentFolder);
-                               }
-                       }
-               });
-
-               FileDrop fileDrop = new FileDrop() {
-
-                       @Override
-                       protected void processFileUpload(InputStream in, String fileName, String contetnType) throws IOException {
-                               Path file = currentFolder.resolve(fileName);
-                               Files.copy(in, file);
-                               refresh();
-                       }
-               };
-               fileDrop.createDropTarget(directoryDisplayViewer.getTable());
-       }
-
-       /**
-        * Overwrite to enable single sourcing between workbench and CMS navigation
-        */
-       protected void externalNavigateTo(Path path) {
-
-       }
-
-       private void addPathElementBtn(Path path) {
-               Button elemBtn = new Button(breadCrumbCmp, SWT.PUSH);
-               String nameStr;
-               if (path.toString().equals("/"))
-                       nameStr = "[jcr:root]";
-               else
-                       nameStr = path.getFileName().toString();
-//             elemBtn.setText(nameStr + " >> ");
-               elemBtn.setText(nameStr);
-               CmsUiUtils.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(currentFolder))
-                       return;
-               // below initial path
-               if (!initialPath.equals(path) && initialPath.startsWith(path))
-                       return;
-               currentFolder = path;
-
-               Path diff = initialPath.relativize(currentFolder);
-
-               for (Control child : filterCmp.getChildren())
-                       if (!child.equals(filterTxt))
-                               child.dispose();
-
-               // Bread crumbs
-               breadCrumbCmp = new Composite(filterCmp, SWT.NO_FOCUS);
-               CmsUiUtils.style(breadCrumbCmp, FsStyles.BREAD_CRUMB_BTN);
-               RowLayout breadCrumbLayout = new RowLayout();
-               breadCrumbLayout.spacing = 0;
-               breadCrumbLayout.marginTop = 0;
-               breadCrumbLayout.marginBottom = 0;
-               breadCrumbLayout.marginRight = 0;
-               breadCrumbLayout.marginLeft = 0;
-               breadCrumbCmp.setLayout(breadCrumbLayout);
-               addPathElementBtn(initialPath);
-               Path currTarget = initialPath;
-               if (!diff.toString().equals(""))
-                       for (Path pathElem : diff) {
-                               currTarget = currTarget.resolve(pathElem);
-                               addPathElementBtn(currTarget);
-                       }
-
-               if (filterTxt != null) {
-                       filterTxt.setText("");
-                       filterTxt.moveBelow(null);
-               } else {
-                       modifyFilter(false);
-               }
-               setSelected(null);
-               filterCmp.getParent().layout(true, true);
-       }
-
-       private void setSelected(Path path) {
-               if (path == null)
-                       setOverviewInput(currentFolder);
-               else
-                       setOverviewInput(path);
-       }
-
-       public Viewer getViewer() {
-               return directoryDisplayViewer;
-       }
-
-       /**
-        * Recreates the content of the box that displays information about the current
-        * selected Path.
-        */
-       private void setOverviewInput(Path path) {
-               try {
-                       EclipseUiUtils.clear(rightPanelCmp);
-                       rightPanelCmp.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(rightPanelCmp, SWT.NONE);
-                               contextL.setText(path.getFileName().toString());
-                               contextL.setFont(EclipseUiUtils.getBoldFont(rightPanelCmp));
-                               FileTime lastModified = Files.getLastModifiedTime(path);
-                               if (lastModified.toMillis() != 0)
-                                       try {
-                                               String lastModifiedStr = dateFormat.format(new Date(lastModified.toMillis()));
-                                               addProperty(rightPanelCmp, "Last modified", lastModifiedStr);
-                                       } catch (Exception e) {
-                                               log.error("Workarounded issue while getting last update date for " + path, e);
-                                               addProperty(rightPanelCmp, "Last modified", "-");
-                                       }
-                               // addProperty(rightPannelCmp, "Owner",
-                               // Files.getOwner(path).getName());
-                               if (Files.isDirectory(path)) {
-                                       addProperty(rightPanelCmp, "Type", "Folder");
-                               } else {
-                                       String mimeType = Files.probeContentType(path);
-                                       if (EclipseUiUtils.isEmpty(mimeType))
-                                               mimeType = "<i>Unknown</i>";
-                                       addProperty(rightPanelCmp, "Type", mimeType);
-                                       addProperty(rightPanelCmp, "Size", FsUiUtils.humanReadableByteCount(Files.size(path), false));
-                               }
-
-                               // read all attributes
-//                             Map<String, Object> attrs = Files.readAttributes(path, "*");
-//                             for (String attr : attrs.keySet()) {
-//                                     Object value = attrs.get(attr);
-//                                     String str;
-//                                     if (value instanceof Calendar) {
-//                                             str = dateFormat.format(((Calendar) value).getTime());
-//                                     } else {
-//                                             str = value.toString();
-//                                     }
-//                                     addProperty(rightPanelCmp, attr, str);
-//
-//                             }
-                       }
-                       rightPanelCmp.layout(true, true);
-               } catch (IOException e) {
-                       throw new IllegalStateException("Cannot display details for " + path.toString(), e);
-               }
-       }
-
-       private void addFilterPanel(Composite parent) {
-               // 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<Path> 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 DocumentsException(
-       // "Unable to determine unique child existence and get it under " + parent +
-       // " with filter " + filter,
-       // ioe);
-       // }
-       // }
-
-       private void modifyFilter(boolean fromOutside) {
-               if (!fromOutside)
-                       if (currentFolder != null) {
-                               String filter;
-                               if (filterTxt != null)
-                                       filter = filterTxt.getText() + "*";
-                               else
-                                       filter = "*";
-                               directoryDisplayViewer.setInput(currentFolder, filter);
-                       }
-       }
-
-       // Simplify UI implementation
-       private void addProperty(Composite parent, String propName, String value) {
-               Label propLbl = new Label(parent, SWT.NONE);
-               //propLbl.setText(ConnectUtils.replaceAmpersand(propName + ": " + value));
-               propLbl.setText(value);
-               //CmsUiUtils.markup(propLbl);
-       }
-
-       public Path getCurrentFolder() {
-               return currentFolder;
-       }
-
-}
diff --git a/library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsFolderUiProvider.java b/library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsFolderUiProvider.java
deleted file mode 100644 (file)
index e525a9e..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-package org.argeo.documents.ui;
-
-import java.nio.file.Path;
-import java.nio.file.spi.FileSystemProvider;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.fs.CmsFsUtils;
-import org.argeo.cms.ui.CmsUiProvider;
-import org.argeo.cms.ui.CmsView;
-import org.argeo.cms.ui.util.CmsUiUtils;
-import org.argeo.jcr.Jcr;
-import org.argeo.suite.ui.SuiteEvent;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-
-/** UI provider of a document folder. */
-public class DocumentsFolderUiProvider implements CmsUiProvider {
-       private FileSystemProvider nodeFileSystemProvider;
-
-       @Override
-       public Control createUi(Composite parent, Node context) throws RepositoryException {
-               CmsView cmsView = CmsView.getCmsView(parent);
-               DocumentsFolderComposite dfc = new DocumentsFolderComposite(parent, SWT.NONE, context) {
-
-                       @Override
-                       protected void externalNavigateTo(Path path) {
-                               Node folderNode = cmsView.doAs(() -> CmsFsUtils.getNode(Jcr.getSession(context).getRepository(), path));
-                               parent.addDisposeListener((e1) -> Jcr.logout(folderNode));
-                               cmsView.sendEvent(SuiteEvent.openNewPart.topic(), SuiteEvent.eventProperties(folderNode));
-                       }
-               };
-               dfc.setLayoutData(CmsUiUtils.fillAll());
-               dfc.populate(cmsView.doAs(() -> CmsFsUtils.getPath(nodeFileSystemProvider, context)));
-               return dfc;
-       }
-
-       public void setNodeFileSystemProvider(FileSystemProvider nodeFileSystemProvider) {
-               this.nodeFileSystemProvider = nodeFileSystemProvider;
-       }
-
-}
diff --git a/library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsTreeUiProvider.java b/library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsTreeUiProvider.java
deleted file mode 100644 (file)
index 13c26e5..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-package org.argeo.documents.ui;
-
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.spi.FileSystemProvider;
-
-import javax.jcr.Node;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-
-import org.argeo.api.NodeConstants;
-import org.argeo.api.NodeUtils;
-import org.argeo.cms.fs.CmsFsUtils;
-import org.argeo.cms.ui.CmsUiProvider;
-import org.argeo.cms.ui.CmsView;
-import org.argeo.cms.ui.util.CmsUiUtils;
-import org.argeo.eclipse.ui.fs.FsTreeViewer;
-import org.argeo.jcr.Jcr;
-import org.argeo.suite.ui.SuiteEvent;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-
-/** Tree view of a user root folders. */
-public class DocumentsTreeUiProvider implements CmsUiProvider {
-       private FileSystemProvider nodeFileSystemProvider;
-       private Repository repository;
-
-       @Override
-       public Control createUi(Composite parent, Node context) throws RepositoryException {
-               parent.setLayout(new GridLayout());
-               FsTreeViewer fsTreeViewer = new FsTreeViewer(parent, SWT.NONE);
-               fsTreeViewer.configureDefaultSingleColumnTable(500);
-               CmsView cmsView = CmsView.getCmsView(parent);
-               Node homeNode = NodeUtils.getUserHome(cmsView.doAs(() -> Jcr.login(repository, NodeConstants.HOME_WORKSPACE)));
-               parent.addDisposeListener((e1) -> Jcr.logout(homeNode));
-               Path homePath = CmsFsUtils.getPath(nodeFileSystemProvider, homeNode);
-               fsTreeViewer.addSelectionChangedListener((e) -> {
-                       IStructuredSelection selection = (IStructuredSelection) fsTreeViewer.getSelection();
-                       if (selection.isEmpty())
-                               return;
-                       else {
-                               Path newSelected = (Path) selection.getFirstElement();
-                               if (Files.isDirectory(newSelected)) {
-                                       Node folderNode = cmsView.doAs(() -> CmsFsUtils.getNode(repository, newSelected));
-                                       parent.addDisposeListener((e1) -> Jcr.logout(folderNode));
-                                       cmsView.sendEvent(SuiteEvent.refreshPart.topic(), SuiteEvent.eventProperties(folderNode));
-                               }
-                       }
-               });
-               fsTreeViewer.addDoubleClickListener((e) -> {
-                       IStructuredSelection selection = (IStructuredSelection) fsTreeViewer.getSelection();
-                       if (selection.isEmpty())
-                               return;
-                       else {
-                               Path newSelected = (Path) selection.getFirstElement();
-                               if (Files.isDirectory(newSelected)) {
-                                       Node folderNode = cmsView.doAs(() -> CmsFsUtils.getNode(repository, newSelected));
-                                       parent.addDisposeListener((e1) -> Jcr.logout(folderNode));
-                                       cmsView.sendEvent(SuiteEvent.openNewPart.topic(), SuiteEvent.eventProperties(folderNode));
-                               }
-                       }
-               });
-               fsTreeViewer.setPathsInput(homePath);
-               fsTreeViewer.getControl().setLayoutData(CmsUiUtils.fillAll());
-               fsTreeViewer.getControl().getParent().layout(true, true);
-               return fsTreeViewer.getControl();
-       }
-
-       public void setNodeFileSystemProvider(FileSystemProvider nodeFileSystemProvider) {
-               this.nodeFileSystemProvider = nodeFileSystemProvider;
-       }
-
-       public void setRepository(Repository repository) {
-               this.repository = repository;
-       }
-
-}
diff --git a/library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsUiService.java b/library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsUiService.java
deleted file mode 100644 (file)
index a0fa782..0000000
+++ /dev/null
@@ -1,310 +0,0 @@
-package org.argeo.documents.ui;
-
-import static org.argeo.cms.ui.dialogs.CmsMessageDialog.openConfirm;
-import static org.argeo.cms.ui.dialogs.CmsMessageDialog.openError;
-import static org.argeo.cms.ui.dialogs.SingleValueDialog.ask;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.reflect.Method;
-import java.net.URI;
-import java.nio.file.DirectoryNotEmptyException;
-import java.nio.file.FileVisitResult;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.SimpleFileVisitor;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.ui.dialogs.CmsFeedback;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.argeo.eclipse.ui.specific.OpenFile;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.FileDialog;
-import org.eclipse.swt.widgets.Shell;
-
-public class DocumentsUiService {
-       private final static Log log = LogFactory.getLog(DocumentsUiService.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_RENAME = "rename";
-       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";
-       public final static String ACTION_ID_DELETE_BOOKMARK = "deleteBookmark";
-       public final static String ACTION_ID_RENAME_BOOKMARK = "renameBookmark";
-
-       public 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_RENAME:
-                       return "Rename";
-               case ACTION_ID_DELETE:
-                       return "Delete";
-               case ACTION_ID_UPLOAD_FILE:
-                       return "Upload Files";
-//             case ACTION_ID_OPEN:
-//                     return "Open";
-               case ACTION_ID_DELETE_BOOKMARK:
-                       return "Delete bookmark";
-               case ACTION_ID_RENAME_BOOKMARK:
-                       return "Rename bookmark";
-               default:
-                       throw new IllegalArgumentException("Unknown action ID " + actionId);
-               }
-       }
-
-       public void openFile(Path toOpenPath) {
-               try {
-                       String name = toOpenPath.getFileName().toString();
-                       File tmpFile = File.createTempFile("tmp", name);
-                       tmpFile.deleteOnExit();
-                       try (OutputStream os = new FileOutputStream(tmpFile)) {
-                               Files.copy(toOpenPath, os);
-                       } catch (IOException e) {
-                               throw new IllegalStateException("Cannot open copy " + name + " to tmpFile.", e);
-                       }
-                       String uri = Paths.get(tmpFile.getAbsolutePath()).toUri().toString();
-                       Map<String, String> params = new HashMap<String, String>();
-                       params.put(OpenFile.PARAM_FILE_NAME, name);
-                       params.put(OpenFile.PARAM_FILE_URI, uri);
-                       // FIXME open file without a command
-                       // CommandUtils.callCommand(OpenFile.ID, params);
-               } catch (IOException e1) {
-                       throw new IllegalStateException("Cannot create tmp copy of " + toOpenPath, e1);
-               }
-       }
-
-       public boolean deleteItems(Shell shell, IStructuredSelection selection) {
-               if (selection.isEmpty())
-                       return false;
-
-               StringBuilder builder = new StringBuilder();
-               @SuppressWarnings("unchecked")
-               Iterator<Object> iterator = selection.iterator();
-               List<Path> 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 (openConfirm(msg)) {
-                       for (Path path : paths) {
-                               try {
-                                       // recursively delete directory and its content
-                                       Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
-                                               @Override
-                                               public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
-                                                       Files.delete(file);
-                                                       return FileVisitResult.CONTINUE;
-                                               }
-
-                                               @Override
-                                               public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
-                                                       Files.delete(dir);
-                                                       return FileVisitResult.CONTINUE;
-                                               }
-                                       });
-                               } catch (DirectoryNotEmptyException e) {
-                                       String errMsg = path.getFileName() + " cannot be deleted: directory is not empty.";
-                                       openError( errMsg);
-                                       throw new IllegalArgumentException("Cannot delete path " + path, e);
-                               } catch (IOException e) {
-                                       String errMsg = e.toString();
-                                       openError(errMsg);
-                                       throw new IllegalArgumentException("Cannot delete path " + path, e);
-                               }
-                       }
-                       return true;
-               }
-               return false;
-       }
-
-       public boolean renameItem(Shell shell, Path parentFolderPath, Path toRenamePath) {
-               String msg = "Enter a new name:";
-               String name = ask( msg, toRenamePath.getFileName().toString());
-               // TODO enhance check of name validity
-               if (EclipseUiUtils.notEmpty(name)) {
-                       try {
-                               Path child = parentFolderPath.resolve(name);
-                               if (Files.exists(child)) {
-                                       String errMsg = "An object named " + name + " already exists at " + parentFolderPath.toString()
-                                                       + ", please provide another name";
-                                       openError( errMsg);
-                                       throw new IllegalArgumentException(errMsg);
-                               } else {
-                                       Files.move(toRenamePath, child);
-                                       return true;
-                               }
-                       } catch (IOException e) {
-                               throw new IllegalStateException("Cannot rename " + name + " at " + parentFolderPath.toString(), e);
-                       }
-               }
-               return false;
-       }
-
-       public boolean createFolder(Shell shell, Path currFolderPath) {
-               String msg = "Enter a name:";
-               String name = ask( msg);
-               // TODO enhance check of name validity
-               if (EclipseUiUtils.notEmpty(name)) {
-                       name = name.trim();
-                       try {
-                               Path child = currFolderPath.resolve(name);
-                               if (Files.exists(child)) {
-                                       String errMsg = "A folder named " + name + " already exists at " + currFolderPath.toString()
-                                                       + ", cannot create";
-                                       openError(errMsg);
-                                       throw new IllegalArgumentException(errMsg);
-                               } else {
-                                       Files.createDirectories(child);
-                                       return true;
-                               }
-                       } catch (IOException e) {
-                               throw new IllegalStateException("Cannot create folder " + name + " at " + currFolderPath.toString(), e);
-                       }
-               }
-               return false;
-       }
-
-//     public void bookmarkFolder(Path toBookmarkPath, Repository repository, DocumentsService documentsService) {
-//             String msg = "Provide a name:";
-//             String name = SingleQuestion.ask("Create bookmark", msg, toBookmarkPath.getFileName().toString());
-//             if (EclipseUiUtils.notEmpty(name))
-//                     documentsService.createFolderBookmark(toBookmarkPath, name, repository);
-//     }
-
-       public boolean uploadFiles(Shell shell, Path currFolderPath) {
-//             shell = Display.getCurrent().getActiveShell();// ignore argument
-               try {
-                       FileDialog dialog = new FileDialog(shell, 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 false;
-                               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)) {
-                                                               openError(
-                                                                               "Upload of directories in the system is not yet implemented");
-                                                               continue loop;
-                                                       }
-                                                       Path targetPath = currFolderPath.resolve(tmpPath.getFileName().toString());
-                                                       try (InputStream in = new FileInputStream(tmpPath.toFile())) {
-                                                               Files.copy(in, targetPath);
-                                                               Files.delete(tmpPath);
-                                                       }
-                                                       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";
-                                                       openError( msg);
-                                                       continue loop;
-                                               }
-                                       }
-                                       return true;
-                               }
-                       }
-               } catch (Exception e) {
-                       CmsFeedback.show("Cannot import files to " + currFolderPath,e);
-               }
-               return false;
-       }
-
-//     public boolean deleteBookmark(Shell shell, IStructuredSelection selection, Node bookmarkParent) {
-//             if (selection.isEmpty())
-//                     return false;
-//
-//             StringBuilder builder = new StringBuilder();
-//             @SuppressWarnings("unchecked")
-//             Iterator<Object> iterator = selection.iterator();
-//             List<Node> nodes = new ArrayList<>();
-//
-//             while (iterator.hasNext()) {
-//                     Node node = (Node) iterator.next();
-//                     builder.append(Jcr.get(node, Property.JCR_TITLE) + ", ");
-//                     nodes.add(node);
-//             }
-//             String msg = "You are about to delete following bookmark: " + builder.substring(0, builder.length() - 2)
-//                             + ". Are you sure?";
-//             if (MessageDialog.openConfirm(shell, "Confirm deletion", msg)) {
-//                     Session session = Jcr.session(bookmarkParent);
-//                     try {
-//                             if (session.hasPendingChanges())
-//                                     throw new DocumentsException("Cannot remove bookmarks, session is not clean");
-//                             for (Node path : nodes)
-//                                     path.remove();
-//                             bookmarkParent.getSession().save();
-//                             return true;
-//                     } catch (RepositoryException e) {
-//                             JcrUtils.discardQuietly(session);
-//                             throw new DocumentsException("Cannot delete bookmarks " + builder.toString(), e);
-//                     }
-//             }
-//             return false;
-//     }
-
-//     public boolean renameBookmark(IStructuredSelection selection) {
-//             if (selection.isEmpty() || selection.size() > 1)
-//                     return false;
-//             Node toRename = (Node) selection.getFirstElement();
-//             String msg = "Please provide a new name.";
-//             String name = SingleQuestion.ask("Rename bookmark", msg, ConnectJcrUtils.get(toRename, Property.JCR_TITLE));
-//             if (EclipseUiUtils.notEmpty(name)
-//                             && ConnectJcrUtils.setJcrProperty(toRename, Property.JCR_TITLE, PropertyType.STRING, name)) {
-//                     ConnectJcrUtils.saveIfNecessary(toRename);
-//                     return true;
-//             }
-//             return false;
-//     }
-}
diff --git a/library/org.argeo.library.ui/.classpath b/library/org.argeo.library.ui/.classpath
new file mode 100644 (file)
index 0000000..e801ebf
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/library/org.argeo.library.ui/.gitignore b/library/org.argeo.library.ui/.gitignore
new file mode 100644 (file)
index 0000000..09e3bc9
--- /dev/null
@@ -0,0 +1,2 @@
+/bin/
+/target/
diff --git a/library/org.argeo.library.ui/.project b/library/org.argeo.library.ui/.project
new file mode 100644 (file)
index 0000000..6aa2010
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.library.ui</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ManifestBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.SchemaBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ds.core.builder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.pde.PluginNature</nature>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+       </natures>
+</projectDescription>
diff --git a/library/org.argeo.library.ui/META-INF/.gitignore b/library/org.argeo.library.ui/META-INF/.gitignore
new file mode 100644 (file)
index 0000000..4854a41
--- /dev/null
@@ -0,0 +1 @@
+/MANIFEST.MF
diff --git a/library/org.argeo.library.ui/OSGI-INF/contentEntryArea.xml b/library/org.argeo.library.ui/OSGI-INF/contentEntryArea.xml
new file mode 100644 (file)
index 0000000..0b5646e
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
+   <implementation class="org.argeo.library.ui.ContentEntryArea"/>
+   <service>
+      <provide interface="org.argeo.cms.ui.CmsUiProvider"/>
+   </service>
+   <properties entry="config/contentEntryArea.properties"/>
+</scr:component>
diff --git a/library/org.argeo.library.ui/OSGI-INF/contentLayer.xml b/library/org.argeo.library.ui/OSGI-INF/contentLayer.xml
new file mode 100644 (file)
index 0000000..0dae1af
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
+   <implementation class="org.argeo.suite.ui.DefaultEditionLayer"/>
+   <service>
+      <provide interface="org.argeo.suite.ui.SuiteLayer"/>
+   </service>
+   <reference bind="setEntryArea" cardinality="1..1" interface="org.argeo.cms.ui.CmsUiProvider" name="CmsUiProvider" policy="dynamic" target="(service.pid=argeo.library.ui.contentEntryArea)"/>
+   <properties entry="config/contentLayer.properties"/>
+</scr:component>
diff --git a/library/org.argeo.library.ui/OSGI-INF/documentsFolder.xml b/library/org.argeo.library.ui/OSGI-INF/documentsFolder.xml
new file mode 100644 (file)
index 0000000..d7d71f0
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Documents Folder">
+   <implementation class="org.argeo.library.ui.DocumentsFolderUiProvider"/>
+   <service>
+      <provide interface="org.argeo.cms.ui.CmsUiProvider"/>
+   </service>
+   <properties entry="config/documentsFolder.properties"/>
+   <reference bind="setNodeFileSystemProvider" cardinality="1..1" interface="java.nio.file.spi.FileSystemProvider" name="FileSystemProvider" policy="dynamic" target="(service.pid=org.argeo.api.fsProvider)"/>
+</scr:component>
diff --git a/library/org.argeo.library.ui/OSGI-INF/fsEntryArea.xml b/library/org.argeo.library.ui/OSGI-INF/fsEntryArea.xml
new file mode 100644 (file)
index 0000000..540f4ff
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
+   <implementation class="org.argeo.library.ui.DocumentsTreeUiProvider"/>
+   <service>
+      <provide interface="org.argeo.cms.ui.CmsUiProvider"/>
+   </service>
+   <properties entry="config/contentEntryArea.properties"/>
+   <reference bind="setNodeFileSystemProvider" cardinality="1..1" interface="java.nio.file.spi.FileSystemProvider" name="FileSystemProvider" policy="dynamic" target="(service.pid=org.argeo.api.fsProvider)"/>
+   <reference bind="setRepository" cardinality="1..1" interface="javax.jcr.Repository" name="Repository" policy="static" target="(cn=ego)"/>
+</scr:component>
diff --git a/library/org.argeo.library.ui/OSGI-INF/l10n/bundle.properties b/library/org.argeo.library.ui/OSGI-INF/l10n/bundle.properties
new file mode 100644 (file)
index 0000000..8015421
--- /dev/null
@@ -0,0 +1 @@
+content=content
diff --git a/library/org.argeo.library.ui/bnd.bnd b/library/org.argeo.library.ui/bnd.bnd
new file mode 100644 (file)
index 0000000..71f4618
--- /dev/null
@@ -0,0 +1,12 @@
+Service-Component:\
+OSGI-INF/contentEntryArea.xml,\
+OSGI-INF/fsEntryArea.xml,\
+OSGI-INF/contentLayer.xml,\
+OSGI-INF/documentsFolder.xml
+
+Import-Package:\
+org.eclipse.swt,\
+javax.jcr.nodetype,\
+org.argeo.api,\
+org.argeo.suite.ui,\
+*
\ No newline at end of file
diff --git a/library/org.argeo.library.ui/build.properties b/library/org.argeo.library.ui/build.properties
new file mode 100644 (file)
index 0000000..0859c52
--- /dev/null
@@ -0,0 +1,7 @@
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               OSGI-INF/,\
+               OSGI-INF/contentLayer.xml,\
+               OSGI-INF/documentsFolder.xml
+source.. = src/
diff --git a/library/org.argeo.library.ui/config/contentEntryArea.properties b/library/org.argeo.library.ui/config/contentEntryArea.properties
new file mode 100644 (file)
index 0000000..855fe97
--- /dev/null
@@ -0,0 +1 @@
+service.pid=argeo.library.ui.contentEntryArea
diff --git a/library/org.argeo.library.ui/config/contentLayer.properties b/library/org.argeo.library.ui/config/contentLayer.properties
new file mode 100644 (file)
index 0000000..ad7b10e
--- /dev/null
@@ -0,0 +1,6 @@
+service.pid=argeo.library.ui.contentLayer
+
+title=%content
+icon=documents
+
+entity.type=nt:folder,entity:space
diff --git a/library/org.argeo.library.ui/config/documentsFolder.properties b/library/org.argeo.library.ui/config/documentsFolder.properties
new file mode 100644 (file)
index 0000000..349e930
--- /dev/null
@@ -0,0 +1 @@
+entity.type=nt:folder
\ No newline at end of file
diff --git a/library/org.argeo.library.ui/config/fsEntryArea.properties b/library/org.argeo.library.ui/config/fsEntryArea.properties
new file mode 100644 (file)
index 0000000..0bceaf0
--- /dev/null
@@ -0,0 +1 @@
+service.pid=argeo.library.ui.fsEntryArea
diff --git a/library/org.argeo.library.ui/pom.xml b/library/org.argeo.library.ui/pom.xml
new file mode 100644 (file)
index 0000000..048dd4a
--- /dev/null
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.argeo.suite</groupId>
+               <artifactId>library</artifactId>
+               <version>2.3.1-SNAPSHOT</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.library.ui</artifactId>
+       <name>Documents UI</name>
+       <packaging>jar</packaging>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.suite</groupId>
+                       <artifactId>org.argeo.suite.ui</artifactId>
+                       <version>2.3.1-SNAPSHOT</version>
+               </dependency>
+
+               <!-- Eclipse E4 -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>argeo-tp-rap-e4</artifactId>
+                       <version>${version.argeo-tp}</version>
+                       <type>pom</type>
+                       <scope>provided</scope>
+               </dependency>
+               <!-- Specific -->
+               <dependency>
+                       <groupId>org.argeo.commons</groupId>
+                       <artifactId>org.argeo.eclipse.ui.rap</artifactId>
+                       <version>${version.argeo-commons}</version>
+                       <scope>provided</scope>
+               </dependency>
+
+       </dependencies>
+</project>
diff --git a/library/org.argeo.library.ui/src/org/argeo/library/ui/ContentEntryArea.java b/library/org.argeo.library.ui/src/org/argeo/library/ui/ContentEntryArea.java
new file mode 100644 (file)
index 0000000..4c8b041
--- /dev/null
@@ -0,0 +1,103 @@
+package org.argeo.library.ui;
+
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.query.Query;
+
+import org.argeo.cms.ui.CmsUiProvider;
+import org.argeo.cms.ui.util.CmsUiUtils;
+import org.argeo.entity.EntityType;
+import org.argeo.jcr.Jcr;
+import org.argeo.jcr.JcrException;
+import org.argeo.suite.ui.widgets.TreeOrSearchArea;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+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 ContentEntryArea implements CmsUiProvider {
+
+       @Override
+       public Control createUi(Composite parent, Node context) throws RepositoryException {
+               parent.setLayout(new GridLayout());
+               Ui ui = new Ui(parent, SWT.NONE);
+               ui.setLayoutData(CmsUiUtils.fillAll());
+
+               ui.getTreeViewer().setContentProvider(new SpacesContentProvider());
+               ui.getTreeViewer().setInput(context.getSession());
+               return ui;
+       }
+
+       protected boolean isLeaf(Node node) {
+               return Jcr.isNodeType(node, EntityType.entity.get()) || Jcr.isNodeType(node, NodeType.NT_FILE);
+       }
+
+       class Ui extends TreeOrSearchArea {
+
+               public Ui(Composite parent, int style) {
+                       super(parent, style);
+               }
+
+       }
+
+       class SpacesContentProvider implements ITreeContentProvider {
+
+               @Override
+               public Object[] getElements(Object inputElement) {
+                       Session session = (Session) inputElement;
+                       try {
+                               Query query = session.getWorkspace().getQueryManager()
+                                               .createQuery("SELECT * FROM [" + EntityType.space.get() + "]", Query.JCR_SQL2);
+                               NodeIterator spacesIt = query.execute().getNodes();
+                               SortedMap<String, Node> map = new TreeMap<>();
+                               while (spacesIt.hasNext()) {
+                                       Node space = spacesIt.nextNode();
+                                       String path = space.getPath();
+                                       map.put(path, space);
+                               }
+                               return map.values().toArray();
+                       } catch (RepositoryException e) {
+                               throw new JcrException(e);
+                       }
+               }
+
+               @Override
+               public Object[] getChildren(Object parentElement) {
+                       Node parent = (Node) parentElement;
+                       if (isLeaf(parent))
+                               return null;
+                       return Jcr.getNodes(parent).toArray();
+               }
+
+               @Override
+               public Object getParent(Object element) {
+                       Node node = (Node) element;
+                       return Jcr.getParent(node);
+               }
+
+               @Override
+               public boolean hasChildren(Object element) {
+                       Node node = (Node) element;
+                       return !isLeaf(node);
+               }
+
+               @Override
+               public void dispose() {
+               }
+
+               @Override
+               public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+               }
+
+       }
+
+}
diff --git a/library/org.argeo.library.ui/src/org/argeo/library/ui/DocumentsContextMenu.java b/library/org.argeo.library.ui/src/org/argeo/library/ui/DocumentsContextMenu.java
new file mode 100644 (file)
index 0000000..03de251
--- /dev/null
@@ -0,0 +1,177 @@
+package org.argeo.library.ui;
+
+import static org.argeo.library.ui.DocumentsUiService.ACTION_ID_BOOKMARK_FOLDER;
+import static org.argeo.library.ui.DocumentsUiService.ACTION_ID_CREATE_FOLDER;
+import static org.argeo.library.ui.DocumentsUiService.ACTION_ID_DELETE;
+import static org.argeo.library.ui.DocumentsUiService.ACTION_ID_DOWNLOAD_FOLDER;
+import static org.argeo.library.ui.DocumentsUiService.ACTION_ID_RENAME;
+import static org.argeo.library.ui.DocumentsUiService.ACTION_ID_SHARE_FOLDER;
+import static org.argeo.library.ui.DocumentsUiService.ACTION_ID_UPLOAD_FILE;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.argeo.suite.ui.widgets.AbstractConnectContextMenu;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Control;
+
+/** Generic popup context menu to manage NIO Path in a Viewer. */
+public class DocumentsContextMenu extends AbstractConnectContextMenu {
+       // Local context
+       private final DocumentsFolderComposite browser;
+       private final DocumentsUiService uiService;
+//     private final Repository repository;
+
+       private final static String[] DEFAULT_ACTIONS = { ACTION_ID_CREATE_FOLDER, ACTION_ID_BOOKMARK_FOLDER,
+                       ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_RENAME,
+                       ACTION_ID_DELETE };
+
+       private Path currFolderPath;
+
+       public DocumentsContextMenu(DocumentsFolderComposite browser,
+                       DocumentsUiService documentsUiService) {
+               super(browser.getDisplay(), DEFAULT_ACTIONS);
+               this.browser = browser;
+               this.uiService = documentsUiService;
+//             this.repository = repository;
+
+               createControl();
+       }
+
+       public void setCurrFolderPath(Path currFolderPath) {
+               this.currFolderPath = currFolderPath;
+       }
+
+       protected boolean aboutToShow(Control source, Point location, IStructuredSelection selection) {
+               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, ACTION_ID_BOOKMARK_FOLDER);
+                       setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_RENAME, ACTION_ID_DELETE
+                               );
+               } else if (multiSel) {
+                       setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_DELETE,
+                                       ACTION_ID_BOOKMARK_FOLDER);
+                       setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_RENAME);
+               } else if (isFolder) {
+                       setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_RENAME, ACTION_ID_DELETE,
+                                       ACTION_ID_BOOKMARK_FOLDER);
+                       setVisible(false, 
+                                       // to be implemented
+                                       ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER);
+               } else {
+                       setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_RENAME,
+                                       ACTION_ID_DELETE);
+                       setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_BOOKMARK_FOLDER);
+               }
+               return true;
+       }
+
+       public void show(Control source, Point location, IStructuredSelection selection, Path currFolderPath) {
+               // 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;
+               super.show(source, location, selection);
+
+       }
+
+       @Override
+       protected boolean performAction(String actionId) {
+               switch (actionId) {
+               case ACTION_ID_CREATE_FOLDER:
+                       createFolder();
+                       break;
+               case ACTION_ID_BOOKMARK_FOLDER:
+                       bookmarkFolder();
+                       break;
+               case ACTION_ID_RENAME:
+                       renameItem();
+                       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";
+               }
+               browser.setFocus();
+               return false;
+       }
+
+       @Override
+       protected String getLabel(String actionId) {
+               return uiService.getLabel(actionId);
+       }
+
+       private void openFile() {
+               IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection());
+               if (selection.isEmpty() || selection.size() > 1)
+                       // Should never happen
+                       return;
+               Path toOpenPath = ((Path) selection.getFirstElement());
+               uiService.openFile(toOpenPath);
+       }
+
+       private void deleteItems() {
+               IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection());
+               if (selection.isEmpty())
+                       return;
+               else if (uiService.deleteItems(getParentShell(), selection))
+                       browser.refresh();
+       }
+
+       private void renameItem() {
+               IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection());
+               if (selection.isEmpty() || selection.size() > 1)
+                       // Should never happen
+                       return;
+               Path toRenamePath = ((Path) selection.getFirstElement());
+               if (uiService.renameItem(getParentShell(), currFolderPath, toRenamePath))
+                       browser.refresh();
+       }
+
+       private void createFolder() {
+               if (uiService.createFolder(getParentShell(), currFolderPath))
+                       browser.refresh();
+       }
+
+       private void bookmarkFolder() {
+               Path toBookmarkPath = null;
+               IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection());
+               if (selection.isEmpty())
+                       toBookmarkPath = currFolderPath;
+               else if (selection.size() > 1)
+                       toBookmarkPath = currFolderPath;
+               else if (selection.size() == 1) {
+                       Path currSelected = ((Path) selection.getFirstElement());
+                       if (Files.isDirectory(currSelected))
+                               toBookmarkPath = currSelected;
+                       else
+                               return;
+               }
+               //uiService.bookmarkFolder(toBookmarkPath, repository, null);
+       }
+
+       private void uploadFiles() {
+               if (uiService.uploadFiles(getParentShell(), currFolderPath))
+                       browser.refresh();
+       }
+}
diff --git a/library/org.argeo.library.ui/src/org/argeo/library/ui/DocumentsFileComposite.java b/library/org.argeo.library.ui/src/org/argeo/library/ui/DocumentsFileComposite.java
new file mode 100644 (file)
index 0000000..10cf3bd
--- /dev/null
@@ -0,0 +1,125 @@
+package org.argeo.library.ui;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.spi.FileSystemProvider;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.fs.CmsFsUtils;
+import org.argeo.cms.ui.util.CmsUiUtils;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.eclipse.ui.fs.FsUiUtils;
+import org.argeo.eclipse.ui.specific.UiContext;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.browser.Browser;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+
+/**
+ * Default Documents file composite: a sashForm with a browser in the middle and
+ * meta data at right hand side.
+ */
+public class DocumentsFileComposite extends Composite {
+       private static final long serialVersionUID = -7567632342889241793L;
+
+       private final static Log log = LogFactory.getLog(DocumentsFileComposite.class);
+
+       private final Node currentBaseContext;
+
+       // UI Parts for the browser
+       private Composite rightPannelCmp;
+
+       public DocumentsFileComposite(Composite parent, int style, Node context,
+                       FileSystemProvider fsp) {
+               super(parent, style);
+               this.currentBaseContext = context;
+               this.setLayout(EclipseUiUtils.noSpaceGridLayout());
+               SashForm form = new SashForm(this, SWT.HORIZONTAL);
+
+               Composite centerCmp = new Composite(form, SWT.BORDER | SWT.NO_FOCUS);
+               createDisplay(centerCmp);
+
+               rightPannelCmp = new Composite(form, SWT.NO_FOCUS);
+
+               Path path = CmsFsUtils.getPath(fsp, context);
+               setOverviewInput(path);
+               form.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               form.setWeights(new int[] { 55, 20 });
+       }
+
+       private void createDisplay(final Composite parent) {
+               parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
+               Browser browser = new Browser(parent, SWT.NONE);
+               // browser.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true,
+               // true));
+               browser.setLayoutData(EclipseUiUtils.fillAll());
+               try {
+                       // FIXME make it more robust
+                       String url = CmsUiUtils.getDataUrl(currentBaseContext, UiContext.getHttpRequest());
+                       // FIXME issue with the redirection to https
+                       if (url.startsWith("http://") && !url.startsWith("http://localhost"))
+                               url = "https://" + url.substring("http://".length(), url.length());
+                       if (log.isTraceEnabled())
+                               log.debug("Trying to display " + url);
+                       browser.setUrl(url);
+                       browser.layout(true, true);
+               } catch (RepositoryException re) {
+                       throw new IllegalStateException("Cannot open file at " + currentBaseContext, re);
+               }
+       }
+
+       /**
+        * 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 = "<i>Unknown</i>";
+                                       addProperty(rightPannelCmp, "Type", mimeType);
+                                       addProperty(rightPannelCmp, "Size", FsUiUtils.humanReadableByteCount(Files.size(path), false));
+                               }
+                       }
+                       rightPannelCmp.layout(true, true);
+               } catch (IOException e) {
+                       throw new IllegalStateException("Cannot display details for " + path.toString(), e);
+               }
+       }
+
+       // Simplify UI implementation
+       private void addProperty(Composite parent, String propName, String value) {
+               Label propLbl = new Label(parent, SWT.NONE);
+               //propLbl.setText(ConnectUtils.replaceAmpersand(propName + ": " + value));
+               propLbl.setText(value);
+               //CmsUiUtils.markup(propLbl);
+       }
+}
diff --git a/library/org.argeo.library.ui/src/org/argeo/library/ui/DocumentsFolderComposite.java b/library/org.argeo.library.ui/src/org/argeo/library/ui/DocumentsFolderComposite.java
new file mode 100644 (file)
index 0000000..a686074
--- /dev/null
@@ -0,0 +1,455 @@
+package org.argeo.library.ui;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.FileTime;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import javax.jcr.Node;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.ui.fs.FileDrop;
+import org.argeo.cms.ui.fs.FsStyles;
+import org.argeo.cms.ui.util.CmsUiUtils;
+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.eclipse.ui.fs.ParentDir;
+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 Documents folder composite: a sashForm layout with a simple table in
+ * the middle and an overview at right hand side.
+ */
+public class DocumentsFolderComposite extends Composite {
+       private final static Log log = LogFactory.getLog(DocumentsFolderComposite.class);
+       private static final long serialVersionUID = -40347919096946585L;
+
+       private final Node currentBaseContext;
+
+       private final DocumentsUiService documentUiService = new DocumentsUiService();
+
+       // UI Parts for the browser
+       private Composite filterCmp;
+       private Composite breadCrumbCmp;
+       private Text filterTxt;
+       private FsTableViewer directoryDisplayViewer;
+       private Composite rightPanelCmp;
+
+       private DocumentsContextMenu contextMenu;
+       private DateFormat dateFormat = new SimpleDateFormat("YYYY-MM-dd HH:mm");
+
+       // Local context
+       private Path initialPath;
+       private Path currentFolder;
+
+       public DocumentsFolderComposite(Composite parent, int style, Node context) {
+               super(parent, style);
+               this.currentBaseContext = context;
+
+               this.setLayout(EclipseUiUtils.noSpaceGridLayout());
+
+               SashForm form = new SashForm(this, SWT.HORIZONTAL);
+
+               Composite centerCmp = new Composite(form, SWT.BORDER | SWT.NO_FOCUS);
+               createDisplay(centerCmp);
+
+               rightPanelCmp = new Composite(form, SWT.NO_FOCUS);
+
+               form.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               form.setWeights(new int[] { 55, 20 });
+       }
+
+       public void populate(Path path) {
+               initialPath = path;
+               directoryDisplayViewer.setInitialPath(initialPath);
+               setInput(path);
+       }
+
+       void refresh() {
+               modifyFilter(false);
+       }
+
+       private void createDisplay(final Composite parent) {
+               parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
+
+               // top filter
+               filterCmp = new Composite(parent, SWT.NO_FOCUS);
+               filterCmp.setLayoutData(EclipseUiUtils.fillWidth());
+               RowLayout rl = new RowLayout(SWT.HORIZONTAL);
+               rl.wrap = true;
+               rl.center = true;
+               filterCmp.setLayout(rl);
+               // addFilterPanel(filterCmp);
+
+               // Main display
+               directoryDisplayViewer = new FsTableViewer(parent, SWT.MULTI);
+               List<ColumnDefinition> 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());
+
+               directoryDisplayViewer.addSelectionChangedListener(new ISelectionChangedListener() {
+
+                       @Override
+                       public void selectionChanged(SelectionChangedEvent event) {
+                               IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection();
+                               Path selected = null;
+                               if (selection.isEmpty())
+                                       setSelected(null);
+                               else {
+                                       Object o = selection.getFirstElement();
+                                       if (o instanceof Path)
+                                               selected = (Path) o;
+                                       else if (o instanceof ParentDir)
+                                               selected = ((ParentDir) o).getPath();
+                               }
+                               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()) {
+                                       Object o = selection.getFirstElement();
+                                       if (o instanceof Path)
+                                               selected = (Path) o;
+                                       else if (o instanceof ParentDir)
+                                               selected = ((ParentDir) o).getPath();
+                               }
+                               if (selected != null) {
+                                       if (Files.isDirectory(selected))
+                                               setInput(selected);
+                                       else
+                                               externalNavigateTo(selected);
+                               }
+                       }
+               });
+
+               // The context menu
+               contextMenu = new DocumentsContextMenu(this,  documentUiService);
+
+               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),
+                                                       (IStructuredSelection) directoryDisplayViewer.getSelection(), currentFolder);
+                               }
+                       }
+               });
+
+               FileDrop fileDrop = new FileDrop() {
+
+                       @Override
+                       protected void processFileUpload(InputStream in, String fileName, String contetnType) throws IOException {
+                               Path file = currentFolder.resolve(fileName);
+                               Files.copy(in, file);
+                               refresh();
+                       }
+               };
+               fileDrop.createDropTarget(directoryDisplayViewer.getTable());
+       }
+
+       /**
+        * Overwrite to enable single sourcing between workbench and CMS navigation
+        */
+       protected void externalNavigateTo(Path path) {
+
+       }
+
+       private void addPathElementBtn(Path path) {
+               Button elemBtn = new Button(breadCrumbCmp, SWT.PUSH);
+               String nameStr;
+               if (path.toString().equals("/"))
+                       nameStr = "[jcr:root]";
+               else
+                       nameStr = path.getFileName().toString();
+//             elemBtn.setText(nameStr + " >> ");
+               elemBtn.setText(nameStr);
+               CmsUiUtils.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(currentFolder))
+                       return;
+               // below initial path
+               if (!initialPath.equals(path) && initialPath.startsWith(path))
+                       return;
+               currentFolder = path;
+
+               Path diff = initialPath.relativize(currentFolder);
+
+               for (Control child : filterCmp.getChildren())
+                       if (!child.equals(filterTxt))
+                               child.dispose();
+
+               // Bread crumbs
+               breadCrumbCmp = new Composite(filterCmp, SWT.NO_FOCUS);
+               CmsUiUtils.style(breadCrumbCmp, FsStyles.BREAD_CRUMB_BTN);
+               RowLayout breadCrumbLayout = new RowLayout();
+               breadCrumbLayout.spacing = 0;
+               breadCrumbLayout.marginTop = 0;
+               breadCrumbLayout.marginBottom = 0;
+               breadCrumbLayout.marginRight = 0;
+               breadCrumbLayout.marginLeft = 0;
+               breadCrumbCmp.setLayout(breadCrumbLayout);
+               addPathElementBtn(initialPath);
+               Path currTarget = initialPath;
+               if (!diff.toString().equals(""))
+                       for (Path pathElem : diff) {
+                               currTarget = currTarget.resolve(pathElem);
+                               addPathElementBtn(currTarget);
+                       }
+
+               if (filterTxt != null) {
+                       filterTxt.setText("");
+                       filterTxt.moveBelow(null);
+               } else {
+                       modifyFilter(false);
+               }
+               setSelected(null);
+               filterCmp.getParent().layout(true, true);
+       }
+
+       private void setSelected(Path path) {
+               if (path == null)
+                       setOverviewInput(currentFolder);
+               else
+                       setOverviewInput(path);
+       }
+
+       public Viewer getViewer() {
+               return directoryDisplayViewer;
+       }
+
+       /**
+        * Recreates the content of the box that displays information about the current
+        * selected Path.
+        */
+       private void setOverviewInput(Path path) {
+               try {
+                       EclipseUiUtils.clear(rightPanelCmp);
+                       rightPanelCmp.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(rightPanelCmp, SWT.NONE);
+                               contextL.setText(path.getFileName().toString());
+                               contextL.setFont(EclipseUiUtils.getBoldFont(rightPanelCmp));
+                               FileTime lastModified = Files.getLastModifiedTime(path);
+                               if (lastModified.toMillis() != 0)
+                                       try {
+                                               String lastModifiedStr = dateFormat.format(new Date(lastModified.toMillis()));
+                                               addProperty(rightPanelCmp, "Last modified", lastModifiedStr);
+                                       } catch (Exception e) {
+                                               log.error("Workarounded issue while getting last update date for " + path, e);
+                                               addProperty(rightPanelCmp, "Last modified", "-");
+                                       }
+                               // addProperty(rightPannelCmp, "Owner",
+                               // Files.getOwner(path).getName());
+                               if (Files.isDirectory(path)) {
+                                       addProperty(rightPanelCmp, "Type", "Folder");
+                               } else {
+                                       String mimeType = Files.probeContentType(path);
+                                       if (EclipseUiUtils.isEmpty(mimeType))
+                                               mimeType = "<i>Unknown</i>";
+                                       addProperty(rightPanelCmp, "Type", mimeType);
+                                       addProperty(rightPanelCmp, "Size", FsUiUtils.humanReadableByteCount(Files.size(path), false));
+                               }
+
+                               // read all attributes
+//                             Map<String, Object> attrs = Files.readAttributes(path, "*");
+//                             for (String attr : attrs.keySet()) {
+//                                     Object value = attrs.get(attr);
+//                                     String str;
+//                                     if (value instanceof Calendar) {
+//                                             str = dateFormat.format(((Calendar) value).getTime());
+//                                     } else {
+//                                             str = value.toString();
+//                                     }
+//                                     addProperty(rightPanelCmp, attr, str);
+//
+//                             }
+                       }
+                       rightPanelCmp.layout(true, true);
+               } catch (IOException e) {
+                       throw new IllegalStateException("Cannot display details for " + path.toString(), e);
+               }
+       }
+
+       private void addFilterPanel(Composite parent) {
+               // 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<Path> 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 DocumentsException(
+       // "Unable to determine unique child existence and get it under " + parent +
+       // " with filter " + filter,
+       // ioe);
+       // }
+       // }
+
+       private void modifyFilter(boolean fromOutside) {
+               if (!fromOutside)
+                       if (currentFolder != null) {
+                               String filter;
+                               if (filterTxt != null)
+                                       filter = filterTxt.getText() + "*";
+                               else
+                                       filter = "*";
+                               directoryDisplayViewer.setInput(currentFolder, filter);
+                       }
+       }
+
+       // Simplify UI implementation
+       private void addProperty(Composite parent, String propName, String value) {
+               Label propLbl = new Label(parent, SWT.NONE);
+               //propLbl.setText(ConnectUtils.replaceAmpersand(propName + ": " + value));
+               propLbl.setText(value);
+               //CmsUiUtils.markup(propLbl);
+       }
+
+       public Path getCurrentFolder() {
+               return currentFolder;
+       }
+
+}
diff --git a/library/org.argeo.library.ui/src/org/argeo/library/ui/DocumentsFolderUiProvider.java b/library/org.argeo.library.ui/src/org/argeo/library/ui/DocumentsFolderUiProvider.java
new file mode 100644 (file)
index 0000000..bdc194b
--- /dev/null
@@ -0,0 +1,44 @@
+package org.argeo.library.ui;
+
+import java.nio.file.Path;
+import java.nio.file.spi.FileSystemProvider;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.fs.CmsFsUtils;
+import org.argeo.cms.ui.CmsUiProvider;
+import org.argeo.cms.ui.CmsView;
+import org.argeo.cms.ui.util.CmsUiUtils;
+import org.argeo.jcr.Jcr;
+import org.argeo.suite.ui.SuiteEvent;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/** UI provider of a document folder. */
+public class DocumentsFolderUiProvider implements CmsUiProvider {
+       private FileSystemProvider nodeFileSystemProvider;
+
+       @Override
+       public Control createUi(Composite parent, Node context) throws RepositoryException {
+               CmsView cmsView = CmsView.getCmsView(parent);
+               DocumentsFolderComposite dfc = new DocumentsFolderComposite(parent, SWT.NONE, context) {
+
+                       @Override
+                       protected void externalNavigateTo(Path path) {
+                               Node folderNode = cmsView.doAs(() -> CmsFsUtils.getNode(Jcr.getSession(context).getRepository(), path));
+                               parent.addDisposeListener((e1) -> Jcr.logout(folderNode));
+                               cmsView.sendEvent(SuiteEvent.openNewPart.topic(), SuiteEvent.eventProperties(folderNode));
+                       }
+               };
+               dfc.setLayoutData(CmsUiUtils.fillAll());
+               dfc.populate(cmsView.doAs(() -> CmsFsUtils.getPath(nodeFileSystemProvider, context)));
+               return dfc;
+       }
+
+       public void setNodeFileSystemProvider(FileSystemProvider nodeFileSystemProvider) {
+               this.nodeFileSystemProvider = nodeFileSystemProvider;
+       }
+
+}
diff --git a/library/org.argeo.library.ui/src/org/argeo/library/ui/DocumentsTreeUiProvider.java b/library/org.argeo.library.ui/src/org/argeo/library/ui/DocumentsTreeUiProvider.java
new file mode 100644 (file)
index 0000000..4660515
--- /dev/null
@@ -0,0 +1,80 @@
+package org.argeo.library.ui;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.spi.FileSystemProvider;
+
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+
+import org.argeo.api.NodeConstants;
+import org.argeo.api.NodeUtils;
+import org.argeo.cms.fs.CmsFsUtils;
+import org.argeo.cms.ui.CmsUiProvider;
+import org.argeo.cms.ui.CmsView;
+import org.argeo.cms.ui.util.CmsUiUtils;
+import org.argeo.eclipse.ui.fs.FsTreeViewer;
+import org.argeo.jcr.Jcr;
+import org.argeo.suite.ui.SuiteEvent;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/** Tree view of a user root folders. */
+public class DocumentsTreeUiProvider implements CmsUiProvider {
+       private FileSystemProvider nodeFileSystemProvider;
+       private Repository repository;
+
+       @Override
+       public Control createUi(Composite parent, Node context) throws RepositoryException {
+               parent.setLayout(new GridLayout());
+               FsTreeViewer fsTreeViewer = new FsTreeViewer(parent, SWT.NONE);
+               fsTreeViewer.configureDefaultSingleColumnTable(500);
+               CmsView cmsView = CmsView.getCmsView(parent);
+               Node homeNode = NodeUtils.getUserHome(cmsView.doAs(() -> Jcr.login(repository, NodeConstants.HOME_WORKSPACE)));
+               parent.addDisposeListener((e1) -> Jcr.logout(homeNode));
+               Path homePath = CmsFsUtils.getPath(nodeFileSystemProvider, homeNode);
+               fsTreeViewer.addSelectionChangedListener((e) -> {
+                       IStructuredSelection selection = (IStructuredSelection) fsTreeViewer.getSelection();
+                       if (selection.isEmpty())
+                               return;
+                       else {
+                               Path newSelected = (Path) selection.getFirstElement();
+                               if (Files.isDirectory(newSelected)) {
+                                       Node folderNode = cmsView.doAs(() -> CmsFsUtils.getNode(repository, newSelected));
+                                       parent.addDisposeListener((e1) -> Jcr.logout(folderNode));
+                                       cmsView.sendEvent(SuiteEvent.refreshPart.topic(), SuiteEvent.eventProperties(folderNode));
+                               }
+                       }
+               });
+               fsTreeViewer.addDoubleClickListener((e) -> {
+                       IStructuredSelection selection = (IStructuredSelection) fsTreeViewer.getSelection();
+                       if (selection.isEmpty())
+                               return;
+                       else {
+                               Path newSelected = (Path) selection.getFirstElement();
+                               if (Files.isDirectory(newSelected)) {
+                                       Node folderNode = cmsView.doAs(() -> CmsFsUtils.getNode(repository, newSelected));
+                                       parent.addDisposeListener((e1) -> Jcr.logout(folderNode));
+                                       cmsView.sendEvent(SuiteEvent.openNewPart.topic(), SuiteEvent.eventProperties(folderNode));
+                               }
+                       }
+               });
+               fsTreeViewer.setPathsInput(homePath);
+               fsTreeViewer.getControl().setLayoutData(CmsUiUtils.fillAll());
+               fsTreeViewer.getControl().getParent().layout(true, true);
+               return fsTreeViewer.getControl();
+       }
+
+       public void setNodeFileSystemProvider(FileSystemProvider nodeFileSystemProvider) {
+               this.nodeFileSystemProvider = nodeFileSystemProvider;
+       }
+
+       public void setRepository(Repository repository) {
+               this.repository = repository;
+       }
+
+}
diff --git a/library/org.argeo.library.ui/src/org/argeo/library/ui/DocumentsUiService.java b/library/org.argeo.library.ui/src/org/argeo/library/ui/DocumentsUiService.java
new file mode 100644 (file)
index 0000000..ad13d28
--- /dev/null
@@ -0,0 +1,310 @@
+package org.argeo.library.ui;
+
+import static org.argeo.cms.ui.dialogs.CmsMessageDialog.openConfirm;
+import static org.argeo.cms.ui.dialogs.CmsMessageDialog.openError;
+import static org.argeo.cms.ui.dialogs.SingleValueDialog.ask;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.nio.file.DirectoryNotEmptyException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.ui.dialogs.CmsFeedback;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.eclipse.ui.specific.OpenFile;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Shell;
+
+public class DocumentsUiService {
+       private final static Log log = LogFactory.getLog(DocumentsUiService.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_RENAME = "rename";
+       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";
+       public final static String ACTION_ID_DELETE_BOOKMARK = "deleteBookmark";
+       public final static String ACTION_ID_RENAME_BOOKMARK = "renameBookmark";
+
+       public 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_RENAME:
+                       return "Rename";
+               case ACTION_ID_DELETE:
+                       return "Delete";
+               case ACTION_ID_UPLOAD_FILE:
+                       return "Upload Files";
+//             case ACTION_ID_OPEN:
+//                     return "Open";
+               case ACTION_ID_DELETE_BOOKMARK:
+                       return "Delete bookmark";
+               case ACTION_ID_RENAME_BOOKMARK:
+                       return "Rename bookmark";
+               default:
+                       throw new IllegalArgumentException("Unknown action ID " + actionId);
+               }
+       }
+
+       public void openFile(Path toOpenPath) {
+               try {
+                       String name = toOpenPath.getFileName().toString();
+                       File tmpFile = File.createTempFile("tmp", name);
+                       tmpFile.deleteOnExit();
+                       try (OutputStream os = new FileOutputStream(tmpFile)) {
+                               Files.copy(toOpenPath, os);
+                       } catch (IOException e) {
+                               throw new IllegalStateException("Cannot open copy " + name + " to tmpFile.", e);
+                       }
+                       String uri = Paths.get(tmpFile.getAbsolutePath()).toUri().toString();
+                       Map<String, String> params = new HashMap<String, String>();
+                       params.put(OpenFile.PARAM_FILE_NAME, name);
+                       params.put(OpenFile.PARAM_FILE_URI, uri);
+                       // FIXME open file without a command
+                       // CommandUtils.callCommand(OpenFile.ID, params);
+               } catch (IOException e1) {
+                       throw new IllegalStateException("Cannot create tmp copy of " + toOpenPath, e1);
+               }
+       }
+
+       public boolean deleteItems(Shell shell, IStructuredSelection selection) {
+               if (selection.isEmpty())
+                       return false;
+
+               StringBuilder builder = new StringBuilder();
+               @SuppressWarnings("unchecked")
+               Iterator<Object> iterator = selection.iterator();
+               List<Path> 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 (openConfirm(msg)) {
+                       for (Path path : paths) {
+                               try {
+                                       // recursively delete directory and its content
+                                       Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
+                                               @Override
+                                               public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+                                                       Files.delete(file);
+                                                       return FileVisitResult.CONTINUE;
+                                               }
+
+                                               @Override
+                                               public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
+                                                       Files.delete(dir);
+                                                       return FileVisitResult.CONTINUE;
+                                               }
+                                       });
+                               } catch (DirectoryNotEmptyException e) {
+                                       String errMsg = path.getFileName() + " cannot be deleted: directory is not empty.";
+                                       openError( errMsg);
+                                       throw new IllegalArgumentException("Cannot delete path " + path, e);
+                               } catch (IOException e) {
+                                       String errMsg = e.toString();
+                                       openError(errMsg);
+                                       throw new IllegalArgumentException("Cannot delete path " + path, e);
+                               }
+                       }
+                       return true;
+               }
+               return false;
+       }
+
+       public boolean renameItem(Shell shell, Path parentFolderPath, Path toRenamePath) {
+               String msg = "Enter a new name:";
+               String name = ask( msg, toRenamePath.getFileName().toString());
+               // TODO enhance check of name validity
+               if (EclipseUiUtils.notEmpty(name)) {
+                       try {
+                               Path child = parentFolderPath.resolve(name);
+                               if (Files.exists(child)) {
+                                       String errMsg = "An object named " + name + " already exists at " + parentFolderPath.toString()
+                                                       + ", please provide another name";
+                                       openError( errMsg);
+                                       throw new IllegalArgumentException(errMsg);
+                               } else {
+                                       Files.move(toRenamePath, child);
+                                       return true;
+                               }
+                       } catch (IOException e) {
+                               throw new IllegalStateException("Cannot rename " + name + " at " + parentFolderPath.toString(), e);
+                       }
+               }
+               return false;
+       }
+
+       public boolean createFolder(Shell shell, Path currFolderPath) {
+               String msg = "Enter a name:";
+               String name = ask( msg);
+               // TODO enhance check of name validity
+               if (EclipseUiUtils.notEmpty(name)) {
+                       name = name.trim();
+                       try {
+                               Path child = currFolderPath.resolve(name);
+                               if (Files.exists(child)) {
+                                       String errMsg = "A folder named " + name + " already exists at " + currFolderPath.toString()
+                                                       + ", cannot create";
+                                       openError(errMsg);
+                                       throw new IllegalArgumentException(errMsg);
+                               } else {
+                                       Files.createDirectories(child);
+                                       return true;
+                               }
+                       } catch (IOException e) {
+                               throw new IllegalStateException("Cannot create folder " + name + " at " + currFolderPath.toString(), e);
+                       }
+               }
+               return false;
+       }
+
+//     public void bookmarkFolder(Path toBookmarkPath, Repository repository, DocumentsService documentsService) {
+//             String msg = "Provide a name:";
+//             String name = SingleQuestion.ask("Create bookmark", msg, toBookmarkPath.getFileName().toString());
+//             if (EclipseUiUtils.notEmpty(name))
+//                     documentsService.createFolderBookmark(toBookmarkPath, name, repository);
+//     }
+
+       public boolean uploadFiles(Shell shell, Path currFolderPath) {
+//             shell = Display.getCurrent().getActiveShell();// ignore argument
+               try {
+                       FileDialog dialog = new FileDialog(shell, 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 false;
+                               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)) {
+                                                               openError(
+                                                                               "Upload of directories in the system is not yet implemented");
+                                                               continue loop;
+                                                       }
+                                                       Path targetPath = currFolderPath.resolve(tmpPath.getFileName().toString());
+                                                       try (InputStream in = new FileInputStream(tmpPath.toFile())) {
+                                                               Files.copy(in, targetPath);
+                                                               Files.delete(tmpPath);
+                                                       }
+                                                       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";
+                                                       openError( msg);
+                                                       continue loop;
+                                               }
+                                       }
+                                       return true;
+                               }
+                       }
+               } catch (Exception e) {
+                       CmsFeedback.show("Cannot import files to " + currFolderPath,e);
+               }
+               return false;
+       }
+
+//     public boolean deleteBookmark(Shell shell, IStructuredSelection selection, Node bookmarkParent) {
+//             if (selection.isEmpty())
+//                     return false;
+//
+//             StringBuilder builder = new StringBuilder();
+//             @SuppressWarnings("unchecked")
+//             Iterator<Object> iterator = selection.iterator();
+//             List<Node> nodes = new ArrayList<>();
+//
+//             while (iterator.hasNext()) {
+//                     Node node = (Node) iterator.next();
+//                     builder.append(Jcr.get(node, Property.JCR_TITLE) + ", ");
+//                     nodes.add(node);
+//             }
+//             String msg = "You are about to delete following bookmark: " + builder.substring(0, builder.length() - 2)
+//                             + ". Are you sure?";
+//             if (MessageDialog.openConfirm(shell, "Confirm deletion", msg)) {
+//                     Session session = Jcr.session(bookmarkParent);
+//                     try {
+//                             if (session.hasPendingChanges())
+//                                     throw new DocumentsException("Cannot remove bookmarks, session is not clean");
+//                             for (Node path : nodes)
+//                                     path.remove();
+//                             bookmarkParent.getSession().save();
+//                             return true;
+//                     } catch (RepositoryException e) {
+//                             JcrUtils.discardQuietly(session);
+//                             throw new DocumentsException("Cannot delete bookmarks " + builder.toString(), e);
+//                     }
+//             }
+//             return false;
+//     }
+
+//     public boolean renameBookmark(IStructuredSelection selection) {
+//             if (selection.isEmpty() || selection.size() > 1)
+//                     return false;
+//             Node toRename = (Node) selection.getFirstElement();
+//             String msg = "Please provide a new name.";
+//             String name = SingleQuestion.ask("Rename bookmark", msg, ConnectJcrUtils.get(toRename, Property.JCR_TITLE));
+//             if (EclipseUiUtils.notEmpty(name)
+//                             && ConnectJcrUtils.setJcrProperty(toRename, Property.JCR_TITLE, PropertyType.STRING, name)) {
+//                     ConnectJcrUtils.saveIfNecessary(toRename);
+//                     return true;
+//             }
+//             return false;
+//     }
+}
index ea3ef63ea4395507d25c3de28695f60f2faebdf3..ea051de789e313d618af538006c9d6c8baf882d6 100644 (file)
@@ -11,6 +11,6 @@
        <name>Argeo Library Components</name>
        <packaging>pom</packaging>
        <modules>
-               <module>org.argeo.documents.ui</module>
+               <module>org.argeo.library.ui</module>
        </modules>
 </project>