From: Mathieu Baudier Date: Wed, 5 May 2021 09:14:46 +0000 (+0200) Subject: Introduce content layer. X-Git-Tag: argeo-suite-2.3.1~53 X-Git-Url: https://git.argeo.org/?p=gpl%2Fargeo-suite.git;a=commitdiff_plain;h=e8d31e8321bb18489b37a53a32db833f8efb5ddf Introduce content layer. --- diff --git a/core/org.argeo.entity.api/src/org/argeo/entity/EntityType.java b/core/org.argeo.entity.api/src/org/argeo/entity/EntityType.java index ecd6330..84fa16f 100644 --- a/core/org.argeo.entity.api/src/org/argeo/entity/EntityType.java +++ b/core/org.argeo.entity.api/src/org/argeo/entity/EntityType.java @@ -4,6 +4,8 @@ package org.argeo.entity; public enum EntityType implements JcrName { // entity entity, local, relatedTo, + // structure + space, // typology typologies, terms, term, // form diff --git a/core/org.argeo.entity.api/src/org/argeo/entity/entity.cnd b/core/org.argeo.entity.api/src/org/argeo/entity/entity.cnd index b64a275..cd3f327 100644 --- a/core/org.argeo.entity.api/src/org/argeo/entity/entity.cnd +++ b/core/org.argeo.entity.api/src/org/argeo/entity/entity.cnd @@ -35,6 +35,12 @@ mixin //+ * (entity:reference) //+ * (entity:composite) +// +// STRUCTURE +// +[entity:space] +mixin + // // TYPOLOGY // diff --git a/core/org.argeo.suite.ui/OSGI-INF/leadPane.xml b/core/org.argeo.suite.ui/OSGI-INF/leadPane.xml index 1aeec0a..c43d933 100644 --- a/core/org.argeo.suite.ui/OSGI-INF/leadPane.xml +++ b/core/org.argeo.suite.ui/OSGI-INF/leadPane.xml @@ -6,7 +6,7 @@ argeo.suite.ui.dashboardLayer -argeo.documents.ui.documentsLayer +argeo.library.ui.contentLayer argeo.people.ui.peopleLayer argeo.geo.ui.mapLayer 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 index 0000000..3434ed5 --- /dev/null +++ b/core/org.argeo.suite.ui/src/org/argeo/suite/ui/widgets/TreeOrSearchArea.java @@ -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 index e801ebf..0000000 --- a/library/org.argeo.documents.ui/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/library/org.argeo.documents.ui/.gitignore b/library/org.argeo.documents.ui/.gitignore deleted file mode 100644 index 09e3bc9..0000000 --- a/library/org.argeo.documents.ui/.gitignore +++ /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 index c046a7d..0000000 --- a/library/org.argeo.documents.ui/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.argeo.documents.ui - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/library/org.argeo.documents.ui/META-INF/.gitignore b/library/org.argeo.documents.ui/META-INF/.gitignore deleted file mode 100644 index 4854a41..0000000 --- a/library/org.argeo.documents.ui/META-INF/.gitignore +++ /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 index a7c2e4b..0000000 --- a/library/org.argeo.documents.ui/OSGI-INF/documentsFolder.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - 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 index 3833237..0000000 --- a/library/org.argeo.documents.ui/OSGI-INF/documentsLayer.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - 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 index 50fcfe3..0000000 --- a/library/org.argeo.documents.ui/OSGI-INF/entryArea.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - 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 index 8015421..0000000 --- a/library/org.argeo.documents.ui/OSGI-INF/l10n/bundle.properties +++ /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 index 9a3a99c..0000000 --- a/library/org.argeo.documents.ui/bnd.bnd +++ /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 index 0a508e2..0000000 --- a/library/org.argeo.documents.ui/build.properties +++ /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 index 349e930..0000000 --- a/library/org.argeo.documents.ui/config/documentsFolder.properties +++ /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 index 78382a6..0000000 --- a/library/org.argeo.documents.ui/config/documentsLayer.properties +++ /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 index 43b08f0..0000000 --- a/library/org.argeo.documents.ui/config/entryArea.properties +++ /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 index 2c59ff3..0000000 --- a/library/org.argeo.documents.ui/pom.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - 4.0.0 - - org.argeo.suite - library - 2.3.1-SNAPSHOT - .. - - org.argeo.documents.ui - Documents UI - jar - - - org.argeo.suite - org.argeo.suite.ui - 2.3.1-SNAPSHOT - - - - - org.argeo.tp - argeo-tp-rap-e4 - ${version.argeo-tp} - pom - provided - - - - org.argeo.commons - org.argeo.eclipse.ui.rap - ${version.argeo-commons} - provided - - - - 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 index 3757dc7..0000000 --- a/library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsContextMenu.java +++ /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 index 84c56a3..0000000 --- a/library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsFileComposite.java +++ /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 = "Unknown"; - 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 index 289a0cd..0000000 --- a/library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsFolderComposite.java +++ /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 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 = "Unknown"; - addProperty(rightPanelCmp, "Type", mimeType); - addProperty(rightPanelCmp, "Size", FsUiUtils.humanReadableByteCount(Files.size(path), false)); - } - - // read all attributes -// Map 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 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 index e525a9e..0000000 --- a/library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsFolderUiProvider.java +++ /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 index 13c26e5..0000000 --- a/library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsTreeUiProvider.java +++ /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 index a0fa782..0000000 --- a/library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsUiService.java +++ /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 params = new HashMap(); - 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 iterator = selection.iterator(); - List paths = new ArrayList<>(); - - while (iterator.hasNext()) { - Path path = (Path) iterator.next(); - builder.append(path.getFileName() + ", "); - paths.add(path); - } - String msg = "You are about to delete following elements: " + builder.substring(0, builder.length() - 2) - + ". Are you sure?"; - if (openConfirm(msg)) { - for (Path path : paths) { - try { - // recursively delete directory and its content - Files.walkFileTree(path, new SimpleFileVisitor() { - @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 iterator = selection.iterator(); -// List 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 index 0000000..e801ebf --- /dev/null +++ b/library/org.argeo.library.ui/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/library/org.argeo.library.ui/.gitignore b/library/org.argeo.library.ui/.gitignore new file mode 100644 index 0000000..09e3bc9 --- /dev/null +++ b/library/org.argeo.library.ui/.gitignore @@ -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 index 0000000..6aa2010 --- /dev/null +++ b/library/org.argeo.library.ui/.project @@ -0,0 +1,33 @@ + + + org.argeo.library.ui + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.pde.ds.core.builder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/library/org.argeo.library.ui/META-INF/.gitignore b/library/org.argeo.library.ui/META-INF/.gitignore new file mode 100644 index 0000000..4854a41 --- /dev/null +++ b/library/org.argeo.library.ui/META-INF/.gitignore @@ -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 index 0000000..0b5646e --- /dev/null +++ b/library/org.argeo.library.ui/OSGI-INF/contentEntryArea.xml @@ -0,0 +1,8 @@ + + + + + + + + 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 index 0000000..0dae1af --- /dev/null +++ b/library/org.argeo.library.ui/OSGI-INF/contentLayer.xml @@ -0,0 +1,9 @@ + + + + + + + + + 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 index 0000000..d7d71f0 --- /dev/null +++ b/library/org.argeo.library.ui/OSGI-INF/documentsFolder.xml @@ -0,0 +1,9 @@ + + + + + + + + + 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 index 0000000..540f4ff --- /dev/null +++ b/library/org.argeo.library.ui/OSGI-INF/fsEntryArea.xml @@ -0,0 +1,10 @@ + + + + + + + + + + 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 index 0000000..8015421 --- /dev/null +++ b/library/org.argeo.library.ui/OSGI-INF/l10n/bundle.properties @@ -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 index 0000000..71f4618 --- /dev/null +++ b/library/org.argeo.library.ui/bnd.bnd @@ -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 index 0000000..0859c52 --- /dev/null +++ b/library/org.argeo.library.ui/build.properties @@ -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 index 0000000..855fe97 --- /dev/null +++ b/library/org.argeo.library.ui/config/contentEntryArea.properties @@ -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 index 0000000..ad7b10e --- /dev/null +++ b/library/org.argeo.library.ui/config/contentLayer.properties @@ -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 index 0000000..349e930 --- /dev/null +++ b/library/org.argeo.library.ui/config/documentsFolder.properties @@ -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 index 0000000..0bceaf0 --- /dev/null +++ b/library/org.argeo.library.ui/config/fsEntryArea.properties @@ -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 index 0000000..048dd4a --- /dev/null +++ b/library/org.argeo.library.ui/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + + org.argeo.suite + library + 2.3.1-SNAPSHOT + .. + + org.argeo.library.ui + Documents UI + jar + + + org.argeo.suite + org.argeo.suite.ui + 2.3.1-SNAPSHOT + + + + + org.argeo.tp + argeo-tp-rap-e4 + ${version.argeo-tp} + pom + provided + + + + org.argeo.commons + org.argeo.eclipse.ui.rap + ${version.argeo-commons} + provided + + + + 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 index 0000000..4c8b041 --- /dev/null +++ b/library/org.argeo.library.ui/src/org/argeo/library/ui/ContentEntryArea.java @@ -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 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 index 0000000..03de251 --- /dev/null +++ b/library/org.argeo.library.ui/src/org/argeo/library/ui/DocumentsContextMenu.java @@ -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 index 0000000..10cf3bd --- /dev/null +++ b/library/org.argeo.library.ui/src/org/argeo/library/ui/DocumentsFileComposite.java @@ -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 = "Unknown"; + 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 index 0000000..a686074 --- /dev/null +++ b/library/org.argeo.library.ui/src/org/argeo/library/ui/DocumentsFolderComposite.java @@ -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 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 = "Unknown"; + addProperty(rightPanelCmp, "Type", mimeType); + addProperty(rightPanelCmp, "Size", FsUiUtils.humanReadableByteCount(Files.size(path), false)); + } + + // read all attributes +// Map 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 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 index 0000000..bdc194b --- /dev/null +++ b/library/org.argeo.library.ui/src/org/argeo/library/ui/DocumentsFolderUiProvider.java @@ -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 index 0000000..4660515 --- /dev/null +++ b/library/org.argeo.library.ui/src/org/argeo/library/ui/DocumentsTreeUiProvider.java @@ -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 index 0000000..ad13d28 --- /dev/null +++ b/library/org.argeo.library.ui/src/org/argeo/library/ui/DocumentsUiService.java @@ -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 params = new HashMap(); + 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 iterator = selection.iterator(); + List paths = new ArrayList<>(); + + while (iterator.hasNext()) { + Path path = (Path) iterator.next(); + builder.append(path.getFileName() + ", "); + paths.add(path); + } + String msg = "You are about to delete following elements: " + builder.substring(0, builder.length() - 2) + + ". Are you sure?"; + if (openConfirm(msg)) { + for (Path path : paths) { + try { + // recursively delete directory and its content + Files.walkFileTree(path, new SimpleFileVisitor() { + @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 iterator = selection.iterator(); +// List 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/pom.xml b/library/pom.xml index ea3ef63..ea051de 100644 --- a/library/pom.xml +++ b/library/pom.xml @@ -11,6 +11,6 @@ Argeo Library Components pom - org.argeo.documents.ui + org.argeo.library.ui