From: Mathieu Baudier Date: Sun, 10 Jul 2022 05:46:29 +0000 (+0200) Subject: Refactor SWT directory structure. X-Git-Tag: v2.3.10~130 X-Git-Url: http://git.argeo.org/?a=commitdiff_plain;h=7b242851c0094d13cbaca5b68261ad92c873a59f;p=lgpl%2Fargeo-commons.git Refactor SWT directory structure. --- diff --git a/Makefile b/Makefile index 8688ce849..ee0604630 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,7 @@ org.argeo.init \ org.argeo.util \ org.argeo.api.uuid \ org.argeo.api.acr \ +org.argeo.api.cli \ org.argeo.api.cms \ org.argeo.cms \ org.argeo.cms.sql \ @@ -23,11 +24,12 @@ org.argeo.cms.ssh \ org.argeo.cms.ux \ eclipse/org.argeo.ext.equinox.jetty \ eclipse/org.argeo.cms.servlet \ -eclipse/org.argeo.cms.swt \ -eclipse/org.argeo.cms.e4 \ -rap/org.argeo.cms.ui.rap \ -rap/org.argeo.swt.specific.rap \ -rap/org.argeo.cms.e4.rap \ +swt/org.argeo.cms.swt \ +swt/org.argeo.cms.e4 \ +swt/rap/org.argeo.swt.specific.rap \ +swt/rap/org.argeo.cms.swt.rap \ +swt/rap/org.argeo.cms.swt.rap.cli \ +swt/rap/org.argeo.cms.e4.rap \ jcr/org.argeo.cms.jcr \ jcr/org.argeo.cms.ui \ @@ -44,7 +46,7 @@ org.argeo.api.cms A2_OUTPUT = $(SDK_BUILD_BASE)/a2 A2_BASE = $(A2_OUTPUT) -VPATH = .:eclipse:rap:jcr +VPATH = .:eclipse:rap:jcr:swt:swt/rap DEP_CATEGORIES = \ org.argeo.tp \ @@ -64,4 +66,16 @@ clean: $(MAKE) -C jni clean $(MAKE) -f Makefile-rcp.mk clean +A2_BUNDLES_CLASSPATH = $(subst $(space),$(pathsep),$(strip $(A2_BUNDLES))) + +native-image: + mkdir -p $(A2_OUTPUT)/libexec/$(A2_CATEGORY) + cd $(A2_OUTPUT)/libexec/$(A2_CATEGORY) && /opt/graalvm-ce/bin/native-image \ + -cp $(A2_CLASSPATH):$(A2_BUNDLES_CLASSPATH) org.argeo.eclipse.ui.jetty.CmsRapCli \ + --enable-url-protocols=http,https \ + -H:AdditionalSecurityProviders=sun.security.jgss.SunProvider,org.bouncycastle.jce.provider.BouncyCastleProvider,net.i2p.crypto.eddsa.EdDSASecurityProvider \ + --initialize-at-build-time=org.argeo.init.logging.ThinLogging,org.slf4j.LoggerFactory \ + --no-fallback + + include $(SDK_SRC_BASE)/sdk/argeo-build/osgi.mk \ No newline at end of file diff --git a/Makefile-rcp.mk b/Makefile-rcp.mk index 02ca52962..8b96683fc 100644 --- a/Makefile-rcp.mk +++ b/Makefile-rcp.mk @@ -6,10 +6,11 @@ all: osgi A2_CATEGORY = org.argeo.cms.eclipse.rcp BUNDLES = \ -rcp/org.argeo.cms.e4.rcp \ -rcp/org.argeo.cms.ui.rcp \ rcp/org.argeo.swt.minidesktop \ -rcp/org.argeo.swt.specific.rcp \ +swt/rcp/org.argeo.swt.specific.rcp \ +swt/rcp/org.argeo.cms.swt.rcp \ +swt/rcp/org.argeo.cms.swt.rcp.cli \ +swt/rcp/org.argeo.cms.e4.rcp \ A2_OUTPUT = $(SDK_BUILD_BASE)/a2 A2_BASE = $(A2_OUTPUT) @@ -30,6 +31,6 @@ org.argeo.tp.jcr clean: rm -rf $(BUILD_BASE) -VPATH = .:rcp +VPATH = .:rcp:swt/rcp include $(SDK_SRC_BASE)/sdk/argeo-build/osgi.mk \ No newline at end of file diff --git a/eclipse/org.argeo.cms.e4/.classpath b/eclipse/org.argeo.cms.e4/.classpath deleted file mode 100644 index e801ebfb4..000000000 --- a/eclipse/org.argeo.cms.e4/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/eclipse/org.argeo.cms.e4/.project b/eclipse/org.argeo.cms.e4/.project deleted file mode 100644 index 0c0406952..000000000 --- a/eclipse/org.argeo.cms.e4/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.argeo.cms.e4 - - - - - - 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/eclipse/org.argeo.cms.e4/META-INF/.gitignore b/eclipse/org.argeo.cms.e4/META-INF/.gitignore deleted file mode 100644 index 4854a41b9..000000000 --- a/eclipse/org.argeo.cms.e4/META-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/MANIFEST.MF diff --git a/eclipse/org.argeo.cms.e4/OSGI-INF/defaultCallbackHandler.xml b/eclipse/org.argeo.cms.e4/OSGI-INF/defaultCallbackHandler.xml deleted file mode 100644 index fcd3ae5cb..000000000 --- a/eclipse/org.argeo.cms.e4/OSGI-INF/defaultCallbackHandler.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/eclipse/org.argeo.cms.e4/OSGI-INF/homeRepository.xml b/eclipse/org.argeo.cms.e4/OSGI-INF/homeRepository.xml deleted file mode 100644 index 65690f262..000000000 --- a/eclipse/org.argeo.cms.e4/OSGI-INF/homeRepository.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/eclipse/org.argeo.cms.e4/OSGI-INF/userAdminWrapper.xml b/eclipse/org.argeo.cms.e4/OSGI-INF/userAdminWrapper.xml deleted file mode 100644 index cc7087b6e..000000000 --- a/eclipse/org.argeo.cms.e4/OSGI-INF/userAdminWrapper.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/eclipse/org.argeo.cms.e4/bnd.bnd b/eclipse/org.argeo.cms.e4/bnd.bnd deleted file mode 100644 index 8026f9869..000000000 --- a/eclipse/org.argeo.cms.e4/bnd.bnd +++ /dev/null @@ -1,19 +0,0 @@ -Service-Component: OSGI-INF/homeRepository.xml,\ -OSGI-INF/userAdminWrapper.xml,\ -OSGI-INF/defaultCallbackHandler.xml -Bundle-ActivationPolicy: lazy - -Import-Package: \ -org.argeo.api.acr,\ -org.eclipse.swt,\ -org.eclipse.swt.widgets;version="0.0.0",\ -org.eclipse.e4.ui.model.application.ui,\ -org.eclipse.e4.ui.model.application,\ -javax.jcr.nodetype,\ -org.argeo.cms,\ -org.eclipse.core.commands.common,\ -org.eclipse.jface.window,\ -org.argeo.cms.swt.auth,\ -org.apache.jackrabbit.*;version="[2,3)",\ -javax.servlet.*;version="[3,5)",\ -* diff --git a/eclipse/org.argeo.cms.e4/build.properties b/eclipse/org.argeo.cms.e4/build.properties deleted file mode 100644 index e46a7baee..000000000 --- a/eclipse/org.argeo.cms.e4/build.properties +++ /dev/null @@ -1,9 +0,0 @@ -output.. = bin/ -bin.includes = META-INF/,\ - OSGI-INF/,\ - .,\ - OSGI-INF/homeRepository.xml,\ - OSGI-INF/userAdminWrapper.xml,\ - OSGI-INF/defaultCallbackHandler.xml,\ - e4xmi/cms-demo.e4xmi -source.. = src/ diff --git a/eclipse/org.argeo.cms.e4/e4xmi/cms-devops.e4xmi b/eclipse/org.argeo.cms.e4/e4xmi/cms-devops.e4xmi deleted file mode 100644 index 89bcc376d..000000000 --- a/eclipse/org.argeo.cms.e4/e4xmi/cms-devops.e4xmi +++ /dev/null @@ -1,129 +0,0 @@ - - - - - shellMaximized - auth.cn=admin,ou=roles,ou=node - - - auth.cn=admin,ou=roles,ou=node - - - - - - - - - - - - - usersEditorArea - - - - - - - - - - - - - - - - - - - - - - - - - ViewMenu - - - - - - - - - - dataExplorer - - - - - - - - - - - - - - - - - - - - - - - - - auth.cn=admin,ou=roles,ou=node - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/eclipse/org.argeo.cms.e4/e4xmi/cms-ego.e4xmi b/eclipse/org.argeo.cms.e4/e4xmi/cms-ego.e4xmi deleted file mode 100644 index ef659fcc7..000000000 --- a/eclipse/org.argeo.cms.e4/e4xmi/cms-ego.e4xmi +++ /dev/null @@ -1,67 +0,0 @@ - - - - - shellMaximized - auth.cn=user,ou=roles,ou=node - - - - - - - - - - - ViewMenu - - - - - - - - - - dataExplorer - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/CmsE4Utils.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/CmsE4Utils.java deleted file mode 100644 index a997de748..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/CmsE4Utils.java +++ /dev/null @@ -1,77 +0,0 @@ -package org.argeo.cms.e4; - -import java.util.List; - -import org.argeo.cms.swt.CmsException; -import org.eclipse.e4.ui.model.application.MApplication; -import org.eclipse.e4.ui.model.application.commands.MCommand; -import org.eclipse.e4.ui.model.application.ui.basic.MPart; -import org.eclipse.e4.ui.model.application.ui.menu.MDirectMenuItem; -import org.eclipse.e4.ui.model.application.ui.menu.MHandledMenuItem; -import org.eclipse.e4.ui.workbench.modeling.EModelService; -import org.eclipse.e4.ui.workbench.modeling.EPartService; -import org.eclipse.e4.ui.workbench.modeling.EPartService.PartState; -import org.osgi.framework.Bundle; -import org.osgi.framework.FrameworkUtil; - -/** Static utilities simplifying recurring Eclipse 4 patterns. */ -public class CmsE4Utils { - /** Open an editor based on its id. */ - public static void openEditor(EPartService partService, String editorId, String key, String state) { - for (MPart part : partService.getParts()) { - String id = part.getPersistedState().get(key); - if (id != null && state.equals(id)) { - partService.showPart(part, PartState.ACTIVATE); - return; - } - } - - // new part - MPart part = partService.createPart(editorId); - if (part == null) - throw new CmsException("No editor found with id " + editorId); - part.getPersistedState().put(key, state); - partService.showPart(part, PartState.ACTIVATE); - } - - /** Dynamically creates an handled menu item from a command ID. */ - public static MHandledMenuItem createHandledMenuItem(EModelService modelService, MApplication app, - String commandId) { - MCommand command = findCommand(modelService, app, commandId); - if (command == null) - return null; - MHandledMenuItem handledItem = modelService.createModelElement(MHandledMenuItem.class); - handledItem.setCommand(command); - return handledItem; - - } - - /** - * Finds a command by ID. - * - * @return the {@link MCommand} or null if not found. - */ - public static MCommand findCommand(EModelService modelService, MApplication app, String commandId) { - List cmds = modelService.findElements(app, null, MCommand.class, null); - for (MCommand cmd : cmds) { - if (cmd.getElementId().equals(commandId)) { - return cmd; - } - } - return null; - } - - /** Dynamically creates a direct menu item from a class. */ - public static MDirectMenuItem createDirectMenuItem(EModelService modelService, Class clss, String label) { - MDirectMenuItem dynamicItem = modelService.createModelElement(MDirectMenuItem.class); - dynamicItem.setLabel(label); - Bundle bundle = FrameworkUtil.getBundle(clss); - dynamicItem.setContributionURI("bundleclass://" + bundle.getSymbolicName() + "/" + clss.getName()); - return dynamicItem; - } - - /** Singleton. */ - private CmsE4Utils() { - } - -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/OsgiFilterContextFunction.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/OsgiFilterContextFunction.java deleted file mode 100644 index 1e3e75cec..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/OsgiFilterContextFunction.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.argeo.cms.e4; - -import org.argeo.cms.swt.CmsException; -import org.eclipse.e4.core.contexts.ContextFunction; -import org.eclipse.e4.core.contexts.IEclipseContext; -import org.eclipse.e4.core.di.IInjector; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.framework.ServiceReference; - -/** An Eclipse 4 {@link ContextFunction} based on an OSGi filter. */ -public class OsgiFilterContextFunction extends ContextFunction { - - private BundleContext bc = FrameworkUtil.getBundle(OsgiFilterContextFunction.class).getBundleContext(); - - @Override - public Object compute(IEclipseContext context, String contextKey) { - ServiceReference[] srs; - try { - srs = bc.getServiceReferences((String) null, contextKey); - } catch (InvalidSyntaxException e) { - throw new CmsException("Context key " + contextKey + " must be a valid osgi filter", e); - } - if (srs == null || srs.length == 0) { - return IInjector.NOT_A_VALUE; - } else { - // return the first one - return bc.getService(srs[0]); - } - } - -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/PrivilegedJob.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/PrivilegedJob.java deleted file mode 100644 index 89055d2ff..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/PrivilegedJob.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.argeo.cms.e4; - -import java.security.AccessControlContext; -import java.security.AccessController; -import java.security.PrivilegedAction; - -import javax.security.auth.Subject; - -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.jobs.Job; - -/** - * Propagate authentication to an eclipse job. Typically to execute a privileged - * action outside the UI thread - */ -public abstract class PrivilegedJob extends Job { - private final Subject subject; - - public PrivilegedJob(String jobName) { - this(jobName, AccessController.getContext()); - } - - public PrivilegedJob(String jobName, - AccessControlContext accessControlContext) { - super(jobName); - subject = Subject.getSubject(accessControlContext); - - // Must be called *before* the job is scheduled, - // it is required for the progress window to appear - setUser(true); - } - - @Override - protected IStatus run(final IProgressMonitor progressMonitor) { - PrivilegedAction privilegedAction = new PrivilegedAction() { - public IStatus run() { - return doRun(progressMonitor); - } - }; - return Subject.doAs(subject, privilegedAction); - } - - /** - * Implement here what should be executed with default context - * authentication - */ - protected abstract IStatus doRun(IProgressMonitor progressMonitor); -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/AuthAddon.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/AuthAddon.java deleted file mode 100644 index e84b18c0b..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/AuthAddon.java +++ /dev/null @@ -1,104 +0,0 @@ -package org.argeo.cms.e4.addons; - -import java.security.AccessController; -import java.util.Iterator; - -import javax.annotation.PostConstruct; -import javax.security.auth.Subject; -import javax.servlet.http.HttpServletRequest; - -import org.argeo.api.cms.CmsLog; -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.swt.CmsException; -import org.eclipse.e4.ui.model.application.MApplication; -import org.eclipse.e4.ui.model.application.ui.MElementContainer; -import org.eclipse.e4.ui.model.application.ui.MUIElement; -import org.eclipse.e4.ui.model.application.ui.basic.MTrimBar; -import org.eclipse.e4.ui.model.application.ui.basic.MTrimmedWindow; -import org.eclipse.e4.ui.model.application.ui.basic.MWindow; - -public class AuthAddon { - private final static CmsLog log = CmsLog.getLog(AuthAddon.class); - - public final static String AUTH = "auth."; - - @PostConstruct - void init(MApplication application) { - Iterator windows = application.getChildren().iterator(); - boolean atLeastOneTopLevelWindowVisible = false; - windows: while (windows.hasNext()) { - MWindow window = windows.next(); - // main window - boolean windowVisible = process(window); - if (!windowVisible) { -// windows.remove(); - continue windows; - } - atLeastOneTopLevelWindowVisible = true; - // trim bars - if (window instanceof MTrimmedWindow) { - Iterator trimBars = ((MTrimmedWindow) window).getTrimBars().iterator(); - while (trimBars.hasNext()) { - MTrimBar trimBar = trimBars.next(); - if (!process(trimBar)) { - trimBars.remove(); - } - } - } - } - - if (!atLeastOneTopLevelWindowVisible) { - log.warn("No top-level window is authorized for user " + CurrentUser.getUsername() + ", logging out.."); - logout(); - } - } - - protected boolean process(MUIElement element) { - for (String tag : element.getTags()) { - if (tag.startsWith(AUTH)) { - String role = tag.substring(AUTH.length(), tag.length()); - if (!CurrentUser.isInRole(role)) { - element.setVisible(false); - element.setToBeRendered(false); - return false; - } - } - } - - // children - if (element instanceof MElementContainer) { - @SuppressWarnings("unchecked") - MElementContainer container = (MElementContainer) element; - Iterator children = container.getChildren().iterator(); - while (children.hasNext()) { - MUIElement child = children.next(); - boolean visible = process(child); - if (!visible) - children.remove(); - } - - for (Object child : container.getChildren()) { - if (child instanceof MUIElement) { - boolean visible = process((MUIElement) child); - if (!visible) - container.getChildren().remove(child); - } - } - } - - return true; - } - - protected void logout() { - Subject subject = Subject.getSubject(AccessController.getContext()); - try { - CurrentUser.logoutCmsSession(subject); - } catch (Exception e) { - throw new CmsException("Cannot log out", e); - } - HttpServletRequest request = org.argeo.eclipse.ui.specific.UiContext.getHttpRequest(); - if (request != null) - request.getSession().setMaxInactiveInterval(0); - } - -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/LocaleAddon.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/LocaleAddon.java deleted file mode 100644 index 5bc0d6936..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/LocaleAddon.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.argeo.cms.e4.addons; - -import java.security.AccessController; -import java.util.List; -import java.util.Locale; -import java.util.Set; - -import javax.annotation.PostConstruct; -import javax.security.auth.Subject; - -import org.argeo.eclipse.ui.specific.UiContext; -import org.eclipse.e4.core.services.nls.ILocaleChangeService; -import org.eclipse.e4.ui.model.application.MApplication; -import org.eclipse.e4.ui.model.application.ui.basic.MWindow; -import org.eclipse.e4.ui.workbench.modeling.EModelService; -import org.eclipse.e4.ui.workbench.modeling.ElementMatcher; -import org.eclipse.swt.SWT; - -/** Integrate workbench with the locale provided at log in. */ -public class LocaleAddon { - private final static String STYLE_OVERRIDE = "styleOverride"; - - // Right to left languages - private final static String ARABIC = "ar"; - private final static String HEBREW = "he"; - - @PostConstruct - public void init(ILocaleChangeService localeChangeService, EModelService modelService, MApplication application) { - Subject subject = Subject.getSubject(AccessController.getContext()); - Set locales = subject.getPublicCredentials(Locale.class); - if (!locales.isEmpty()) { - Locale locale = locales.iterator().next(); - localeChangeService.changeApplicationLocale(locale); - UiContext.setLocale(locale); - - if (locale.getLanguage().equals(ARABIC) || locale.getLanguage().equals(HEBREW)) { - List windows = modelService.findElements(application, MWindow.class, EModelService.ANYWHERE, - new ElementMatcher(null, null, (String) null)); - for (MWindow window : windows) { - String currentStyle = window.getPersistedState().get(STYLE_OVERRIDE); - int style = 0; - if (currentStyle != null) { - style = Integer.parseInt(currentStyle); - } - style = style | SWT.RIGHT_TO_LEFT; - window.getPersistedState().put(STYLE_OVERRIDE, Integer.toString(style)); - } - } - } - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/package-info.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/package-info.java deleted file mode 100644 index 6367b42d5..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Eclipse 4 addons to integrate with Argeo CMS. */ -package org.argeo.cms.e4.addons; \ No newline at end of file diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/files/NodeFsBrowserView.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/files/NodeFsBrowserView.java deleted file mode 100644 index 579d35d6a..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/files/NodeFsBrowserView.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.argeo.cms.e4.files; - -import java.net.URI; -import java.nio.file.FileSystem; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.spi.FileSystemProvider; - -import javax.annotation.PostConstruct; -import javax.inject.Inject; - -import org.argeo.cms.jcr.CmsJcrUtils; -import org.argeo.cms.swt.CmsException; -import org.argeo.eclipse.ui.fs.AdvancedFsBrowser; -import org.argeo.eclipse.ui.fs.SimpleFsBrowser; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; - -/** Browse the node file system. */ -public class NodeFsBrowserView { - // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + - // ".nodeFsBrowserView"; - - @Inject - FileSystemProvider nodeFileSystemProvider; - - @PostConstruct - public void createPartControl(Composite parent) { - try { - //URI uri = new URI("node://root:demo@localhost:7070/"); - URI uri = new URI("node:///"); - FileSystem fileSystem = nodeFileSystemProvider.getFileSystem(uri); - if (fileSystem == null) - fileSystem = nodeFileSystemProvider.newFileSystem(uri, null); - Path nodePath = fileSystem.getPath("/"); - - Path localPath = Paths.get(System.getProperty("user.home")); - - SimpleFsBrowser browser = new SimpleFsBrowser(parent, SWT.NO_FOCUS); - browser.setInput(nodePath, localPath); -// AdvancedFsBrowser browser = new AdvancedFsBrowser(); -// browser.createUi(parent, localPath); - } catch (Exception e) { - throw new CmsException("Cannot open file system browser", e); - } - } - - public void setFocus() { - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/files/package-info.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/files/package-info.java deleted file mode 100644 index b481dd48a..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/files/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Files browser perspective. */ -package org.argeo.cms.e4.files; \ No newline at end of file diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangeLanguage.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangeLanguage.java deleted file mode 100644 index 416df7df1..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangeLanguage.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.argeo.cms.e4.handlers; - -import java.util.Locale; - -import org.eclipse.e4.core.di.annotations.Execute; -import org.eclipse.e4.core.services.nls.ILocaleChangeService; - -public class ChangeLanguage { - @Execute - public void execute(ILocaleChangeService localeChangeService) { - localeChangeService.changeApplicationLocale(Locale.FRENCH); - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangePassword.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangePassword.java deleted file mode 100644 index 7ef8c59da..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangePassword.java +++ /dev/null @@ -1,137 +0,0 @@ -package org.argeo.cms.e4.handlers; - -import static org.argeo.cms.CmsMsg.changePassword; -import static org.argeo.cms.CmsMsg.currentPassword; -import static org.argeo.cms.CmsMsg.newPassword; -import static org.argeo.cms.CmsMsg.passwordChanged; -import static org.argeo.cms.CmsMsg.repeatNewPassword; - -import java.util.Arrays; - -import javax.inject.Inject; -import javax.naming.InvalidNameException; -import javax.naming.ldap.LdapName; - -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.security.CryptoKeyring; -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.swt.dialogs.CmsMessageDialog; -import org.argeo.eclipse.ui.dialogs.ErrorFeedback; -import org.argeo.util.transaction.WorkTransaction; -import org.eclipse.e4.core.di.annotations.Execute; -import org.eclipse.e4.core.di.annotations.Optional; -import org.eclipse.jface.dialogs.Dialog; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Text; -import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdmin; - -/** Change the password of the logged-in user. */ -public class ChangePassword { - @Inject - private UserAdmin userAdmin; - @Inject - private WorkTransaction userTransaction; - @Inject - @Optional - private CryptoKeyring keyring = null; - - @Execute - public void execute() { - ChangePasswordDialog dialog = new ChangePasswordDialog(Display.getCurrent().getActiveShell(), userAdmin); - if (dialog.open() == Dialog.OK) { - new CmsMessageDialog(Display.getCurrent().getActiveShell(), passwordChanged.lead(), - CmsMessageDialog.INFORMATION).open(); - } - } - - protected void changePassword(char[] oldPassword, char[] newPassword) { - String name = CurrentUser.getUsername(); - LdapName dn; - try { - dn = new LdapName(name); - } catch (InvalidNameException e) { - throw new CmsException("Invalid user dn " + name, e); - } - User user = (User) userAdmin.getRole(dn.toString()); - if (!user.hasCredential(null, oldPassword)) - throw new CmsException("Invalid password"); - if (Arrays.equals(newPassword, new char[0])) - throw new CmsException("New password empty"); - try { - userTransaction.begin(); - user.getCredentials().put(null, newPassword); - if (keyring != null) { - keyring.changePassword(oldPassword, newPassword); - // TODO change secret keys in the CMS session - } - userTransaction.commit(); - } catch (Exception e) { - try { - userTransaction.rollback(); - } catch (Exception e1) { - e1.printStackTrace(); - } - if (e instanceof RuntimeException) - throw (RuntimeException) e; - else - throw new CmsException("Cannot change password", e); - } - } - - class ChangePasswordDialog extends CmsMessageDialog { - private Text oldPassword, newPassword1, newPassword2; - - public ChangePasswordDialog(Shell parentShell, UserAdmin securityService) { - super(parentShell, changePassword.lead(), CONFIRM); - } - -// protected Point getInitialSize() { -// return new Point(400, 450); -// } - - protected Control createDialogArea(Composite parent) { - Composite dialogarea = (Composite) super.createDialogArea(parent); - dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - Composite composite = new Composite(dialogarea, SWT.NONE); - composite.setLayout(new GridLayout(2, false)); - composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); - oldPassword = createLP(composite, currentPassword.lead()); - newPassword1 = createLP(composite, newPassword.lead()); - newPassword2 = createLP(composite, repeatNewPassword.lead()); - -// parent.pack(); - oldPassword.setFocus(); - return composite; - } - - @Override - protected void okPressed() { - try { - if (!newPassword1.getText().equals(newPassword2.getText())) - throw new CmsException("New passwords are different"); - changePassword(oldPassword.getTextChars(), newPassword1.getTextChars()); - closeShell(OK); - } catch (Exception e) { - ErrorFeedback.show("Cannot change password", e); - } - } - - /** Creates label and password. */ - protected Text createLP(Composite parent, String label) { - new Label(parent, SWT.NONE).setText(label); - Text text = new Text(parent, SWT.SINGLE | SWT.LEAD | SWT.PASSWORD | SWT.BORDER); - text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); - return text; - } - - } - -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/CloseAllParts.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/CloseAllParts.java deleted file mode 100644 index d11c0412c..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/CloseAllParts.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.argeo.cms.e4.handlers; - -import org.eclipse.e4.core.di.annotations.CanExecute; -import org.eclipse.e4.core.di.annotations.Execute; -import org.eclipse.e4.ui.model.application.ui.basic.MPart; -import org.eclipse.e4.ui.workbench.modeling.EPartService; - -public class CloseAllParts { - - @Execute - void execute(EPartService partService) { - for (MPart part : partService.getParts()) { - if (part.isCloseable()) { - if (part.isDirty()) { - if (partService.savePart(part, true)) { - partService.hidePart(part, true); - } - } else { - partService.hidePart(part, true); - } - } - } - } - - @CanExecute - boolean canExecute(EPartService partService) { - boolean atLeastOnePart = false; - for (MPart part : partService.getParts()) { - if (part.isVisible() && part.isCloseable()) { - atLeastOnePart = true; - break; - } - } - return atLeastOnePart; - } - -} \ No newline at end of file diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/CloseWorkbench.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/CloseWorkbench.java deleted file mode 100644 index c2ae4bff7..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/CloseWorkbench.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.argeo.cms.e4.handlers; - -import java.security.AccessController; - -import javax.security.auth.Subject; - -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.swt.CmsException; -import org.eclipse.e4.core.di.annotations.Execute; -import org.eclipse.e4.ui.workbench.IWorkbench; - -public class CloseWorkbench { - @Execute - public void execute(IWorkbench workbench) { - logout(); - workbench.close(); - } - - protected void logout() { - Subject subject = Subject.getSubject(AccessController.getContext()); - try { - CurrentUser.logoutCmsSession(subject); - } catch (Exception e) { - throw new CmsException("Cannot log out", e); - } - } - -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/DoNothing.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/DoNothing.java deleted file mode 100644 index 358494c5b..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/DoNothing.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.argeo.cms.e4.handlers; - -import org.eclipse.e4.core.di.annotations.Execute; - -public class DoNothing { - @Execute - public void execute() { - - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/LanguageMenuContribution.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/LanguageMenuContribution.java deleted file mode 100644 index ac825bb0d..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/LanguageMenuContribution.java +++ /dev/null @@ -1,29 +0,0 @@ - -package org.argeo.cms.e4.handlers; - -import java.util.Date; -import java.util.List; - -import org.eclipse.e4.ui.di.AboutToHide; -import org.eclipse.e4.ui.di.AboutToShow; -import org.eclipse.e4.ui.model.application.ui.menu.MDirectMenuItem; -import org.eclipse.e4.ui.model.application.ui.menu.MMenuElement; -import org.eclipse.e4.ui.workbench.modeling.EModelService; - -public class LanguageMenuContribution { - @AboutToShow - public void aboutToShow(List items, EModelService modelService) { - MDirectMenuItem dynamicItem = modelService.createModelElement(MDirectMenuItem.class); - dynamicItem.setLabel("Dynamic Menu Item (" + new Date() + ")"); - //dynamicItem.setContributorURI("platform:/plugin/org.argeo.cms.e4"); - //dynamicItem.setContributionURI("bundleclass://org.argeo.cms.e4/" + ChangeLanguage.class.getName()); - dynamicItem.setEnabled(true); - dynamicItem.setContributionURI("bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.handlers.ChangeLanguage"); - items.add(dynamicItem); - } - - @AboutToHide - public void aboutToHide() { - - } -} \ No newline at end of file diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/OpenPerspective.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/OpenPerspective.java deleted file mode 100644 index ac544b107..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/OpenPerspective.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.argeo.cms.e4.handlers; - -import java.util.List; - -import javax.inject.Inject; -import javax.inject.Named; - -import org.eclipse.e4.core.di.annotations.Execute; -import org.eclipse.e4.ui.model.application.MApplication; -import org.eclipse.e4.ui.model.application.ui.advanced.MPerspective; -import org.eclipse.e4.ui.workbench.modeling.EModelService; -import org.eclipse.e4.ui.workbench.modeling.EPartService; - -public class OpenPerspective { - @Inject - MApplication application; - @Inject - EPartService partService; - @Inject - EModelService modelService; - - @Execute - public void execute(@Named("perspectiveId") String perspectiveId) { - List perspectives = modelService.findElements(application, perspectiveId, MPerspective.class, - null); - if (perspectives.size() == 0) - return; - MPerspective perspective = perspectives.get(0); - partService.switchPerspective(perspective); - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/SaveAllParts.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/SaveAllParts.java deleted file mode 100644 index 3b60abd7e..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/SaveAllParts.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.argeo.cms.e4.handlers; - -import org.eclipse.e4.core.di.annotations.CanExecute; -import org.eclipse.e4.core.di.annotations.Execute; -import org.eclipse.e4.ui.workbench.modeling.EPartService; - -public class SaveAllParts { - - @Execute - void execute(EPartService partService) { - partService.saveAll(false); - } - - @CanExecute - boolean canExecute(EPartService partService) { - return partService.getDirtyParts().size() > 0; - } - -} \ No newline at end of file diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/SavePart.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/SavePart.java deleted file mode 100644 index 73486f363..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/SavePart.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.argeo.cms.e4.handlers; - -import org.eclipse.e4.core.di.annotations.CanExecute; -import org.eclipse.e4.core.di.annotations.Execute; -import org.eclipse.e4.ui.model.application.ui.basic.MPart; -import org.eclipse.e4.ui.workbench.modeling.EPartService; - -public class SavePart { - @Execute - void execute(EPartService partService, MPart part) { - partService.savePart(part, false); - } - - @CanExecute - boolean canExecute(MPart part) { - return part.isDirty(); - } -} \ No newline at end of file diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/package-info.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/package-info.java deleted file mode 100644 index a44ca9056..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Generic Eclipse 4 handlers. */ -package org.argeo.cms.e4.handlers; \ No newline at end of file diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/EclipseJcrMonitor.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/EclipseJcrMonitor.java deleted file mode 100644 index e10738ee0..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/EclipseJcrMonitor.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.argeo.cms.e4.jcr; - -import org.argeo.jcr.JcrMonitor; -import org.eclipse.core.runtime.IProgressMonitor; - -/** - * Wraps an Eclipse {@link IProgressMonitor} so that it can be passed to - * framework agnostic Argeo routines. - */ -public class EclipseJcrMonitor implements JcrMonitor { - private final IProgressMonitor progressMonitor; - - public EclipseJcrMonitor(IProgressMonitor progressMonitor) { - this.progressMonitor = progressMonitor; - } - - public void beginTask(String name, int totalWork) { - progressMonitor.beginTask(name, totalWork); - } - - public void done() { - progressMonitor.done(); - } - - public boolean isCanceled() { - return progressMonitor.isCanceled(); - } - - public void setCanceled(boolean value) { - progressMonitor.setCanceled(value); - } - - public void setTaskName(String name) { - progressMonitor.setTaskName(name); - } - - public void subTask(String name) { - progressMonitor.subTask(name); - } - - public void worked(int work) { - progressMonitor.worked(work); - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/GenericPropertyPage.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/GenericPropertyPage.java deleted file mode 100644 index e17f17bb7..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/GenericPropertyPage.java +++ /dev/null @@ -1,141 +0,0 @@ -package org.argeo.cms.e4.jcr; - -import java.util.ArrayList; -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.PropertyIterator; -import javax.jcr.RepositoryException; - -import org.argeo.cms.ui.jcr.PropertyLabelProvider; -import org.argeo.eclipse.ui.EclipseUiException; -import org.eclipse.jface.layout.TreeColumnLayout; -import org.eclipse.jface.viewers.ColumnWeightData; -import org.eclipse.jface.viewers.IBaseLabelProvider; -import org.eclipse.jface.viewers.ITreeContentProvider; -import org.eclipse.jface.viewers.TreeViewer; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.ScrolledComposite; -import org.eclipse.swt.layout.FillLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Tree; -import org.eclipse.swt.widgets.TreeColumn; - -/** - * Generic editor property page. Lists all properties of current node as a - * complex tree. TODO: enable editing - */ -public class GenericPropertyPage { - - // Main business Objects - private Node currentNode; - - public GenericPropertyPage(Node currentNode) { - this.currentNode = currentNode; - } - - protected void createFormContent(Composite parent) { - Composite innerBox = new Composite(parent, SWT.NONE); - // Composite innerBox = new Composite(body, SWT.NO_FOCUS); - FillLayout layout = new FillLayout(); - layout.marginHeight = 5; - layout.marginWidth = 5; - innerBox.setLayout(layout); - createComplexTree(innerBox); - // TODO TreeColumnLayout triggers a scroll issue with the form: - // The inside body is always to big and a scroll bar is shown - // Composite tableCmp = new Composite(body, SWT.NO_FOCUS); - // createComplexTree(tableCmp); - } - - private TreeViewer createComplexTree(Composite parent) { - int style = SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION; - Tree tree = new Tree(parent, style); - TreeColumnLayout tableColumnLayout = new TreeColumnLayout(); - - createColumn(tree, tableColumnLayout, "Property", SWT.LEFT, 200, 30); - createColumn(tree, tableColumnLayout, "Value(s)", SWT.LEFT, 300, 60); - createColumn(tree, tableColumnLayout, "Type", SWT.LEFT, 75, 10); - createColumn(tree, tableColumnLayout, "Attributes", SWT.LEFT, 75, 0); - // Do not apply the treeColumnLayout it does not work yet - // parent.setLayout(tableColumnLayout); - - tree.setLinesVisible(true); - tree.setHeaderVisible(true); - - TreeViewer treeViewer = new TreeViewer(tree); - treeViewer.setContentProvider(new TreeContentProvider()); - treeViewer.setLabelProvider((IBaseLabelProvider) new PropertyLabelProvider()); - treeViewer.setInput(currentNode); - treeViewer.expandAll(); - return treeViewer; - } - - private static TreeColumn createColumn(Tree parent, TreeColumnLayout tableColumnLayout, String name, int style, - int width, int weight) { - TreeColumn column = new TreeColumn(parent, style); - column.setText(name); - column.setWidth(width); - column.setMoveable(true); - column.setResizable(true); - tableColumnLayout.setColumnData(column, new ColumnWeightData(weight, width, true)); - return column; - } - - private class TreeContentProvider implements ITreeContentProvider { - private static final long serialVersionUID = -6162736530019406214L; - - public Object[] getElements(Object parent) { - Object[] props = null; - try { - - if (parent instanceof Node) { - Node node = (Node) parent; - PropertyIterator pi; - pi = node.getProperties(); - List propList = new ArrayList(); - while (pi.hasNext()) { - propList.add(pi.nextProperty()); - } - props = propList.toArray(); - } - } catch (RepositoryException e) { - throw new EclipseUiException("Unexpected exception while listing node properties", e); - } - return props; - } - - public Object getParent(Object child) { - return null; - } - - public Object[] getChildren(Object parent) { - if (parent instanceof Property) { - Property prop = (Property) parent; - try { - if (prop.isMultiple()) - return prop.getValues(); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot get multi-prop values on " + prop, e); - } - } - return null; - } - - public boolean hasChildren(Object parent) { - try { - return (parent instanceof Property && ((Property) parent).isMultiple()); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot check if property is multiple for " + parent, e); - } - } - - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - } - - public void dispose() { - } - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrBrowserView.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrBrowserView.java deleted file mode 100644 index 0b77c0732..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrBrowserView.java +++ /dev/null @@ -1,349 +0,0 @@ -package org.argeo.cms.e4.jcr; - -import java.util.List; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import javax.inject.Inject; -import javax.jcr.Property; -import javax.jcr.PropertyType; -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.jcr.RepositoryFactory; -import javax.jcr.Session; -import javax.jcr.Value; -import javax.jcr.observation.Event; -import javax.jcr.observation.EventListener; -import javax.jcr.observation.ObservationManager; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.cms.security.CryptoKeyring; -import org.argeo.cms.security.Keyring; -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ui.jcr.JcrBrowserUtils; -import org.argeo.cms.ui.jcr.NodeContentProvider; -import org.argeo.cms.ui.jcr.NodeLabelProvider; -import org.argeo.cms.ui.jcr.OsgiRepositoryRegister; -import org.argeo.cms.ui.jcr.PropertiesContentProvider; -import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; -import org.argeo.cms.ux.widgets.TreeParent; -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.eclipse.ui.jcr.AsyncUiEventListener; -import org.argeo.eclipse.ui.jcr.util.NodeViewerComparer; -import org.argeo.jcr.JcrUtils; -import org.eclipse.e4.core.contexts.IEclipseContext; -import org.eclipse.e4.core.di.annotations.Optional; -import org.eclipse.e4.ui.services.EMenuService; -import org.eclipse.e4.ui.workbench.modeling.EPartService; -import org.eclipse.e4.ui.workbench.modeling.ESelectionService; -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.jface.viewers.IBaseLabelProvider; -import org.eclipse.jface.viewers.ISelectionChangedListener; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.ITreeContentProvider; -import org.eclipse.jface.viewers.SelectionChangedEvent; -import org.eclipse.jface.viewers.StructuredSelection; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.jface.viewers.TableViewerColumn; -import org.eclipse.jface.viewers.TreeViewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.SashForm; -import org.eclipse.swt.layout.FillLayout; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; - -/** - * Basic View to display a sash form to browse a JCR compliant multiple - * repository environment - */ -public class JcrBrowserView { - final static String ID = "org.argeo.cms.e4.jcrbrowser"; - final static String NODE_VIEWER_POPUP_MENU_ID = "org.argeo.cms.e4.popupmenu.nodeViewer"; - - private boolean sortChildNodes = true; - - /* DEPENDENCY INJECTION */ - @Inject - @Optional - private Keyring keyring; - @Inject - private RepositoryFactory repositoryFactory; - @Inject - private Repository nodeRepository; - - // Current user session on the home repository default workspace - private Session userSession; - - private OsgiRepositoryRegister repositoryRegister = new OsgiRepositoryRegister(); - - // This page widgets - private TreeViewer nodesViewer; - private NodeContentProvider nodeContentProvider; - private TableViewer propertiesViewer; - private EventListener resultsObserver; - - @PostConstruct - public void createPartControl(Composite parent, IEclipseContext context, EPartService partService, - ESelectionService selectionService, EMenuService menuService) { - repositoryRegister.init(); - - parent.setLayout(new FillLayout()); - SashForm sashForm = new SashForm(parent, SWT.VERTICAL); - // sashForm.setSashWidth(4); - // sashForm.setLayout(new FillLayout()); - - // Create the tree on top of the view - Composite top = new Composite(sashForm, SWT.NONE); - // GridLayout gl = new GridLayout(1, false); - top.setLayout(CmsSwtUtils.noSpaceGridLayout()); - - try { - this.userSession = this.nodeRepository.login(CmsConstants.HOME_WORKSPACE); - } catch (RepositoryException e) { - throw new CmsException("Cannot open user session", e); - } - - nodeContentProvider = new NodeContentProvider(userSession, keyring, repositoryRegister, repositoryFactory, - sortChildNodes); - - // nodes viewer - nodesViewer = createNodeViewer(top, nodeContentProvider); - - // context menu : it is completely defined in the plugin.xml file. - // MenuManager menuManager = new MenuManager(); - // Menu menu = menuManager.createContextMenu(nodesViewer.getTree()); - - // nodesViewer.getTree().setMenu(menu); - - nodesViewer.setInput(""); - - // Create the property viewer on the bottom - Composite bottom = new Composite(sashForm, SWT.NONE); - bottom.setLayout(CmsSwtUtils.noSpaceGridLayout()); - propertiesViewer = createPropertiesViewer(bottom); - - sashForm.setWeights(getWeights()); - nodesViewer.setComparer(new NodeViewerComparer()); - nodesViewer.addSelectionChangedListener(new ISelectionChangedListener() { - public void selectionChanged(SelectionChangedEvent event) { - IStructuredSelection selection = (IStructuredSelection) event.getSelection(); - selectionService.setSelection(selection.toList()); - } - }); - nodesViewer.addDoubleClickListener(new JcrE4DClickListener(nodesViewer, partService)); - menuService.registerContextMenu(nodesViewer.getControl(), NODE_VIEWER_POPUP_MENU_ID); - // getSite().registerContextMenu(menuManager, nodesViewer); - // getSite().setSelectionProvider(nodesViewer); - } - - @PreDestroy - public void dispose() { - JcrUtils.logoutQuietly(userSession); - repositoryRegister.destroy(); - } - - public void refresh(Object obj) { - // Enable full refresh from a command when no element of the tree is - // selected - if (obj == null) { - Object[] elements = nodeContentProvider.getElements(null); - for (Object el : elements) { - if (el instanceof TreeParent) - JcrBrowserUtils.forceRefreshIfNeeded((TreeParent) el); - getNodeViewer().refresh(el); - } - } else - getNodeViewer().refresh(obj); - } - - /** - * To be overridden to adapt size of form and result frames. - */ - protected int[] getWeights() { - return new int[] { 70, 30 }; - } - - protected TreeViewer createNodeViewer(Composite parent, final ITreeContentProvider nodeContentProvider) { - - final TreeViewer tmpNodeViewer = new TreeViewer(parent, SWT.MULTI); - - tmpNodeViewer.getTree().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - - tmpNodeViewer.setContentProvider(nodeContentProvider); - tmpNodeViewer.setLabelProvider((IBaseLabelProvider) new NodeLabelProvider()); - tmpNodeViewer.addSelectionChangedListener(new ISelectionChangedListener() { - public void selectionChanged(SelectionChangedEvent event) { - if (!event.getSelection().isEmpty()) { - IStructuredSelection sel = (IStructuredSelection) event.getSelection(); - Object firstItem = sel.getFirstElement(); - if (firstItem instanceof SingleJcrNodeElem) - propertiesViewer.setInput(((SingleJcrNodeElem) firstItem).getNode()); - } else { - propertiesViewer.setInput(""); - } - } - }); - - resultsObserver = new TreeObserver(tmpNodeViewer.getTree().getDisplay()); - if (keyring != null) - try { - ObservationManager observationManager = userSession.getWorkspace().getObservationManager(); - observationManager.addEventListener(resultsObserver, Event.PROPERTY_ADDED | Event.PROPERTY_CHANGED, "/", - true, null, null, false); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot register listeners", e); - } - - // tmpNodeViewer.addDoubleClickListener(new JcrDClickListener(tmpNodeViewer)); - return tmpNodeViewer; - } - - protected TableViewer createPropertiesViewer(Composite parent) { - propertiesViewer = new TableViewer(parent, SWT.NONE); - propertiesViewer.getTable().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - propertiesViewer.getTable().setHeaderVisible(true); - propertiesViewer.setContentProvider(new PropertiesContentProvider()); - TableViewerColumn col = new TableViewerColumn(propertiesViewer, SWT.NONE); - col.getColumn().setText("Name"); - col.getColumn().setWidth(200); - col.setLabelProvider(new ColumnLabelProvider() { - private static final long serialVersionUID = -6684361063107478595L; - - public String getText(Object element) { - try { - return ((Property) element).getName(); - } catch (RepositoryException e) { - throw new EclipseUiException("Unexpected exception in label provider", e); - } - } - }); - col = new TableViewerColumn(propertiesViewer, SWT.NONE); - col.getColumn().setText("Value"); - col.getColumn().setWidth(400); - col.setLabelProvider(new ColumnLabelProvider() { - private static final long serialVersionUID = -8201994187693336657L; - - public String getText(Object element) { - try { - Property property = (Property) element; - if (property.getType() == PropertyType.BINARY) - return ""; - else if (property.isMultiple()) { - StringBuffer buf = new StringBuffer("["); - Value[] values = property.getValues(); - for (int i = 0; i < values.length; i++) { - if (i != 0) - buf.append(", "); - buf.append(values[i].getString()); - } - buf.append(']'); - return buf.toString(); - } else - return property.getValue().getString(); - } catch (RepositoryException e) { - throw new EclipseUiException("Unexpected exception in label provider", e); - } - } - }); - col = new TableViewerColumn(propertiesViewer, SWT.NONE); - col.getColumn().setText("Type"); - col.getColumn().setWidth(200); - col.setLabelProvider(new ColumnLabelProvider() { - private static final long serialVersionUID = -6009599998150286070L; - - public String getText(Object element) { - return JcrBrowserUtils.getPropertyTypeAsString((Property) element); - } - }); - propertiesViewer.setInput(""); - return propertiesViewer; - } - - protected TreeViewer getNodeViewer() { - return nodesViewer; - } - - /** - * Resets the tree content provider - * - * @param sortChildNodes if true the content provider will use a comparer to - * sort nodes that might slow down the display - */ - public void setSortChildNodes(boolean sortChildNodes) { - this.sortChildNodes = sortChildNodes; - ((NodeContentProvider) nodesViewer.getContentProvider()).setSortChildren(sortChildNodes); - nodesViewer.setInput(""); - } - - /** Notifies the current view that a node has been added */ - public void nodeAdded(TreeParent parentNode) { - // insure that Ui objects have been correctly created: - JcrBrowserUtils.forceRefreshIfNeeded(parentNode); - getNodeViewer().refresh(parentNode); - getNodeViewer().expandToLevel(parentNode, 1); - } - - /** Notifies the current view that a node has been removed */ - public void nodeRemoved(TreeParent parentNode) { - IStructuredSelection newSel = new StructuredSelection(parentNode); - getNodeViewer().setSelection(newSel, true); - // Force refresh - IStructuredSelection tmpSel = (IStructuredSelection) getNodeViewer().getSelection(); - getNodeViewer().refresh(tmpSel.getFirstElement()); - } - - class TreeObserver extends AsyncUiEventListener { - - public TreeObserver(Display display) { - super(display); - } - - @Override - protected Boolean willProcessInUiThread(List events) throws RepositoryException { - for (Event event : events) { - if (getLog().isTraceEnabled()) - getLog().debug("Received event " + event); - String path = event.getPath(); - int index = path.lastIndexOf('/'); - String propertyName = path.substring(index + 1); - if (getLog().isTraceEnabled()) - getLog().debug("Concerned property " + propertyName); - } - return false; - } - - protected void onEventInUiThread(List events) throws RepositoryException { - if (getLog().isTraceEnabled()) - getLog().trace("Refresh result list"); - nodesViewer.refresh(); - } - - } - - public boolean getSortChildNodes() { - return sortChildNodes; - } - - public void setFocus() { - getNodeViewer().getTree().setFocus(); - } - - /* DEPENDENCY INJECTION */ - // public void setRepositoryRegister(RepositoryRegister repositoryRegister) { - // this.repositoryRegister = repositoryRegister; - // } - - public void setKeyring(CryptoKeyring keyring) { - this.keyring = keyring; - } - - public void setRepositoryFactory(RepositoryFactory repositoryFactory) { - this.repositoryFactory = repositoryFactory; - } - - public void setNodeRepository(Repository nodeRepository) { - this.nodeRepository = nodeRepository; - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrE4DClickListener.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrE4DClickListener.java deleted file mode 100644 index f4ee2e8da..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrE4DClickListener.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.argeo.cms.e4.jcr; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.ui.jcr.JcrDClickListener; -import org.eclipse.e4.ui.model.application.ui.basic.MPart; -import org.eclipse.e4.ui.workbench.modeling.EPartService; -import org.eclipse.e4.ui.workbench.modeling.EPartService.PartState; -import org.eclipse.jface.viewers.TreeViewer; - -public class JcrE4DClickListener extends JcrDClickListener { - EPartService partService; - - public JcrE4DClickListener(TreeViewer nodeViewer, EPartService partService) { - super(nodeViewer); - this.partService = partService; - } - - @Override - protected void openNode(Node node) { - MPart part = partService.createPart(JcrNodeEditor.DESCRIPTOR_ID); - try { - part.setLabel(node.getName()); - part.getPersistedState().put("nodeWorkspace", node.getSession().getWorkspace().getName()); - part.getPersistedState().put("nodePath", node.getPath()); - } catch (RepositoryException e) { - throw new CmsException("Cannot open " + node, e); - } - - // the provided part is be shown - partService.showPart(part, PartState.ACTIVATE); - } - -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrNodeEditor.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrNodeEditor.java deleted file mode 100644 index ae2b325f5..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrNodeEditor.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.argeo.cms.e4.jcr; - -import java.util.List; - -import javax.annotation.PostConstruct; -import javax.jcr.Node; - -import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; -import org.eclipse.e4.ui.model.application.ui.basic.MPart; -import org.eclipse.e4.ui.workbench.modeling.ESelectionService; -import org.eclipse.swt.layout.FillLayout; -import org.eclipse.swt.widgets.Composite; - -public class JcrNodeEditor { - final static String DESCRIPTOR_ID = "org.argeo.cms.e4.partdescriptor.nodeEditor"; - - @PostConstruct - public void createUi(Composite parent, MPart part, ESelectionService selectionService) { - parent.setLayout(new FillLayout()); - List selection = (List) selectionService.getSelection(); - Node node = ((SingleJcrNodeElem) selection.get(0)).getNode(); - GenericPropertyPage propertyPage = new GenericPropertyPage(node); - propertyPage.createFormContent(parent); - } - -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/SimplePart.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/SimplePart.java deleted file mode 100644 index 17d8d2a23..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/SimplePart.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.argeo.cms.e4.jcr; - -import javax.annotation.PostConstruct; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; - -public class SimplePart { - - @PostConstruct - void init(Composite parent) { - parent.setLayout(new GridLayout()); - Label label = new Label(parent, SWT.NONE); - label.setText("Hello e4 World"); - } - -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/AddFolderNode.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/AddFolderNode.java deleted file mode 100644 index 09fa760cd..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/AddFolderNode.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.argeo.cms.e4.jcr.handlers; - -import java.util.List; - -import javax.inject.Named; -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.nodetype.NodeType; - -import org.argeo.cms.e4.jcr.JcrBrowserView; -import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; -import org.argeo.cms.ui.jcr.model.WorkspaceElem; -import org.argeo.cms.ux.widgets.TreeParent; -import org.argeo.eclipse.ui.dialogs.ErrorFeedback; -import org.argeo.eclipse.ui.dialogs.SingleValue; -import org.eclipse.e4.core.di.annotations.Execute; -import org.eclipse.e4.ui.model.application.ui.basic.MPart; -import org.eclipse.e4.ui.services.IServiceConstants; -import org.eclipse.e4.ui.workbench.modeling.ESelectionService; - -/** - * Adds a node of type nt:folder, only on {@link SingleJcrNodeElem} and - * {@link WorkspaceElem} TreeObject types. - * - * This handler assumes that a selection provider is available and picks only - * first selected item. It is UI's job to enable the command only when the - * selection contains one and only one element. Thus no parameter is passed - * through the command. - */ -public class AddFolderNode { - @Execute - public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) { - List selection = (List) selectionService.getSelection(); - JcrBrowserView view = (JcrBrowserView) part.getObject(); - - if (selection != null && selection.size() == 1) { - TreeParent treeParentNode = null; - Node jcrParentNode = null; - Object obj = selection.get(0); - - if (obj instanceof SingleJcrNodeElem) { - treeParentNode = (TreeParent) obj; - jcrParentNode = ((SingleJcrNodeElem) treeParentNode).getNode(); - } else if (obj instanceof WorkspaceElem) { - treeParentNode = (TreeParent) obj; - jcrParentNode = ((WorkspaceElem) treeParentNode).getRootNode(); - } else - return; - - String folderName = SingleValue.ask("Folder name", "Enter folder name"); - if (folderName != null) { - try { - jcrParentNode.addNode(folderName, NodeType.NT_FOLDER); - jcrParentNode.getSession().save(); - view.nodeAdded(treeParentNode); - } catch (RepositoryException e) { - ErrorFeedback.show("Cannot create folder " + folderName + " under " + treeParentNode, e); - } - } - } else { - // ErrorFeedback.show(WorkbenchUiPlugin - // .getMessage("errorUnvalidNtFolderNodeType")); - ErrorFeedback.show("Invalid NT folder node type"); - } - } - -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/AddRemoteRepository.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/AddRemoteRepository.java deleted file mode 100644 index dc47f6edf..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/AddRemoteRepository.java +++ /dev/null @@ -1,210 +0,0 @@ -package org.argeo.cms.e4.jcr.handlers; - -import java.net.URI; -import java.util.Hashtable; - -import javax.inject.Inject; -import javax.inject.Named; -import javax.jcr.Node; -import javax.jcr.Repository; -import javax.jcr.RepositoryFactory; -import javax.jcr.Session; -import javax.jcr.SimpleCredentials; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.cms.ArgeoNames; -import org.argeo.cms.ArgeoTypes; -import org.argeo.cms.e4.jcr.JcrBrowserView; -import org.argeo.cms.jcr.CmsJcrUtils; -import org.argeo.cms.security.Keyring; -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.eclipse.ui.dialogs.ErrorFeedback; -import org.argeo.jcr.JcrUtils; -import org.eclipse.e4.core.di.annotations.Execute; -import org.eclipse.e4.core.di.annotations.Optional; -import org.eclipse.e4.ui.model.application.ui.basic.MPart; -import org.eclipse.e4.ui.services.IServiceConstants; -import org.eclipse.jface.dialogs.Dialog; -import org.eclipse.jface.dialogs.IMessageProvider; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.dialogs.TitleAreaDialog; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Text; - -/** - * Connect to a remote repository and, if successful publish it as an OSGi - * service. - */ -public class AddRemoteRepository { - - @Inject - private RepositoryFactory repositoryFactory; - @Inject - private Repository nodeRepository; - @Inject - @Optional - private Keyring keyring; - - @Execute - public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part) { - JcrBrowserView view = (JcrBrowserView) part.getObject(); - RemoteRepositoryLoginDialog dlg = new RemoteRepositoryLoginDialog(Display.getDefault().getActiveShell()); - if (dlg.open() == Dialog.OK) { - view.refresh(null); - } - } - - // public void setRepositoryFactory(RepositoryFactory repositoryFactory) { - // this.repositoryFactory = repositoryFactory; - // } - // - // public void setKeyring(Keyring keyring) { - // this.keyring = keyring; - // } - // - // public void setNodeRepository(Repository nodeRepository) { - // this.nodeRepository = nodeRepository; - // } - - class RemoteRepositoryLoginDialog extends TitleAreaDialog { - private static final long serialVersionUID = 2234006887750103399L; - private Text name; - private Text uri; - private Text username; - private Text password; - private Button saveInKeyring; - - public RemoteRepositoryLoginDialog(Shell parentShell) { - super(parentShell); - } - - protected Point getInitialSize() { - return new Point(600, 400); - } - - protected Control createDialogArea(Composite parent) { - Composite dialogarea = (Composite) super.createDialogArea(parent); - dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - Composite composite = new Composite(dialogarea, SWT.NONE); - composite.setLayout(new GridLayout(2, false)); - composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); - setMessage("Login to remote repository", IMessageProvider.NONE); - name = createLT(composite, "Name", "remoteRepository"); - uri = createLT(composite, "URI", "http://localhost:7070/jcr/node"); - username = createLT(composite, "User", ""); - password = createLP(composite, "Password"); - - saveInKeyring = createLC(composite, "Remember password", false); - parent.pack(); - return composite; - } - - @Override - protected void createButtonsForButtonBar(Composite parent) { - super.createButtonsForButtonBar(parent); - Button test = createButton(parent, 2, "Test", false); - test.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = -1829962269440419560L; - - public void widgetSelected(SelectionEvent arg0) { - testConnection(); - } - }); - } - - void testConnection() { - Session session = null; - try { - URI checkedUri = new URI(uri.getText()); - String checkedUriStr = checkedUri.toString(); - - Hashtable params = new Hashtable(); - params.put(CmsConstants.LABELED_URI, checkedUriStr); - Repository repository = repositoryFactory.getRepository(params); - if (username.getText().trim().equals("")) {// anonymous - // FIXME make it more generic - session = repository.login(CmsConstants.SYS_WORKSPACE); - } else { - // FIXME use getTextChars() when upgrading to 3.7 - // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=297412 - char[] pwd = password.getText().toCharArray(); - SimpleCredentials sc = new SimpleCredentials(username.getText(), pwd); - session = repository.login(sc, "main"); - MessageDialog.openInformation(getParentShell(), "Success", - "Connection to '" + uri.getText() + "' successful"); - } - } catch (Exception e) { - ErrorFeedback.show("Connection test failed for " + uri.getText(), e); - } finally { - JcrUtils.logoutQuietly(session); - } - } - - @Override - protected void okPressed() { - Session nodeSession = null; - try { - nodeSession = nodeRepository.login(); - Node home = CmsJcrUtils.getUserHome(nodeSession); - - Node remote = home.hasNode(ArgeoNames.ARGEO_REMOTE) ? home.getNode(ArgeoNames.ARGEO_REMOTE) - : home.addNode(ArgeoNames.ARGEO_REMOTE); - if (remote.hasNode(name.getText())) - throw new EclipseUiException("There is already a remote repository named " + name.getText()); - Node remoteRepository = remote.addNode(name.getText(), ArgeoTypes.ARGEO_REMOTE_REPOSITORY); - remoteRepository.setProperty(ArgeoNames.ARGEO_URI, uri.getText()); - remoteRepository.setProperty(ArgeoNames.ARGEO_USER_ID, username.getText()); - nodeSession.save(); - if (saveInKeyring.getSelection()) { - String pwdPath = remoteRepository.getPath() + '/' + ArgeoNames.ARGEO_PASSWORD; - keyring.set(pwdPath, password.getText().toCharArray()); - } - nodeSession.save(); - MessageDialog.openInformation(getParentShell(), "Repository Added", - "Remote repository '" + username.getText() + "@" + uri.getText() + "' added"); - - super.okPressed(); - } catch (Exception e) { - ErrorFeedback.show("Cannot add remote repository", e); - } finally { - JcrUtils.logoutQuietly(nodeSession); - } - } - - /** Creates label and text. */ - protected Text createLT(Composite parent, String label, String initial) { - new Label(parent, SWT.NONE).setText(label); - Text text = new Text(parent, SWT.SINGLE | SWT.LEAD | SWT.BORDER); - text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - text.setText(initial); - return text; - } - - /** Creates label and check. */ - protected Button createLC(Composite parent, String label, Boolean initial) { - new Label(parent, SWT.NONE).setText(label); - Button check = new Button(parent, SWT.CHECK); - check.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - check.setSelection(initial); - return check; - } - - protected Text createLP(Composite parent, String label) { - new Label(parent, SWT.NONE).setText(label); - Text text = new Text(parent, SWT.SINGLE | SWT.LEAD | SWT.BORDER | SWT.PASSWORD); - text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - return text; - } - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/DeleteNodes.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/DeleteNodes.java deleted file mode 100644 index b8de06b46..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/DeleteNodes.java +++ /dev/null @@ -1,95 +0,0 @@ -package org.argeo.cms.e4.jcr.handlers; - -import java.util.List; - -import javax.inject.Named; -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.e4.jcr.JcrBrowserView; -import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; -import org.argeo.cms.ui.jcr.model.WorkspaceElem; -import org.argeo.cms.ux.widgets.TreeParent; -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.eclipse.ui.dialogs.ErrorFeedback; -import org.eclipse.e4.core.di.annotations.Execute; -import org.eclipse.e4.ui.model.application.ui.basic.MPart; -import org.eclipse.e4.ui.services.IServiceConstants; -import org.eclipse.e4.ui.workbench.modeling.ESelectionService; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.swt.widgets.Display; - -/** - * Delete the selected nodes: both in the JCR repository and in the UI view. - * Warning no check is done, except implementation dependent native checks, - * handle with care. - * - * This handler is still 'hard linked' to a GenericJcrBrowser view to enable - * correct tree refresh when a node is added. This must be corrected in future - * versions. - */ -public class DeleteNodes { - @Execute - public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) { - List selection = (List) selectionService.getSelection(); - if (selection == null) - return; - - JcrBrowserView view = (JcrBrowserView) part.getObject(); - - // confirmation - StringBuffer buf = new StringBuffer(""); - for (Object o : selection) { - SingleJcrNodeElem sjn = (SingleJcrNodeElem) o; - buf.append(sjn.getName()).append(' '); - } - Boolean doRemove = MessageDialog.openConfirm(Display.getCurrent().getActiveShell(), "Confirm deletion", - "Do you want to delete " + buf + "?"); - - // operation - if (doRemove) { - SingleJcrNodeElem ancestor = null; - WorkspaceElem rootAncestor = null; - try { - for (Object obj : selection) { - if (obj instanceof SingleJcrNodeElem) { - // Cache objects - SingleJcrNodeElem sjn = (SingleJcrNodeElem) obj; - TreeParent tp = (TreeParent) sjn.getParent(); - Node node = sjn.getNode(); - - // Jcr Remove - node.remove(); - node.getSession().save(); - // UI remove - tp.removeChild(sjn); - - // Check if the parent is the root node - if (tp instanceof WorkspaceElem) - rootAncestor = (WorkspaceElem) tp; - else - ancestor = getOlder(ancestor, (SingleJcrNodeElem) tp); - } - } - if (rootAncestor != null) - view.nodeRemoved(rootAncestor); - else if (ancestor != null) - view.nodeRemoved(ancestor); - } catch (Exception e) { - ErrorFeedback.show("Cannot delete selected node ", e); - } - } - } - - private SingleJcrNodeElem getOlder(SingleJcrNodeElem A, SingleJcrNodeElem B) { - try { - if (A == null) - return B == null ? null : B; - // Todo enhanced this method - else - return A.getNode().getDepth() <= B.getNode().getDepth() ? A : B; - } catch (RepositoryException re) { - throw new EclipseUiException("Cannot find ancestor", re); - } - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/Refresh.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/Refresh.java deleted file mode 100644 index 036e70ac1..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/Refresh.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.argeo.cms.e4.jcr.handlers; - -import java.util.List; - -import javax.inject.Named; - -import org.argeo.cms.e4.jcr.JcrBrowserView; -import org.argeo.cms.ui.jcr.JcrBrowserUtils; -import org.argeo.cms.ux.widgets.TreeParent; -import org.eclipse.e4.core.di.annotations.Execute; -import org.eclipse.e4.ui.model.application.ui.basic.MPart; -import org.eclipse.e4.ui.services.IServiceConstants; -import org.eclipse.e4.ui.workbench.modeling.EPartService; -import org.eclipse.e4.ui.workbench.modeling.ESelectionService; - -/** - * Force the selected objects of the active view to be refreshed doing the - * following: - *
    - *
  1. The model objects are recomputed
  2. - *
  3. the view is refreshed
  4. - *
- */ -public class Refresh { - - @Execute - public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, EPartService partService, - ESelectionService selectionService) { - - JcrBrowserView view = (JcrBrowserView) part.getObject(); - List selection = (List) selectionService.getSelection(); - - if (selection != null && !selection.isEmpty()) { - for (Object obj : selection) - if (obj instanceof TreeParent) { - TreeParent tp = (TreeParent) obj; - JcrBrowserUtils.forceRefreshIfNeeded(tp); - view.refresh(obj); - } - } else if (view instanceof JcrBrowserView) - view.refresh(null); // force full refresh - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/RenameNode.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/RenameNode.java deleted file mode 100644 index 97674abc2..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/RenameNode.java +++ /dev/null @@ -1,59 +0,0 @@ -package org.argeo.cms.e4.jcr.handlers; - -import java.util.List; - -import javax.inject.Named; -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.Session; - -import org.argeo.cms.e4.jcr.JcrBrowserView; -import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.eclipse.ui.dialogs.SingleValue; -import org.argeo.jcr.JcrUtils; -import org.eclipse.e4.core.di.annotations.Execute; -import org.eclipse.e4.ui.model.application.ui.basic.MPart; -import org.eclipse.e4.ui.services.IServiceConstants; -import org.eclipse.e4.ui.workbench.modeling.EPartService; -import org.eclipse.e4.ui.workbench.modeling.ESelectionService; - -/** - * Canonically call JCR Session#move(String, String) on the first element - * returned by HandlerUtil#getActiveWorkbenchWindow() - * (...getActivePage().getSelection()), if it is a {@link SingleJcrNodeElem}. - * The user must then fill a new name in and confirm - */ -public class RenameNode { - @Execute - public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, EPartService partService, - ESelectionService selectionService) { - List selection = (List) selectionService.getSelection(); - if (selection == null || selection.size() != 1) - return; - JcrBrowserView view = (JcrBrowserView) part.getObject(); - - Object element = selection.get(0); - if (element instanceof SingleJcrNodeElem) { - SingleJcrNodeElem sjn = (SingleJcrNodeElem) element; - Node node = sjn.getNode(); - Session session = null; - String newName = null; - String oldPath = null; - try { - newName = SingleValue.ask("New node name", "Please provide a new name for [" + node.getName() + "]"); - // TODO sanity check and user feedback - newName = JcrUtils.replaceInvalidChars(newName); - oldPath = node.getPath(); - session = node.getSession(); - session.move(oldPath, JcrUtils.parentPath(oldPath) + "/" + newName); - session.save(); - - // Manually refresh the browser view. Must be enhanced - view.refresh(sjn); - } catch (RepositoryException e) { - throw new EclipseUiException("Unable to rename " + node + " to " + newName, e); - } - } - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/package-info.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/package-info.java deleted file mode 100644 index 4e075e2b1..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** JCR browser handlers. */ -package org.argeo.cms.e4.jcr.handlers; \ No newline at end of file diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/package-info.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/package-info.java deleted file mode 100644 index 3e92fb04d..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** JCR browser perspective. */ -package org.argeo.cms.e4.jcr; \ No newline at end of file diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/AbstractOsgiComposite.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/AbstractOsgiComposite.java deleted file mode 100644 index 4fd1d68dc..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/AbstractOsgiComposite.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.argeo.cms.e4.maintenance; - -import java.util.Collection; - -import org.argeo.api.cms.CmsLog; -import org.argeo.cms.swt.CmsSwtUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Composite; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.framework.ServiceReference; - -abstract class AbstractOsgiComposite extends Composite { - private static final long serialVersionUID = -4097415973477517137L; - protected final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext(); - protected final CmsLog log = CmsLog.getLog(getClass()); - - public AbstractOsgiComposite(Composite parent, int style) { - super(parent, style); - parent.setLayout(CmsSwtUtils.noSpaceGridLayout()); - setLayout(CmsSwtUtils.noSpaceGridLayout()); - setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); - initUi(style); - } - - protected abstract void initUi(int style); - - protected T getService(Class clazz) { - return bc.getService(bc.getServiceReference(clazz)); - } - - protected Collection> getServiceReferences(Class clazz, String filter) { - try { - return bc.getServiceReferences(clazz, filter); - } catch (InvalidSyntaxException e) { - throw new IllegalArgumentException("Filter " + filter + " is invalid", e); - } - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/Browse.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/Browse.java deleted file mode 100644 index a536da066..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/Browse.java +++ /dev/null @@ -1,576 +0,0 @@ -package org.argeo.cms.e4.maintenance; - -import static org.eclipse.swt.SWT.RIGHT; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.LinkedHashMap; - -import javax.jcr.ItemNotFoundException; -import javax.jcr.Node; -import javax.jcr.NodeIterator; -import javax.jcr.Property; -import javax.jcr.PropertyIterator; -import javax.jcr.PropertyType; -import javax.jcr.RepositoryException; -import javax.jcr.Value; - -import org.argeo.api.cms.ux.Cms2DSize; -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.cms.ui.util.CmsLink; -import org.argeo.cms.ui.widgets.EditableImage; -import org.argeo.cms.ui.widgets.Img; -import org.argeo.jcr.JcrUtils; -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.jface.viewers.ILazyContentProvider; -import org.eclipse.jface.viewers.ISelectionChangedListener; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.SelectionChangedEvent; -import org.eclipse.jface.viewers.StructuredSelection; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.jface.viewers.TableViewerColumn; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.ScrolledComposite; -import org.eclipse.swt.events.ControlAdapter; -import org.eclipse.swt.events.ControlEvent; -import org.eclipse.swt.events.KeyEvent; -import org.eclipse.swt.events.KeyListener; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Table; -import org.eclipse.swt.widgets.TableColumn; -import org.eclipse.swt.widgets.Text; - -public class Browse implements CmsUiProvider { - - // Some local constants to experiment. should be cleaned - private final static String BROWSE_PREFIX = "browse#"; - private final static int THUMBNAIL_WIDTH = 400; - private final static int COLUMN_WIDTH = 160; - private DateFormat timeFormatter = new SimpleDateFormat("dd-MM-yyyy', 'HH:mm"); - - // keep a cache of the opened nodes - // Key is the path - private LinkedHashMap browserCols = new LinkedHashMap(); - private Composite nodeDisplayParent; - private Composite colViewer; - private ScrolledComposite scrolledCmp; - private Text parentPathTxt; - private Text filterTxt; - private Node currEdited; - - private String initialPath; - - @Override - public Control createUi(Composite parent, Node context) throws RepositoryException { - if (context == null) - // return null; - throw new CmsException("Context cannot be null"); - GridLayout layout = CmsSwtUtils.noSpaceGridLayout(); - layout.numColumns = 2; - parent.setLayout(layout); - - // Left - Composite leftCmp = new Composite(parent, SWT.NO_FOCUS); - leftCmp.setLayoutData(CmsSwtUtils.fillAll()); - createBrowserPart(leftCmp, context); - - // Right - nodeDisplayParent = new Composite(parent, SWT.NO_FOCUS | SWT.BORDER); - GridData gd = new GridData(SWT.RIGHT, SWT.FILL, false, true); - gd.widthHint = THUMBNAIL_WIDTH; - nodeDisplayParent.setLayoutData(gd); - createNodeView(nodeDisplayParent, context); - - // INIT - setEdited(context); - initialPath = context.getPath(); - - // Workaround we don't yet manage the delete to display parent of the - // initial context node - - return null; - } - - private void createBrowserPart(Composite parent, Node context) throws RepositoryException { - GridLayout layout = CmsSwtUtils.noSpaceGridLayout(); - parent.setLayout(layout); - Composite filterCmp = new Composite(parent, SWT.NO_FOCUS); - filterCmp.setLayoutData(CmsSwtUtils.fillWidth()); - - // top filter - addFilterPanel(filterCmp); - - // scrolled composite - scrolledCmp = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.BORDER | SWT.NO_FOCUS); - scrolledCmp.setLayoutData(CmsSwtUtils.fillAll()); - scrolledCmp.setExpandVertical(true); - scrolledCmp.setExpandHorizontal(true); - scrolledCmp.setShowFocusedControl(true); - - colViewer = new Composite(scrolledCmp, SWT.NO_FOCUS); - scrolledCmp.setContent(colViewer); - scrolledCmp.addControlListener(new ControlAdapter() { - private static final long serialVersionUID = 6589392045145698201L; - - @Override - public void controlResized(ControlEvent e) { - Rectangle r = scrolledCmp.getClientArea(); - scrolledCmp.setMinSize(colViewer.computeSize(SWT.DEFAULT, r.height)); - } - }); - initExplorer(colViewer, context); - } - - private Control initExplorer(Composite parent, Node context) throws RepositoryException { - parent.setLayout(CmsSwtUtils.noSpaceGridLayout()); - createBrowserColumn(parent, context); - return null; - } - - private Control createBrowserColumn(Composite parent, Node context) throws RepositoryException { - // TODO style is not correctly managed. - FilterEntitiesVirtualTable table = new FilterEntitiesVirtualTable(parent, SWT.BORDER | SWT.NO_FOCUS, context); - // CmsUiUtils.style(table, ArgeoOrgStyle.browserColumn.style()); - table.filterList("*"); - table.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, false, true)); - browserCols.put(context.getPath(), table); - return null; - } - - public void addFilterPanel(Composite parent) { - - parent.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(2, false))); - - // Text Area for the filter - parentPathTxt = new Text(parent, SWT.NO_FOCUS); - parentPathTxt.setEditable(false); - filterTxt = new Text(parent, SWT.SEARCH | SWT.ICON_CANCEL); - filterTxt.setMessage("Filter current list"); - filterTxt.setLayoutData(CmsSwtUtils.fillWidth()); - filterTxt.addModifyListener(new ModifyListener() { - private static final long serialVersionUID = 7709303319740056286L; - - public void modifyText(ModifyEvent event) { - modifyFilter(false); - } - }); - - filterTxt.addKeyListener(new KeyListener() { - private static final long serialVersionUID = -4523394262771183968L; - - @Override - public void keyReleased(KeyEvent e) { - } - - @Override - public void keyPressed(KeyEvent e) { - boolean shiftPressed = (e.stateMask & SWT.SHIFT) != 0; - // boolean altPressed = (e.stateMask & SWT.ALT) != 0; - FilterEntitiesVirtualTable currTable = null; - if (currEdited != null) { - FilterEntitiesVirtualTable table = browserCols.get(getPath(currEdited)); - if (table != null && !table.isDisposed()) - currTable = table; - } - - try { - if (e.keyCode == SWT.ARROW_DOWN) - currTable.setFocus(); - else if (e.keyCode == SWT.BS) { - if (filterTxt.getText().equals("") - && !(getPath(currEdited).equals("/") || getPath(currEdited).equals(initialPath))) { - setEdited(currEdited.getParent()); - e.doit = false; - filterTxt.setFocus(); - } - } else if (e.keyCode == SWT.TAB && !shiftPressed) { - if (currEdited.getNodes(filterTxt.getText() + "*").getSize() == 1) { - setEdited(currEdited.getNodes(filterTxt.getText() + "*").nextNode()); - } - filterTxt.setFocus(); - e.doit = false; - } - } catch (RepositoryException e1) { - throw new CmsException("Unexpected error in key management for " + currEdited + "with filter " - + filterTxt.getText(), e1); - } - - } - }); - } - - private void setEdited(Node node) { - try { - currEdited = node; - CmsSwtUtils.clear(nodeDisplayParent); - createNodeView(nodeDisplayParent, currEdited); - nodeDisplayParent.layout(); - refreshFilters(node); - refreshBrowser(node); - } catch (RepositoryException re) { - throw new CmsException("Unable to update browser for " + node, re); - } - } - - private void refreshFilters(Node node) throws RepositoryException { - String currNodePath = node.getPath(); - parentPathTxt.setText(currNodePath); - filterTxt.setText(""); - filterTxt.getParent().layout(); - } - - private void refreshBrowser(Node node) throws RepositoryException { - - // Retrieve - String currNodePath = node.getPath(); - String currParPath = ""; - if (!"/".equals(currNodePath)) - currParPath = JcrUtils.parentPath(currNodePath); - if ("".equals(currParPath)) - currParPath = "/"; - - Object[][] colMatrix = new Object[browserCols.size()][2]; - - int i = 0, j = -1, k = -1; - for (String path : browserCols.keySet()) { - colMatrix[i][0] = path; - colMatrix[i][1] = browserCols.get(path); - if (j >= 0 && k < 0 && !currNodePath.equals("/")) { - boolean leaveOpened = path.startsWith(currNodePath); - - // workaround for same name siblings - // fix me weird side effect when we go left or click on anb - // already selected, unfocused node - if (leaveOpened && (path.lastIndexOf("/") == 0 && currNodePath.lastIndexOf("/") == 0 - || JcrUtils.parentPath(path).equals(JcrUtils.parentPath(currNodePath)))) - leaveOpened = JcrUtils.lastPathElement(path).equals(JcrUtils.lastPathElement(currNodePath)); - - if (!leaveOpened) - k = i; - } - if (currParPath.equals(path)) - j = i; - i++; - } - - if (j >= 0 && k >= 0) - // remove useless cols - for (int l = i - 1; l >= k; l--) { - browserCols.remove(colMatrix[l][0]); - ((FilterEntitiesVirtualTable) colMatrix[l][1]).dispose(); - } - - // Remove disposed columns - // TODO investigate and fix the mechanism that leave them there after - // disposal - if (browserCols.containsKey(currNodePath)) { - FilterEntitiesVirtualTable currCol = browserCols.get(currNodePath); - if (currCol.isDisposed()) - browserCols.remove(currNodePath); - } - - if (!browserCols.containsKey(currNodePath)) - createBrowserColumn(colViewer, node); - - colViewer.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(browserCols.size(), false))); - // colViewer.pack(); - colViewer.layout(); - // also resize the scrolled composite - scrolledCmp.layout(); - scrolledCmp.getShowFocusedControl(); - // colViewer.getParent().layout(); - // if (JcrUtils.parentPath(currNodePath).equals(currBrowserKey)) { - // } else { - // } - } - - private void modifyFilter(boolean fromOutside) { - if (!fromOutside) - if (currEdited != null) { - String filter = filterTxt.getText() + "*"; - FilterEntitiesVirtualTable table = browserCols.get(getPath(currEdited)); - if (table != null && !table.isDisposed()) - table.filterList(filter); - } - - } - - private String getPath(Node node) { - try { - return node.getPath(); - } catch (RepositoryException e) { - throw new CmsException("Unable to get path for node " + node, e); - } - } - - private Cms2DSize imageWidth = new Cms2DSize(250, 0); - - /** - * Recreates the content of the box that displays information about the current - * selected node. - */ - private Control createNodeView(Composite parent, Node context) throws RepositoryException { - - parent.setLayout(new GridLayout(2, false)); - - if (isImg(context)) { - EditableImage image = new Img(parent, RIGHT, context, imageWidth); - image.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false, 2, 1)); - } - - // Name and primary type - Label contextL = new Label(parent, SWT.NONE); - CmsSwtUtils.markup(contextL); - contextL.setText("" + context.getName() + ""); - new Label(parent, SWT.NONE).setText(context.getPrimaryNodeType().getName()); - - // Children - for (NodeIterator nIt = context.getNodes(); nIt.hasNext();) { - Node child = nIt.nextNode(); - new CmsLink(child.getName(), BROWSE_PREFIX + child.getPath()).createUi(parent, context); - new Label(parent, SWT.NONE).setText(child.getPrimaryNodeType().getName()); - } - - // Properties - for (PropertyIterator pIt = context.getProperties(); pIt.hasNext();) { - Property property = pIt.nextProperty(); - Label label = new Label(parent, SWT.NONE); - label.setText(property.getName()); - label.setToolTipText(JcrUtils.getPropertyDefinitionAsString(property)); - new Label(parent, SWT.NONE).setText(getPropAsString(property)); - } - - return null; - } - - private boolean isImg(Node node) throws RepositoryException { - // TODO support images - return false; -// return node.hasNode(JCR_CONTENT) && node.isNodeType(CmsTypes.CMS_IMAGE); - } - - private String getPropAsString(Property property) throws RepositoryException { - String result = ""; - if (property.isMultiple()) { - result = getMultiAsString(property, ", "); - } else { - Value value = property.getValue(); - if (value.getType() == PropertyType.BINARY) - result = ""; - else if (value.getType() == PropertyType.DATE) - result = timeFormatter.format(value.getDate().getTime()); - else - result = value.getString(); - } - return result; - } - - private String getMultiAsString(Property property, String separator) throws RepositoryException { - if (separator == null) - separator = "; "; - Value[] values = property.getValues(); - StringBuilder builder = new StringBuilder(); - for (Value val : values) { - String currStr = val.getString(); - if (!"".equals(currStr.trim())) - builder.append(currStr).append(separator); - } - if (builder.lastIndexOf(separator) >= 0) - return builder.substring(0, builder.length() - separator.length()); - else - return builder.toString(); - } - - /** Almost canonical implementation of a table that display entities */ - private class FilterEntitiesVirtualTable extends Composite { - private static final long serialVersionUID = 8798147431706283824L; - - // Context - private Node context; - - // UI Objects - private TableViewer entityViewer; - - // enable management of multiple columns - Node getNode() { - return context; - } - - @Override - public boolean setFocus() { - if (entityViewer.getTable().isDisposed()) - return false; - if (entityViewer.getSelection().isEmpty()) { - Object first = entityViewer.getElementAt(0); - if (first != null) { - entityViewer.setSelection(new StructuredSelection(first), true); - } - } - return entityViewer.getTable().setFocus(); - } - - void filterList(String filter) { - try { - NodeIterator nit = context.getNodes(filter); - refreshFilteredList(nit); - } catch (RepositoryException e) { - throw new CmsException("Unable to filter " + getNode() + " children with filter " + filter, e); - } - - } - - public FilterEntitiesVirtualTable(Composite parent, int style, Node context) { - super(parent, SWT.NO_FOCUS); - this.context = context; - populate(); - } - - protected void populate() { - Composite parent = this; - GridLayout layout = CmsSwtUtils.noSpaceGridLayout(); - - this.setLayout(layout); - createTableViewer(parent); - } - - private void createTableViewer(final Composite parent) { - // the list - // We must limit the size of the table otherwise the full list is - // loaded - // before the layout happens - Composite listCmp = new Composite(parent, SWT.NO_FOCUS); - GridData gd = new GridData(SWT.LEFT, SWT.FILL, false, true); - gd.widthHint = COLUMN_WIDTH; - listCmp.setLayoutData(gd); - listCmp.setLayout(CmsSwtUtils.noSpaceGridLayout()); - - entityViewer = new TableViewer(listCmp, SWT.VIRTUAL | SWT.SINGLE); - Table table = entityViewer.getTable(); - - table.setLayoutData(CmsSwtUtils.fillAll()); - table.setLinesVisible(true); - table.setHeaderVisible(false); - CmsSwtUtils.markup(table); - - CmsSwtUtils.style(table, MaintenanceStyles.BROWSER_COLUMN); - - // first column - TableViewerColumn column = new TableViewerColumn(entityViewer, SWT.NONE); - TableColumn tcol = column.getColumn(); - tcol.setWidth(COLUMN_WIDTH); - tcol.setResizable(true); - column.setLabelProvider(new SimpleNameLP()); - - entityViewer.setContentProvider(new MyLazyCP(entityViewer)); - entityViewer.addSelectionChangedListener(new ISelectionChangedListener() { - - @Override - public void selectionChanged(SelectionChangedEvent event) { - IStructuredSelection selection = (IStructuredSelection) entityViewer.getSelection(); - if (selection.isEmpty()) - return; - else - setEdited((Node) selection.getFirstElement()); - - } - }); - - table.addKeyListener(new KeyListener() { - private static final long serialVersionUID = -330694313896036230L; - - @Override - public void keyReleased(KeyEvent e) { - } - - @Override - public void keyPressed(KeyEvent e) { - - IStructuredSelection selection = (IStructuredSelection) entityViewer.getSelection(); - Node selected = null; - if (!selection.isEmpty()) - selected = ((Node) selection.getFirstElement()); - try { - if (e.keyCode == SWT.ARROW_RIGHT) { - if (selected != null) { - setEdited(selected); - browserCols.get(selected.getPath()).setFocus(); - } - } else if (e.keyCode == SWT.ARROW_LEFT) { - try { - selected = getNode().getParent(); - String newPath = selected.getPath(); // getNode().getParent() - setEdited(selected); - if (browserCols.containsKey(newPath)) - browserCols.get(newPath).setFocus(); - } catch (ItemNotFoundException ie) { - // root silent - } - } - } catch (RepositoryException ie) { - throw new CmsException("Error while managing arrow " + "events in the browser for " + selected, - ie); - } - } - }); - } - - private class MyLazyCP implements ILazyContentProvider { - private static final long serialVersionUID = 1L; - private TableViewer viewer; - private Object[] elements; - - public MyLazyCP(TableViewer viewer) { - this.viewer = viewer; - } - - public void dispose() { - } - - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - // IMPORTANT: don't forget this: an exception will be thrown if - // a selected object is not part of the results anymore. - viewer.setSelection(null); - this.elements = (Object[]) newInput; - } - - public void updateElement(int index) { - viewer.replace(elements[index], index); - } - } - - protected void refreshFilteredList(NodeIterator children) { - Object[] rows = JcrUtils.nodeIteratorToList(children).toArray(); - entityViewer.setInput(rows); - entityViewer.setItemCount(rows.length); - entityViewer.refresh(); - } - - public class SimpleNameLP extends ColumnLabelProvider { - private static final long serialVersionUID = 2465059387875338553L; - - @Override - public String getText(Object element) { - if (element instanceof Node) { - Node curr = ((Node) element); - try { - return curr.getName(); - } catch (RepositoryException e) { - throw new CmsException("Unable to get name for" + curr); - } - } - return super.getText(element); - } - } - } -} \ No newline at end of file diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/ConnectivityDeploymentUi.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/ConnectivityDeploymentUi.java deleted file mode 100644 index 97f3e6713..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/ConnectivityDeploymentUi.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.argeo.cms.e4.maintenance; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; -import org.osgi.framework.ServiceReference; -import org.osgi.service.http.HttpService; -import org.osgi.service.useradmin.UserAdmin; - -class ConnectivityDeploymentUi extends AbstractOsgiComposite { - private static final long serialVersionUID = 590221539553514693L; - - public ConnectivityDeploymentUi(Composite parent, int style) { - super(parent, style); - } - - @Override - protected void initUi(int style) { - StringBuffer text = new StringBuffer(); - text.append("Provided Servers
"); - - ServiceReference userAdminRef = bc.getServiceReference(HttpService.class); - if (userAdminRef != null) { - // FIXME use constants - Object httpPort = userAdminRef.getProperty("http.port"); - Object httpsPort = userAdminRef.getProperty("https.port"); - if (httpPort != null) - text.append("http ").append(httpPort).append("
"); - if (httpsPort != null) - text.append("https ").append(httpsPort).append("
"); - - } - - text.append("
"); - text.append("Referenced Servers
"); - - Label label = new Label(this, SWT.NONE); - label.setData(new GridData(SWT.FILL, SWT.FILL, false, false)); - CmsSwtUtils.markup(label); - label.setText(text.toString()); - } - - protected boolean isDeployed() { - return bc.getServiceReference(UserAdmin.class) != null; - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DataDeploymentUi.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DataDeploymentUi.java deleted file mode 100644 index ef95bde64..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DataDeploymentUi.java +++ /dev/null @@ -1,139 +0,0 @@ -package org.argeo.cms.e4.maintenance; - -import java.io.File; -import java.io.IOException; -import java.nio.file.FileStore; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Collection; - -import org.apache.jackrabbit.core.RepositoryContext; -import org.apache.jackrabbit.core.config.RepositoryConfig; -import org.argeo.api.cms.CmsConstants; -import org.argeo.cms.swt.CmsSwtUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; -import org.osgi.framework.ServiceReference; - -class DataDeploymentUi extends AbstractOsgiComposite { - private static final long serialVersionUID = 590221539553514693L; - - public DataDeploymentUi(Composite parent, int style) { - super(parent, style); - } - - @Override - protected void initUi(int style) { - if (isDeployed()) { - initCurrentUi(this); - } else { - initNewUi(this); - } - } - - private void initNewUi(Composite parent) { -// try { -// ConfigurationAdmin confAdmin = bc.getService(bc.getServiceReference(ConfigurationAdmin.class)); -// Configuration[] confs = confAdmin.listConfigurations( -// "(" + ConfigurationAdmin.SERVICE_FACTORYPID + "=" + NodeConstants.NODE_REPOS_FACTORY_PID + ")"); -// if (confs == null || confs.length == 0) { -// Group buttonGroup = new Group(parent, SWT.NONE); -// buttonGroup.setText("Repository Type"); -// buttonGroup.setLayout(new GridLayout(2, true)); -// buttonGroup.setLayoutData(new GridData(GridData.FILL_VERTICAL)); -// -// SelectionListener selectionListener = new SelectionAdapter() { -// private static final long serialVersionUID = 6247064348421088092L; -// -// public void widgetSelected(SelectionEvent event) { -// Button radio = (Button) event.widget; -// if (!radio.getSelection()) -// return; -// log.debug(event); -// JackrabbitType nodeType = (JackrabbitType) radio.getData(); -// if (log.isDebugEnabled()) -// log.debug(" selected = " + nodeType.name()); -// }; -// }; -// -// for (JackrabbitType nodeType : JackrabbitType.values()) { -// Button radio = new Button(buttonGroup, SWT.RADIO); -// radio.setText(nodeType.name()); -// radio.setData(nodeType); -// if (nodeType.equals(JackrabbitType.localfs)) -// radio.setSelection(true); -// radio.addSelectionListener(selectionListener); -// } -// -// } else if (confs.length == 1) { -// -// } else { -// throw new CmsException("Multiple repos not yet supported"); -// } -// } catch (Exception e) { -// throw new CmsException("Cannot initialize UI", e); -// } - - } - - private void initCurrentUi(Composite parent) { - parent.setLayout(new GridLayout()); - Collection> contexts = getServiceReferences(RepositoryContext.class, - "(" + CmsConstants.CN + "=*)"); - StringBuffer text = new StringBuffer(); - text.append("Jackrabbit Repositories
"); - for (ServiceReference sr : contexts) { - RepositoryContext repositoryContext = bc.getService(sr); - String alias = sr.getProperty(CmsConstants.CN).toString(); - String rootNodeId = repositoryContext.getRootNodeId().toString(); - RepositoryConfig repositoryConfig = repositoryContext.getRepositoryConfig(); - Path repoHomePath = new File(repositoryConfig.getHomeDir()).toPath().toAbsolutePath(); - // TODO check data store - - text.append("" + alias + "
"); - text.append("rootNodeId: " + rootNodeId + "
"); - try { - FileStore fileStore = Files.getFileStore(repoHomePath); - text.append("partition: " + fileStore.toString() + "
"); - text.append( - percentUsed(fileStore) + " used (" + humanReadable(fileStore.getUsableSpace()) + " free)
"); - } catch (IOException e) { - log.error("Cannot check fileStore for " + repoHomePath, e); - } - } - Label label = new Label(parent, SWT.NONE); - label.setData(new GridData(SWT.FILL, SWT.FILL, false, false)); - CmsSwtUtils.markup(label); - label.setText("" + text.toString() + ""); - } - - private String humanReadable(long bytes) { - long mb = bytes / (1024 * 1024); - return mb >= 2048 ? Long.toString(mb / 1024) + " GB" : Long.toString(mb) + " MB"; - } - - private String percentUsed(FileStore fs) throws IOException { - long used = fs.getTotalSpace() - fs.getUnallocatedSpace(); - long percent = used * 100 / fs.getTotalSpace(); - if (log.isTraceEnabled()) { - // output identical to `df -B 1`) - log.trace(fs.getTotalSpace() + "," + used + "," + fs.getUsableSpace()); - } - String span; - if (percent < 80) - span = ""; - else if (percent < 95) - span = ""; - else - span = ""; - return span + percent + "%"; - } - - protected boolean isDeployed() { - return bc.getServiceReference(RepositoryContext.class) != null; - } - -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DeploymentEntryPoint.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DeploymentEntryPoint.java deleted file mode 100644 index e713f53e1..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DeploymentEntryPoint.java +++ /dev/null @@ -1,96 +0,0 @@ -package org.argeo.cms.e4.maintenance; - -import java.util.GregorianCalendar; -import java.util.TimeZone; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.api.cms.CmsContext; -import org.argeo.api.cms.CmsDeployment; -import org.argeo.api.cms.CmsState; -import org.argeo.cms.swt.CmsSwtUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.FillLayout; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Group; -import org.eclipse.swt.widgets.Label; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.ServiceReference; - -class DeploymentEntryPoint { - private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext(); - - protected void createContents(Composite parent) { - // FIXME manage authentication if needed - // if (!CurrentUser.roles().contains(AuthConstants.ROLE_ADMIN)) - // return; - - // parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - if (isDesktop()) { - parent.setLayout(new GridLayout(2, true)); - } else { - // TODO add scrolling - parent.setLayout(new GridLayout(1, true)); - } - - initHighLevelSummary(parent); - - Group securityGroup = createHighLevelGroup(parent, "Security"); - securityGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); - new SecurityDeploymentUi(securityGroup, SWT.NONE); - - Group dataGroup = createHighLevelGroup(parent, "Data"); - dataGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); - new DataDeploymentUi(dataGroup, SWT.NONE); - - Group logGroup = createHighLevelGroup(parent, "Notifications"); - logGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true)); - new LogDeploymentUi(logGroup, SWT.NONE); - - Group connectivityGroup = createHighLevelGroup(parent, "Connectivity"); - new ConnectivityDeploymentUi(connectivityGroup, SWT.NONE); - connectivityGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true)); - - } - - private void initHighLevelSummary(Composite parent) { - Composite composite = new Composite(parent, SWT.NONE); - GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false); - if (isDesktop()) - gridData.horizontalSpan = 3; - composite.setLayoutData(gridData); - composite.setLayout(new FillLayout()); - - ServiceReference nodeStateRef = bc.getServiceReference(CmsState.class); - if (nodeStateRef == null) - throw new IllegalStateException("No CMS state available"); - CmsState nodeState = bc.getService(nodeStateRef); - ServiceReference nodeDeploymentRef = bc.getServiceReference(CmsContext.class); - Label label = new Label(composite, SWT.WRAP); - CmsSwtUtils.markup(label); - if (nodeDeploymentRef == null) { - label.setText("Not yet deployed on
" + nodeState.getHostname() + "
, please configure below."); - } else { - Object stateUuid = nodeStateRef.getProperty(CmsConstants.CN); - CmsContext nodeDeployment = bc.getService(nodeDeploymentRef); - GregorianCalendar calendar = new GregorianCalendar(); - calendar.setTimeInMillis(nodeDeployment.getAvailableSince()); - calendar.setTimeZone(TimeZone.getDefault()); - label.setText("[" + "" + nodeState.getHostname() + "]# " + "Deployment state " + stateUuid - + ", available since " + calendar.getTime() + ""); - } - } - - private static Group createHighLevelGroup(Composite parent, String text) { - Group group = new Group(parent, SWT.NONE); - group.setText(text); - CmsSwtUtils.markup(group); - return group; - } - - private boolean isDesktop() { - return true; - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/LogDeploymentUi.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/LogDeploymentUi.java deleted file mode 100644 index fa5d3dae3..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/LogDeploymentUi.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.argeo.cms.e4.maintenance; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Enumeration; -import java.util.GregorianCalendar; -import java.util.TimeZone; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Text; -import org.osgi.service.log.LogEntry; -import org.osgi.service.log.LogListener; -import org.osgi.service.log.LogReaderService; - -class LogDeploymentUi extends AbstractOsgiComposite implements LogListener { - private static final long serialVersionUID = 590221539553514693L; - - private DateFormat dateFormat = new SimpleDateFormat("MMdd HH:mm"); - - private Display display; - private Text logDisplay; - - public LogDeploymentUi(Composite parent, int style) { - super(parent, style); - } - - @Override - protected void initUi(int style) { - LogReaderService logReader = getService(LogReaderService.class); - // FIXME use server push - // logReader.addLogListener(this); - this.display = getDisplay(); - this.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - logDisplay = new Text(this, SWT.WRAP | SWT.MULTI | SWT.READ_ONLY); - logDisplay.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - CmsSwtUtils.markup(logDisplay); - Enumeration logEntries = (Enumeration) logReader.getLog(); - while (logEntries.hasMoreElements()) - logDisplay.append(printEntry(logEntries.nextElement())); - } - - private String printEntry(LogEntry entry) { - StringBuilder sb = new StringBuilder(); - GregorianCalendar calendar = new GregorianCalendar(TimeZone.getDefault()); - calendar.setTimeInMillis(entry.getTime()); - sb.append(dateFormat.format(calendar.getTime())).append(' '); - sb.append(entry.getMessage()); - sb.append('\n'); - return sb.toString(); - } - - @Override - public void logged(LogEntry entry) { - if (display.isDisposed()) - return; - display.asyncExec(() -> { - if (logDisplay.isDisposed()) - return; - logDisplay.append(printEntry(entry)); - }); - display.wake(); - } - - // @Override - // public void dispose() { - // super.dispose(); - // getService(LogReaderService.class).removeLogListener(this); - // } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/MaintenanceStyles.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/MaintenanceStyles.java deleted file mode 100644 index df1be51ad..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/MaintenanceStyles.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.argeo.cms.e4.maintenance; - -/** Specific styles used by the various maintenance pages . */ -public interface MaintenanceStyles { - // General - public final static String PREFIX = "maintenance_"; - - // Browser - public final static String BROWSER_COLUMN = "browser_column"; - } diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/NonAdminPage.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/NonAdminPage.java deleted file mode 100644 index cb38ce899..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/NonAdminPage.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.argeo.cms.e4.maintenance; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ui.CmsUiProvider; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; - -public class NonAdminPage implements CmsUiProvider{ - - @Override - public Control createUi(Composite parent, Node context) - throws RepositoryException { - Composite body = new Composite(parent, SWT.NO_FOCUS); - body.setLayoutData(CmsSwtUtils.fillAll()); - body.setLayout(new GridLayout()); - Label label = new Label(body, SWT.NONE); - label.setText("You should be an admin to perform maintenance operations. " - + "Are you sure you are logged in?"); - label.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true)); - return null; - } - -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/SecurityDeploymentUi.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/SecurityDeploymentUi.java deleted file mode 100644 index 3492c5499..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/SecurityDeploymentUi.java +++ /dev/null @@ -1,85 +0,0 @@ -package org.argeo.cms.e4.maintenance; - -import java.net.URI; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.framework.ServiceReference; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.UserAdmin; - -class SecurityDeploymentUi extends AbstractOsgiComposite { - private static final long serialVersionUID = 590221539553514693L; - - public SecurityDeploymentUi(Composite parent, int style) { - super(parent, style); - } - - @Override - protected void initUi(int style) { - if (isDeployed()) { - initCurrentUi(this); - } else { - initNewUi(this); - } - } - - private void initNewUi(Composite parent) { - new Label(parent, SWT.NONE).setText("Security is not configured"); - } - - private void initCurrentUi(Composite parent) { - ServiceReference userAdminRef = bc.getServiceReference(UserAdmin.class); - UserAdmin userAdmin = bc.getService(userAdminRef); - StringBuffer text = new StringBuffer(); - text.append("Domains
"); - domains: for (String key : userAdminRef.getPropertyKeys()) { - if (!key.startsWith("/")) - continue domains; - URI uri; - try { - uri = new URI(key); - } catch (Exception e) { - // ignore non URI keys - continue domains; - } - - String rootDn = uri.getPath().substring(1, uri.getPath().length()); - // FIXME make reading query options more robust, using utils - boolean readOnly = uri.getQuery().equals("readOnly=true"); - if (readOnly) - text.append(""); - else - text.append(""); - - text.append(rootDn); - text.append("
"); - try { - Role[] roles = userAdmin.getRoles("(dn=*," + rootDn + ")"); - long userCount = 0; - long groupCount = 0; - for (Role role : roles) { - if (role.getType() == Role.USER) - userCount++; - else - groupCount++; - } - text.append(" " + userCount + " users, " + groupCount +" groups.
"); - } catch (InvalidSyntaxException e) { - log.error("Invalid syntax", e); - } - } - Label label = new Label(parent, SWT.NONE); - label.setData(new GridData(SWT.FILL, SWT.FILL, false, false)); - CmsSwtUtils.markup(label); - label.setText(text.toString()); - } - - protected boolean isDeployed() { - return bc.getServiceReference(UserAdmin.class) != null; - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/package-info.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/package-info.java deleted file mode 100644 index e4d2ad4c3..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Maintenance perspective. */ -package org.argeo.cms.e4.maintenance; \ No newline at end of file diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/BundleNode.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/BundleNode.java deleted file mode 100644 index e9536830f..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/BundleNode.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.argeo.cms.e4.monitoring; - -import org.argeo.cms.ux.widgets.TreeParent; -import org.osgi.framework.Bundle; -import org.osgi.framework.ServiceReference; - -/** A tree element representing a {@link Bundle} */ -class BundleNode extends TreeParent { - private final Bundle bundle; - - public BundleNode(Bundle bundle) { - this(bundle, false); - } - - @SuppressWarnings("rawtypes") - public BundleNode(Bundle bundle, boolean hasChildren) { - super(bundle.getSymbolicName()); - this.bundle = bundle; - - if (hasChildren) { - // REFERENCES - ServiceReference[] usedServices = bundle.getServicesInUse(); - if (usedServices != null) { - for (ServiceReference sr : usedServices) { - if (sr != null) - addChild(new ServiceReferenceNode(sr, false)); - } - } - - // SERVICES - ServiceReference[] registeredServices = bundle - .getRegisteredServices(); - if (registeredServices != null) { - for (ServiceReference sr : registeredServices) { - if (sr != null) - addChild(new ServiceReferenceNode(sr, true)); - } - } - } - - } - - Bundle getBundle() { - return bundle; - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/BundlesView.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/BundlesView.java deleted file mode 100644 index c639255b6..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/BundlesView.java +++ /dev/null @@ -1,114 +0,0 @@ -//package org.argeo.eclipse.ui.workbench.osgi; -//public class BundlesView {} - -package org.argeo.cms.e4.monitoring; - -import javax.annotation.PostConstruct; - -import org.argeo.eclipse.ui.ColumnViewerComparator; -import org.argeo.eclipse.ui.specific.EclipseUiSpecificUtils; -import org.eclipse.e4.ui.di.Focus; -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.jface.viewers.IStructuredContentProvider; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.jface.viewers.TableViewerColumn; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; - -/** - * Overview of the bundles as a table. Equivalent to Equinox 'ss' console - * command. - */ -public class BundlesView { - private final static BundleContext bc = FrameworkUtil.getBundle(BundlesView.class).getBundleContext(); - private TableViewer viewer; - - @PostConstruct - public void createPartControl(Composite parent) { - viewer = new TableViewer(parent); - viewer.setContentProvider(new BundleContentProvider()); - viewer.getTable().setHeaderVisible(true); - - EclipseUiSpecificUtils.enableToolTipSupport(viewer); - - // ID - TableViewerColumn column = new TableViewerColumn(viewer, SWT.NONE); - column.getColumn().setWidth(30); - column.getColumn().setText("ID"); - column.getColumn().setAlignment(SWT.RIGHT); - column.setLabelProvider(new ColumnLabelProvider() { - private static final long serialVersionUID = -3122136344359358605L; - - public String getText(Object element) { - return Long.toString(((Bundle) element).getBundleId()); - } - }); - new ColumnViewerComparator(column); - - // State - column = new TableViewerColumn(viewer, SWT.NONE); - column.getColumn().setWidth(18); - column.getColumn().setText("State"); - column.setLabelProvider(new StateLabelProvider()); - new ColumnViewerComparator(column); - - // Symbolic name - column = new TableViewerColumn(viewer, SWT.NONE); - column.getColumn().setWidth(250); - column.getColumn().setText("Symbolic Name"); - column.setLabelProvider(new ColumnLabelProvider() { - private static final long serialVersionUID = -4280840684440451080L; - - public String getText(Object element) { - return ((Bundle) element).getSymbolicName(); - } - }); - new ColumnViewerComparator(column); - - // Version - column = new TableViewerColumn(viewer, SWT.NONE); - column.getColumn().setWidth(250); - column.getColumn().setText("Version"); - column.setLabelProvider(new ColumnLabelProvider() { - private static final long serialVersionUID = 6871926308708629989L; - - public String getText(Object element) { - Bundle bundle = (org.osgi.framework.Bundle) element; - return bundle.getVersion().toString(); - } - }); - new ColumnViewerComparator(column); - - viewer.setInput(bc); - - } - - @Focus - public void setFocus() { - if (viewer != null) - viewer.getControl().setFocus(); - } - - /** Content provider managing the array of bundles */ - private static class BundleContentProvider implements IStructuredContentProvider { - private static final long serialVersionUID = -8533792785725875977L; - - public Object[] getElements(Object inputElement) { - if (inputElement instanceof BundleContext) { - BundleContext bc = (BundleContext) inputElement; - return bc.getBundles(); - } - return null; - } - - public void dispose() { - } - - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - } - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/CmsSessionsView.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/CmsSessionsView.java deleted file mode 100644 index 95b1eb2cb..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/CmsSessionsView.java +++ /dev/null @@ -1,173 +0,0 @@ -//package org.argeo.eclipse.ui.workbench.osgi; -//public class BundlesView {} - -package org.argeo.cms.e4.monitoring; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import javax.annotation.PostConstruct; - -import org.argeo.api.cms.CmsSession; -import org.argeo.cms.auth.RoleNameUtils; -import org.argeo.eclipse.ui.ColumnViewerComparator; -import org.argeo.eclipse.ui.specific.EclipseUiSpecificUtils; -import org.argeo.util.LangUtils; -import org.eclipse.e4.ui.di.Focus; -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.jface.viewers.IStructuredContentProvider; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.jface.viewers.TableViewerColumn; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.framework.ServiceReference; - -/** - * Overview of the active CMS sessions. - */ -public class CmsSessionsView { - private final static BundleContext bc = FrameworkUtil.getBundle(CmsSessionsView.class).getBundleContext(); - - private TableViewer viewer; - - @PostConstruct - public void createPartControl(Composite parent) { - viewer = new TableViewer(parent); - viewer.setContentProvider(new CmsSessionContentProvider()); - viewer.getTable().setHeaderVisible(true); - - EclipseUiSpecificUtils.enableToolTipSupport(viewer); - - int longColWidth = 150; - int smallColWidth = 100; - - // Display name - TableViewerColumn column = new TableViewerColumn(viewer, SWT.NONE); - column.getColumn().setWidth(longColWidth); - column.getColumn().setText("User"); - column.setLabelProvider(new ColumnLabelProvider() { - private static final long serialVersionUID = -5234573509093747505L; - - public String getText(Object element) { - return ((CmsSession) element).getDisplayName(); - } - - public String getToolTipText(Object element) { - return ((CmsSession) element).getUserDn().toString(); - } - }); - new ColumnViewerComparator(column); - - // Creation time - column = new TableViewerColumn(viewer, SWT.NONE); - column.getColumn().setWidth(smallColWidth); - column.getColumn().setText("Since"); - column.setLabelProvider(new ColumnLabelProvider() { - private static final long serialVersionUID = -5234573509093747505L; - - public String getText(Object element) { - return LangUtils.since(((CmsSession) element).getCreationTime()); - } - - public String getToolTipText(Object element) { - return ((CmsSession) element).getCreationTime().toString(); - } - }); - new ColumnViewerComparator(column); - - // Username - column = new TableViewerColumn(viewer, SWT.NONE); - column.getColumn().setWidth(smallColWidth); - column.getColumn().setText("Username"); - column.setLabelProvider(new ColumnLabelProvider() { - private static final long serialVersionUID = -5234573509093747505L; - - public String getText(Object element) { - String userDn = ((CmsSession) element).getUserDn(); - return RoleNameUtils.getLastRdnValue(userDn); - } - - public String getToolTipText(Object element) { - return ((CmsSession) element).getUserDn().toString(); - } - }); - new ColumnViewerComparator(column); - - // UUID - column = new TableViewerColumn(viewer, SWT.NONE); - column.getColumn().setWidth(smallColWidth); - column.getColumn().setText("UUID"); - column.setLabelProvider(new ColumnLabelProvider() { - private static final long serialVersionUID = -5234573509093747505L; - - public String getText(Object element) { - return ((CmsSession) element).getUuid().toString(); - } - - public String getToolTipText(Object element) { - return getText(element); - } - }); - new ColumnViewerComparator(column); - - // Local ID - column = new TableViewerColumn(viewer, SWT.NONE); - column.getColumn().setWidth(smallColWidth); - column.getColumn().setText("Local ID"); - column.setLabelProvider(new ColumnLabelProvider() { - private static final long serialVersionUID = -5234573509093747505L; - - public String getText(Object element) { - return ((CmsSession) element).getLocalId(); - } - - public String getToolTipText(Object element) { - return getText(element); - } - }); - new ColumnViewerComparator(column); - - viewer.setInput(bc); - - } - - @Focus - public void setFocus() { - if (viewer != null) - viewer.getControl().setFocus(); - } - - /** Content provider managing the array of bundles */ - private static class CmsSessionContentProvider implements IStructuredContentProvider { - private static final long serialVersionUID = -8533792785725875977L; - - public Object[] getElements(Object inputElement) { - if (inputElement instanceof BundleContext) { - BundleContext bc = (BundleContext) inputElement; - Collection> srs; - try { - srs = bc.getServiceReferences(CmsSession.class, null); - } catch (InvalidSyntaxException e) { - throw new IllegalArgumentException("Cannot retrieve CMS sessions", e); - } - List res = new ArrayList<>(); - for (ServiceReference sr : srs) { - res.add(bc.getService(sr)); - } - return res.toArray(); - } - return null; - } - - public void dispose() { - } - - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - } - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/ModulesView.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/ModulesView.java deleted file mode 100644 index 6317882c4..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/ModulesView.java +++ /dev/null @@ -1,91 +0,0 @@ -package org.argeo.cms.e4.monitoring; - -import java.util.ArrayList; -import java.util.List; - -import javax.annotation.PostConstruct; - -import org.argeo.cms.ux.widgets.TreeParent; -import org.eclipse.e4.ui.di.Focus; -import org.eclipse.jface.viewers.ITreeContentProvider; -import org.eclipse.jface.viewers.TreeViewer; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; - -/** The OSGi runtime from a module perspective. */ -public class ModulesView { - private final static BundleContext bc = FrameworkUtil.getBundle(ModulesView.class).getBundleContext(); - private TreeViewer viewer; - - @PostConstruct - public void createPartControl(Composite parent) { - viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); - viewer.setContentProvider(new ModulesContentProvider()); - viewer.setLabelProvider(new ModulesLabelProvider()); - viewer.setInput(bc); - } - - @Focus - public void setFocus() { - viewer.getTree().setFocus(); - } - - private class ModulesContentProvider implements ITreeContentProvider { - private static final long serialVersionUID = 3819934804640641721L; - - public Object[] getElements(Object inputElement) { - return getChildren(inputElement); - } - - public Object[] getChildren(Object parentElement) { - if (parentElement instanceof BundleContext) { - BundleContext bundleContext = (BundleContext) parentElement; - Bundle[] bundles = bundleContext.getBundles(); - - List modules = new ArrayList(); - for (Bundle bundle : bundles) { - if (bundle.getState() == Bundle.ACTIVE) - modules.add(new BundleNode(bundle, true)); - } - return modules.toArray(); - } else if (parentElement instanceof TreeParent) { - return ((TreeParent) parentElement).getChildren(); - } else { - return null; - } - } - - public Object getParent(Object element) { - // TODO Auto-generated method stub - return null; - } - - public boolean hasChildren(Object element) { - if (element instanceof TreeParent) { - return ((TreeParent) element).hasChildren(); - } - return false; - } - - public void dispose() { - } - - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - } - } - - private class ModulesLabelProvider extends StateLabelProvider { - private static final long serialVersionUID = 5290046145534824722L; - - @Override - public String getText(Object element) { - if (element instanceof BundleNode) - return element.toString() + " [" + ((BundleNode) element).getBundle().getBundleId() + "]"; - return element.toString(); - } - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/OsgiConfigurationsView.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/OsgiConfigurationsView.java deleted file mode 100644 index 5db8bd151..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/OsgiConfigurationsView.java +++ /dev/null @@ -1,163 +0,0 @@ -package org.argeo.cms.e4.monitoring; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.Dictionary; -import java.util.List; - -import javax.annotation.PostConstruct; - -import org.argeo.cms.swt.CmsException; -import org.argeo.util.LangUtils; -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.jface.viewers.ITreeContentProvider; -import org.eclipse.jface.viewers.TreeViewer; -import org.eclipse.jface.viewers.TreeViewerColumn; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.widgets.Composite; -import org.osgi.framework.BundleContext; -import org.osgi.framework.Constants; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.service.cm.Configuration; -import org.osgi.service.cm.ConfigurationAdmin; - -public class OsgiConfigurationsView { - private final static BundleContext bc = FrameworkUtil.getBundle(OsgiConfigurationsView.class).getBundleContext(); - - @PostConstruct - public void createPartControl(Composite parent) { - ConfigurationAdmin configurationAdmin = bc.getService(bc.getServiceReference(ConfigurationAdmin.class)); - - TreeViewer viewer = new TreeViewer(parent); - // viewer.getTree().setHeaderVisible(true); - - TreeViewerColumn tvc = new TreeViewerColumn(viewer, SWT.NONE); - tvc.getColumn().setWidth(400); - tvc.setLabelProvider(new ColumnLabelProvider() { - private static final long serialVersionUID = 835407996597566763L; - - @Override - public String getText(Object element) { - if (element instanceof Configuration) { - return ((Configuration) element).getPid(); - } else if (element instanceof Prop) { - return ((Prop) element).key; - } - return super.getText(element); - } - - @Override - public Image getImage(Object element) { - if (element instanceof Configuration) - return OsgiExplorerImages.CONFIGURATION; - return null; - } - - }); - - tvc = new TreeViewerColumn(viewer, SWT.NONE); - tvc.getColumn().setWidth(400); - tvc.setLabelProvider(new ColumnLabelProvider() { - private static final long serialVersionUID = 6999659261190014687L; - - @Override - public String getText(Object element) { - if (element instanceof Configuration) { - // return ((Configuration) element).getFactoryPid(); - return null; - } else if (element instanceof Prop) { - return ((Prop) element).value.toString(); - } - return super.getText(element); - } - }); - - viewer.setContentProvider(new ConfigurationsContentProvider()); - viewer.setInput(configurationAdmin); - } - - static class ConfigurationsContentProvider implements ITreeContentProvider { - private static final long serialVersionUID = -4892768279440981042L; - private ConfigurationComparator configurationComparator = new ConfigurationComparator(); - - @Override - public void dispose() { - } - - @Override - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - } - - @Override - public Object[] getElements(Object inputElement) { - ConfigurationAdmin configurationAdmin = (ConfigurationAdmin) inputElement; - try { - Configuration[] configurations = configurationAdmin.listConfigurations(null); - Arrays.sort(configurations, configurationComparator); - return configurations; - } catch (IOException | InvalidSyntaxException e) { - throw new CmsException("Cannot list configurations", e); - } - } - - @Override - public Object[] getChildren(Object parentElement) { - if (parentElement instanceof Configuration) { - List res = new ArrayList<>(); - Configuration configuration = (Configuration) parentElement; - Dictionary props = configuration.getProperties(); - keys: for (String key : LangUtils.keys(props)) { - if (Constants.SERVICE_PID.equals(key)) - continue keys; - if (ConfigurationAdmin.SERVICE_FACTORYPID.equals(key)) - continue keys; - res.add(new Prop(configuration, key, props.get(key))); - } - return res.toArray(new Prop[res.size()]); - } - return null; - } - - @Override - public Object getParent(Object element) { - if (element instanceof Prop) - return ((Prop) element).configuration; - return null; - } - - @Override - public boolean hasChildren(Object element) { - if (element instanceof Configuration) - return true; - return false; - } - - } - - static class Prop { - final Configuration configuration; - final String key; - final Object value; - - public Prop(Configuration configuration, String key, Object value) { - this.configuration = configuration; - this.key = key; - this.value = value; - } - - } - - static class ConfigurationComparator implements Comparator { - - @Override - public int compare(Configuration o1, Configuration o2) { - return o1.getPid().compareTo(o2.getPid()); - } - - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/OsgiExplorerImages.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/OsgiExplorerImages.java deleted file mode 100644 index 7217fe612..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/OsgiExplorerImages.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.argeo.cms.e4.monitoring; - -import org.argeo.cms.ui.theme.CmsImages; -import org.eclipse.swt.graphics.Image; - -/** Shared icons. */ -public class OsgiExplorerImages extends CmsImages { - public final static Image INSTALLED = createIcon("installed.gif"); - public final static Image RESOLVED = createIcon("resolved.gif"); - public final static Image STARTING = createIcon("starting.gif"); - public final static Image ACTIVE = createIcon("active.gif"); - public final static Image SERVICE_PUBLISHED = createIcon("service_published.gif"); - public final static Image SERVICE_REFERENCED = createIcon("service_referenced.gif"); - public final static Image CONFIGURATION = createIcon("node.gif"); -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/ServiceReferenceNode.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/ServiceReferenceNode.java deleted file mode 100644 index 1c60811d2..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/ServiceReferenceNode.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.argeo.cms.e4.monitoring; - -import org.argeo.cms.ux.widgets.TreeParent; -import org.osgi.framework.Bundle; -import org.osgi.framework.ServiceReference; - -/** A tree element representing a {@link ServiceReference} */ -@SuppressWarnings({ "rawtypes" }) -class ServiceReferenceNode extends TreeParent { - private final ServiceReference serviceReference; - private final boolean published; - - public ServiceReferenceNode(ServiceReference serviceReference, - boolean published) { - super(serviceReference.toString()); - this.serviceReference = serviceReference; - this.published = published; - - if (isPublished()) { - Bundle[] usedBundles = serviceReference.getUsingBundles(); - if (usedBundles != null) { - for (Bundle b : usedBundles) { - if (b != null) - addChild(new BundleNode(b)); - } - } - } else { - Bundle provider = serviceReference.getBundle(); - addChild(new BundleNode(provider)); - } - - for (String key : serviceReference.getPropertyKeys()) { - addChild(new TreeParent(key + "=" - + serviceReference.getProperty(key))); - } - - } - - public ServiceReference getServiceReference() { - return serviceReference; - } - - public boolean isPublished() { - return published; - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/StateLabelProvider.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/StateLabelProvider.java deleted file mode 100644 index 5cb5b6563..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/StateLabelProvider.java +++ /dev/null @@ -1,82 +0,0 @@ -package org.argeo.cms.e4.monitoring; - -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.swt.graphics.Image; -import org.osgi.framework.Bundle; -import org.osgi.framework.Constants; - -/** Label provider showing the sate of bundles */ -class StateLabelProvider extends ColumnLabelProvider { - private static final long serialVersionUID = -7885583135316000733L; - - @Override - public Image getImage(Object element) { - int state; - if (element instanceof Bundle) - state = ((Bundle) element).getState(); - else if (element instanceof BundleNode) - state = ((BundleNode) element).getBundle().getState(); - else if (element instanceof ServiceReferenceNode) - if (((ServiceReferenceNode) element).isPublished()) - return OsgiExplorerImages.SERVICE_PUBLISHED; - else - return OsgiExplorerImages.SERVICE_REFERENCED; - else - return null; - - switch (state) { - case Bundle.UNINSTALLED: - return OsgiExplorerImages.INSTALLED; - case Bundle.INSTALLED: - return OsgiExplorerImages.INSTALLED; - case Bundle.RESOLVED: - return OsgiExplorerImages.RESOLVED; - case Bundle.STARTING: - return OsgiExplorerImages.STARTING; - case Bundle.STOPPING: - return OsgiExplorerImages.STARTING; - case Bundle.ACTIVE: - return OsgiExplorerImages.ACTIVE; - default: - return null; - } - } - - @Override - public String getText(Object element) { - return null; - } - - @Override - public String getToolTipText(Object element) { - Bundle bundle = (Bundle) element; - Integer state = bundle.getState(); - switch (state) { - case Bundle.UNINSTALLED: - return "UNINSTALLED"; - case Bundle.INSTALLED: - return "INSTALLED"; - case Bundle.RESOLVED: - return "RESOLVED"; - case Bundle.STARTING: - String activationPolicy = bundle.getHeaders() - .get(Constants.BUNDLE_ACTIVATIONPOLICY).toString(); - - // .get("Bundle-ActivationPolicy").toString(); - // FIXME constant triggers the compilation failure - if (activationPolicy != null - && activationPolicy.equals(Constants.ACTIVATION_LAZY)) - // && activationPolicy.equals("lazy")) - // FIXME constant triggers the compilation failure - // && activationPolicy.equals(Constants.ACTIVATION_LAZY)) - return "<>"; - return "STARTING"; - case Bundle.STOPPING: - return "STOPPING"; - case Bundle.ACTIVE: - return "ACTIVE"; - default: - return null; - } - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/package-info.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/package-info.java deleted file mode 100644 index 873bf3118..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Monitoring perspective. */ -package org.argeo.cms.e4.monitoring; \ No newline at end of file diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/package-info.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/package-info.java deleted file mode 100644 index 233119c0d..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Eclipse 4 user interfaces. */ -package org.argeo.cms.e4; \ No newline at end of file diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/parts/EgoDashboard.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/parts/EgoDashboard.java deleted file mode 100644 index f2a73f210..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/parts/EgoDashboard.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.argeo.cms.e4.parts; - -import java.time.ZonedDateTime; - -import javax.annotation.PostConstruct; - -import org.argeo.api.cms.CmsSession; -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.swt.CmsSwtUtils; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; - -/** A canonical view of the logged in user. */ -public class EgoDashboard { -// private BundleContext bc = FrameworkUtil.getBundle(EgoDashboard.class).getBundleContext(); - - @PostConstruct - public void createPartControl(Composite p) { - p.setLayout(new GridLayout()); - String username = CurrentUser.getUsername(); - - CmsSwtUtils.lbl(p, "" + CurrentUser.getDisplayName() + ""); - CmsSwtUtils.txt(p, username); - CmsSwtUtils.lbl(p, "Roles:"); - roles: for (String role : CurrentUser.roles()) { - if (username.equals(role)) - continue roles; - CmsSwtUtils.txt(p, role); - } - -// Subject subject = Subject.getSubject(AccessController.getContext()); -// if (subject != null) { - CmsSession cmsSession = CurrentUser.getCmsSession(); - ZonedDateTime loggedIndSince = cmsSession.getCreationTime(); - CmsSwtUtils.lbl(p, "Session:"); - CmsSwtUtils.txt(p, cmsSession.getUuid().toString()); - CmsSwtUtils.lbl(p, "Logged in since:"); - CmsSwtUtils.txt(p, loggedIndSince.toString()); -// } - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/AbstractRoleEditor.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/AbstractRoleEditor.java deleted file mode 100644 index 137f76242..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/AbstractRoleEditor.java +++ /dev/null @@ -1,287 +0,0 @@ -package org.argeo.cms.e4.users; - -import java.util.ArrayList; -import java.util.List; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import javax.inject.Inject; - -import org.argeo.cms.auth.UserAdminUtils; -import org.argeo.cms.ui.eclipse.forms.AbstractFormPart; -import org.argeo.cms.ui.eclipse.forms.IManagedForm; -import org.argeo.cms.ui.eclipse.forms.ManagedForm; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.util.naming.LdapAttrs; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.e4.ui.di.Persist; -import org.eclipse.e4.ui.model.application.ui.basic.MPart; -import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.ScrolledComposite; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; -import org.osgi.service.useradmin.Authorization; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdmin; -import org.osgi.service.useradmin.UserAdminEvent; - -/** Editor for a user, might be a user or a group. */ -public abstract class AbstractRoleEditor { - - // public final static String USER_EDITOR_ID = WorkbenchUiPlugin.PLUGIN_ID + - // ".userEditor"; - // public final static String GROUP_EDITOR_ID = WorkbenchUiPlugin.PLUGIN_ID + - // ".groupEditor"; - - /* DEPENDENCY INJECTION */ - @Inject - protected UserAdminWrapper userAdminWrapper; - - @Inject - private MPart mPart; - - // @Inject - // Composite parent; - - private UserAdmin userAdmin; - - // Context - private User user; - private String username; - - private NameChangeListener listener; - - private ManagedForm managedForm; - - // public void init(IEditorSite site, IEditorInput input) throws - // PartInitException { - @PostConstruct - public void init(Composite parent) { - this.userAdmin = userAdminWrapper.getUserAdmin(); - username = mPart.getPersistedState().get(LdapAttrs.uid.name()); - user = (User) userAdmin.getRole(username); - - listener = new NameChangeListener(Display.getCurrent()); - userAdminWrapper.addListener(listener); - updateEditorTitle(null); - - managedForm = new ManagedForm(parent) { - - @Override - public void staleStateChanged() { - refresh(); - } - }; - ScrolledComposite scrolled = managedForm.getForm(); - Composite body = new Composite(scrolled, SWT.NONE); - scrolled.setContent(body); - createUi(body); - managedForm.refresh(); - } - - abstract void createUi(Composite parent); - - /** - * returns the list of all authorizations for the given user or of the current - * displayed user if parameter is null - */ - protected List getFlatGroups(User aUser) { - Authorization currAuth; - if (aUser == null) - currAuth = userAdmin.getAuthorization(this.user); - else - currAuth = userAdmin.getAuthorization(aUser); - - String[] roles = currAuth.getRoles(); - - List groups = new ArrayList(); - for (String roleStr : roles) { - User currRole = (User) userAdmin.getRole(roleStr); - if (currRole != null && !groups.contains(currRole)) - groups.add(currRole); - } - return groups; - } - - protected IManagedForm getManagedForm() { - return managedForm; - } - - /** Exposes the user (or group) that is displayed by the current editor */ - protected User getDisplayedUser() { - return user; - } - - private void setDisplayedUser(User user) { - this.user = user; - } - - void updateEditorTitle(String title) { - if (title == null) { - String commonName = UserAdminUtils.getProperty(user, LdapAttrs.cn.name()); - title = "".equals(commonName) ? user.getName() : commonName; - } - setPartName(title); - } - - protected void setPartName(String name) { - mPart.setLabel(name); - } - - // protected void addPages() { - // try { - // if (user.getType() == Role.GROUP) - // addPage(new GroupMainPage(this, userAdminWrapper, repository, nodeInstance)); - // else - // addPage(new UserMainPage(this, userAdminWrapper)); - // } catch (Exception e) { - // throw new CmsException("Cannot add pages", e); - // } - // } - - @Persist - public void doSave(IProgressMonitor monitor) { - userAdminWrapper.beginTransactionIfNeeded(); - commitPages(true); - userAdminWrapper.commitOrNotifyTransactionStateChange(); - // firePropertyChange(PROP_DIRTY); - userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_REMOVED, user)); - } - - protected void commitPages(boolean b) { - managedForm.commit(b); - } - - @PreDestroy - public void dispose() { - userAdminWrapper.removeListener(listener); - managedForm.dispose(); - } - - // CONTROLERS FOR THIS EDITOR AND ITS PAGES - - class NameChangeListener extends UiUserAdminListener { - public NameChangeListener(Display display) { - super(display); - } - - @Override - public void roleChangedToUiThread(UserAdminEvent event) { - Role changedRole = event.getRole(); - if (changedRole == null || changedRole.equals(user)) { - updateEditorTitle(null); - User reloadedUser = (User) userAdminWrapper.getUserAdmin().getRole(user.getName()); - setDisplayedUser(reloadedUser); - } - } - } - - class MainInfoListener extends UiUserAdminListener { - private final AbstractFormPart part; - - public MainInfoListener(Display display, AbstractFormPart part) { - super(display); - this.part = part; - } - - @Override - public void roleChangedToUiThread(UserAdminEvent event) { - // Rollback - if (event.getRole() == null) - part.markStale(); - } - } - - class GroupChangeListener extends UiUserAdminListener { - private final AbstractFormPart part; - - public GroupChangeListener(Display display, AbstractFormPart part) { - super(display); - this.part = part; - } - - @Override - public void roleChangedToUiThread(UserAdminEvent event) { - // always mark as stale - part.markStale(); - } - } - - /** Registers a listener that will notify this part */ - class FormPartML implements ModifyListener { - private static final long serialVersionUID = 6299808129505381333L; - private AbstractFormPart formPart; - - public FormPartML(AbstractFormPart generalPart) { - this.formPart = generalPart; - } - - public void modifyText(ModifyEvent e) { - // Discard event when the control does not have the focus, typically - // to avoid all editors being marked as dirty during a Rollback - if (((Control) e.widget).isFocusControl()) - formPart.markDirty(); - } - } - - /* DEPENDENCY INJECTION */ - public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { - this.userAdminWrapper = userAdminWrapper; - } - - /** Creates label and multiline text. */ - Text createLMT(Composite parent, String label, String value) { - Label lbl = new Label(parent, SWT.NONE); - lbl.setText(label); - lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false)); - Text text = new Text(parent, SWT.NONE); - text.setText(value); - text.setLayoutData(new GridData(SWT.LEAD, SWT.FILL, true, true)); - return text; - } - - /** Creates label and password. */ - Text createLP(Composite parent, String label, String value) { - Label lbl = new Label(parent, SWT.NONE); - lbl.setText(label); - lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false)); - Text text = new Text(parent, SWT.PASSWORD | SWT.BORDER); - text.setText(value); - text.setLayoutData(new GridData(SWT.LEAD, SWT.FILL, true, false)); - return text; - } - - /** Creates label and text. */ - Text createLT(Composite parent, String label, String value) { - Label lbl = new Label(parent, SWT.NONE); - lbl.setText(label); - lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false)); - lbl.setFont(EclipseUiUtils.getBoldFont(parent)); - Text text = new Text(parent, SWT.BORDER); - text.setText(value); - text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); - // CmsUiUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT); - return text; - } - - Text createReadOnlyLT(Composite parent, String label, String value) { - Label lbl = new Label(parent, SWT.NONE); - lbl.setText(label); - lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false)); - lbl.setFont(EclipseUiUtils.getBoldFont(parent)); - Text text = new Text(parent, SWT.NONE); - text.setText(value); - text.setLayoutData(new GridData(SWT.LEAD, SWT.FILL, true, false)); - text.setEditable(false); - // CmsUiUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT); - return text; - } - -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/CmsWorkbenchStyles.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/CmsWorkbenchStyles.java deleted file mode 100644 index 07df312e1..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/CmsWorkbenchStyles.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.argeo.cms.e4.users; - -/** Centralize the declaration of Workbench specific CSS Styles */ -interface CmsWorkbenchStyles { - - // Specific People layouting - String WORKBENCH_FORM_TEXT = "workbench_form_text"; -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupEditor.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupEditor.java deleted file mode 100644 index d54f8bc38..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupEditor.java +++ /dev/null @@ -1,566 +0,0 @@ -package org.argeo.cms.e4.users; - -import static org.argeo.api.cms.CmsContext.WORKGROUP; -import static org.argeo.cms.auth.UserAdminUtils.setProperty; -import static org.argeo.util.naming.LdapAttrs.businessCategory; -import static org.argeo.util.naming.LdapAttrs.description; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import javax.annotation.PreDestroy; -import javax.inject.Inject; -import javax.jcr.Node; -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.jcr.Session; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.api.cms.CmsContext; -import org.argeo.cms.auth.UserAdminUtils; -import org.argeo.cms.e4.users.providers.CommonNameLP; -import org.argeo.cms.e4.users.providers.MailLP; -import org.argeo.cms.e4.users.providers.RoleIconLP; -import org.argeo.cms.e4.users.providers.UserFilter; -import org.argeo.cms.jcr.CmsJcrUtils; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ui.eclipse.forms.AbstractFormPart; -import org.argeo.cms.ui.eclipse.forms.IManagedForm; -import org.argeo.eclipse.ui.ColumnDefinition; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.parts.LdifUsersTable; -import org.argeo.jcr.JcrException; -import org.argeo.jcr.JcrUtils; -import org.argeo.util.naming.LdapAttrs; -import org.argeo.util.transaction.WorkTransaction; -import org.eclipse.e4.ui.workbench.modeling.EPartService; -import org.eclipse.jface.action.Action; -import org.eclipse.jface.action.ToolBarManager; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.jface.viewers.ViewerDropAdapter; -import org.eclipse.swt.SWT; -import org.eclipse.swt.dnd.DND; -import org.eclipse.swt.dnd.DropTargetEvent; -import org.eclipse.swt.dnd.TextTransfer; -import org.eclipse.swt.dnd.Transfer; -import org.eclipse.swt.dnd.TransferData; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Link; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Text; -import org.eclipse.swt.widgets.ToolBar; -import org.osgi.service.useradmin.Group; -import org.osgi.service.useradmin.Role; -//import org.eclipse.ui.forms.AbstractFormPart; -//import org.eclipse.ui.forms.IManagedForm; -//import org.eclipse.ui.forms.SectionPart; -//import org.eclipse.ui.forms.editor.FormEditor; -//import org.eclipse.ui.forms.editor.FormPage; -//import org.eclipse.ui.forms.widgets.FormToolkit; -//import org.eclipse.ui.forms.widgets.ScrolledForm; -//import org.eclipse.ui.forms.widgets.Section; -import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdmin; -import org.osgi.service.useradmin.UserAdminEvent; - -/** Display/edit main properties of a given group */ -public class GroupEditor extends AbstractRoleEditor { - // final static String ID = "GroupEditor.mainPage"; - - @Inject - private EPartService partService; - - // private final UserEditor editor; - @Inject - private Repository repository; - @Inject - private CmsContext nodeInstance; - // private final UserAdminWrapper userAdminWrapper; - private Session groupsSession; - - // public GroupMainPage(FormEditor editor, UserAdminWrapper userAdminWrapper, - // Repository repository, - // NodeInstance nodeInstance) { - // super(editor, ID, "Main"); - // try { - // session = repository.login(); - // } catch (RepositoryException e) { - // throw new CmsException("Cannot retrieve session of in MainGroupPage - // constructor", e); - // } - // this.editor = (UserEditor) editor; - // this.userAdminWrapper = userAdminWrapper; - // this.nodeInstance = nodeInstance; - // } - - // protected void createFormContent(final IManagedForm mf) { - // ScrolledForm form = mf.getForm(); - // Composite body = form.getBody(); - // GridLayout mainLayout = new GridLayout(); - // body.setLayout(mainLayout); - // Group group = (Group) editor.getDisplayedUser(); - // appendOverviewPart(body, group); - // appendMembersPart(body, group); - // } - - @Override - protected void createUi(Composite parent) { - try { - groupsSession = repository.login(CmsConstants.SRV_WORKSPACE); - } catch (RepositoryException e) { - throw new JcrException("Cannot retrieve session", e); - } - // ScrolledForm form = mf.getForm(); - // Composite body = form.getBody(); - // Composite body = new Composite(parent, SWT.NONE); - Composite body = parent; - GridLayout mainLayout = new GridLayout(); - body.setLayout(mainLayout); - Group group = (Group) getDisplayedUser(); - appendOverviewPart(body, group); - appendMembersPart(body, group); - } - - @PreDestroy - public void dispose() { - JcrUtils.logoutQuietly(groupsSession); - super.dispose(); - } - - /** Creates the general section */ - protected void appendOverviewPart(final Composite parent, final Group group) { - Composite body = new Composite(parent, SWT.NONE); - // GridLayout layout = new GridLayout(5, false); - GridLayout layout = new GridLayout(2, false); - body.setLayout(layout); - body.setLayoutData(CmsSwtUtils.fillWidth()); - - String cn = UserAdminUtils.getProperty(group, LdapAttrs.cn.name()); - createReadOnlyLT(body, "Name", cn); - createReadOnlyLT(body, "DN", group.getName()); - createReadOnlyLT(body, "Domain", UserAdminUtils.getDomainName(group)); - - // Description - Label descLbl = new Label(body, SWT.LEAD); - descLbl.setFont(EclipseUiUtils.getBoldFont(body)); - descLbl.setText("Description"); - descLbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, true, false, 2, 1)); - final Text descTxt = new Text(body, SWT.LEAD | SWT.MULTI | SWT.WRAP | SWT.BORDER); - GridData gd = EclipseUiUtils.fillWidth(); - gd.heightHint = 50; - gd.horizontalSpan = 2; - descTxt.setLayoutData(gd); - - // Mark as workgroup - Link markAsWorkgroupLk = new Link(body, SWT.NONE); - markAsWorkgroupLk.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1)); - - // create form part (controller) - final AbstractFormPart part = new AbstractFormPart() { - - private MainInfoListener listener; - - @Override - public void initialize(IManagedForm form) { - super.initialize(form); - listener = new MainInfoListener(parent.getDisplay(), this); - userAdminWrapper.addListener(listener); - } - - @Override - public void dispose() { - userAdminWrapper.removeListener(listener); - super.dispose(); - } - - public void commit(boolean onSave) { - // group.getProperties().put(LdapAttrs.description.name(), descTxt.getText()); - setProperty(group, description, descTxt.getText()); - super.commit(onSave); - } - - @Override - public void refresh() { - // dnTxt.setText(group.getName()); - // cnTxt.setText(UserAdminUtils.getProperty(group, LdapAttrs.cn.name())); - descTxt.setText(UserAdminUtils.getProperty(group, LdapAttrs.description.name())); - Node workgroupHome = CmsJcrUtils.getGroupHome(groupsSession, cn); - if (workgroupHome == null) - markAsWorkgroupLk.setText("Mark as workgroup"); - else - markAsWorkgroupLk.setText("Configured as workgroup"); - parent.layout(true, true); - super.refresh(); - } - }; - - markAsWorkgroupLk.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = -6439340898096365078L; - - @Override - public void widgetSelected(SelectionEvent e) { - - boolean confirmed = MessageDialog.openConfirm(parent.getShell(), "Mark as workgroup", - "Are you sure you want to mark " + cn + " as being a workgroup? "); - if (confirmed) { - Node workgroupHome = CmsJcrUtils.getGroupHome(groupsSession, cn); - if (workgroupHome != null) - return; // already marked as workgroup, do nothing - else { - // improve transaction management - userAdminWrapper.beginTransactionIfNeeded(); - nodeInstance.createWorkgroup(group.getName()); - setProperty(group, businessCategory, WORKGROUP); - userAdminWrapper.commitOrNotifyTransactionStateChange(); - userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, group)); - part.refresh(); - } - } - } - }); - - ModifyListener defaultListener = new FormPartML(part); - descTxt.addModifyListener(defaultListener); - getManagedForm().addPart(part); - } - - /** Filtered table with members. Has drag and drop ability */ - protected void appendMembersPart(Composite parent, Group group) { - // Section section = tk.createSection(parent, Section.TITLE_BAR); - // section.setText("Members"); - // section.setLayoutData(EclipseUiUtils.fillAll()); - - Composite body = new Composite(parent, SWT.BORDER); - body.setLayout(new GridLayout()); - // section.setClient(body); - body.setLayoutData(EclipseUiUtils.fillAll()); - - // Define the displayed columns - List columnDefs = new ArrayList(); - columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 0, 24)); - columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Name", 150)); - columnDefs.add(new ColumnDefinition(new MailLP(), "Mail", 150)); - // columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", - // 240)); - - // Create and configure the table - LdifUsersTable userViewerCmp = new MyUserTableViewer(body, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL, - userAdminWrapper.getUserAdmin()); - - userViewerCmp.setColumnDefinitions(columnDefs); - userViewerCmp.populate(true, false); - userViewerCmp.setLayoutData(EclipseUiUtils.fillAll()); - - // Controllers - TableViewer userViewer = userViewerCmp.getTableViewer(); - userViewer.addDoubleClickListener(new UserTableDefaultDClickListener(partService)); - int operations = DND.DROP_COPY | DND.DROP_MOVE; - Transfer[] tt = new Transfer[] { TextTransfer.getInstance() }; - userViewer.addDropSupport(operations, tt, - new GroupDropListener(userAdminWrapper, userViewerCmp, (Group) getDisplayedUser())); - - AbstractFormPart part = new GroupMembersPart(userViewerCmp); - getManagedForm().addPart(part); - - // remove button - // addRemoveAbility(toolBarManager, userViewerCmp.getTableViewer(), group); - Action action = new RemoveMembershipAction(userViewer, group, "Remove selected items from this group", - SecurityAdminImages.ICON_REMOVE_DESC); - - ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT); - ToolBar toolBar = toolBarManager.createControl(body); - toolBar.setLayoutData(CmsSwtUtils.fillWidth()); - - toolBarManager.add(action); - toolBarManager.update(true); - - } - - // private LdifUsersTable createMemberPart(Composite parent, Group group) { - // - // // Define the displayed columns - // List columnDefs = new ArrayList(); - // columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 0, 24)); - // columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Name", 150)); - // columnDefs.add(new ColumnDefinition(new MailLP(), "Mail", 150)); - // // columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished - // Name", - // // 240)); - // - // // Create and configure the table - // LdifUsersTable userViewerCmp = new MyUserTableViewer(parent, SWT.MULTI | - // SWT.H_SCROLL | SWT.V_SCROLL, - // userAdminWrapper.getUserAdmin()); - // - // userViewerCmp.setColumnDefinitions(columnDefs); - // userViewerCmp.populate(true, false); - // userViewerCmp.setLayoutData(EclipseUiUtils.fillAll()); - // - // // Controllers - // TableViewer userViewer = userViewerCmp.getTableViewer(); - // userViewer.addDoubleClickListener(new - // UserTableDefaultDClickListener(partService)); - // int operations = DND.DROP_COPY | DND.DROP_MOVE; - // Transfer[] tt = new Transfer[] { TextTransfer.getInstance() }; - // userViewer.addDropSupport(operations, tt, - // new GroupDropListener(userAdminWrapper, userViewerCmp, (Group) - // getDisplayedUser())); - // - // // userViewerCmp.refresh(); - // return userViewerCmp; - // } - - // Local viewers - private class MyUserTableViewer extends LdifUsersTable { - private static final long serialVersionUID = 8467999509931900367L; - - private final UserFilter userFilter; - - public MyUserTableViewer(Composite parent, int style, UserAdmin userAdmin) { - super(parent, style, true); - userFilter = new UserFilter(); - - } - - @Override - protected List listFilteredElements(String filter) { - // reload user and set it in the editor - Group group = (Group) getDisplayedUser(); - Role[] roles = group.getMembers(); - List users = new ArrayList(); - userFilter.setSearchText(filter); - // userFilter.setShowSystemRole(true); - for (Role role : roles) - // if (role.getType() == Role.GROUP) - if (userFilter.select(null, null, role)) - users.add((User) role); - return users; - } - } - - // private void addRemoveAbility(ToolBarManager toolBarManager, TableViewer - // userViewer, Group group) { - // // Section section = sectionPart.getSection(); - // // ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT); - // // ToolBar toolbar = toolBarManager.createControl(parent); - // // ToolBar toolbar = toolBarManager.getControl(); - // // final Cursor handCursor = new Cursor(toolbar.getDisplay(), - // SWT.CURSOR_HAND); - // // toolbar.setCursor(handCursor); - // // toolbar.addDisposeListener(new DisposeListener() { - // // private static final long serialVersionUID = 3882131405820522925L; - // // - // // public void widgetDisposed(DisposeEvent e) { - // // if ((handCursor != null) && (handCursor.isDisposed() == false)) { - // // handCursor.dispose(); - // // } - // // } - // // }); - // - // Action action = new RemoveMembershipAction(userViewer, group, "Remove - // selected items from this group", - // SecurityAdminImages.ICON_REMOVE_DESC); - // toolBarManager.add(action); - // toolBarManager.update(true); - // // section.setTextClient(toolbar); - // } - - private class RemoveMembershipAction extends Action { - private static final long serialVersionUID = -1337713097184522588L; - - private final TableViewer userViewer; - private final Group group; - - RemoveMembershipAction(TableViewer userViewer, Group group, String name, ImageDescriptor img) { - super(name, img); - this.userViewer = userViewer; - this.group = group; - } - - @Override - public void run() { - ISelection selection = userViewer.getSelection(); - if (selection.isEmpty()) - return; - - @SuppressWarnings("unchecked") - Iterator it = ((IStructuredSelection) selection).iterator(); - List users = new ArrayList(); - while (it.hasNext()) { - User currUser = it.next(); - users.add(currUser); - } - - userAdminWrapper.beginTransactionIfNeeded(); - for (User user : users) { - group.removeMember(user); - } - userAdminWrapper.commitOrNotifyTransactionStateChange(); - userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, group)); - } - } - - // LOCAL CONTROLLERS - private class GroupMembersPart extends AbstractFormPart { - private final LdifUsersTable userViewer; - // private final Group group; - - private GroupChangeListener listener; - - public GroupMembersPart(LdifUsersTable userViewer) { - // super(section); - this.userViewer = userViewer; - // this.group = group; - } - - @Override - public void initialize(IManagedForm form) { - super.initialize(form); - listener = new GroupChangeListener(userViewer.getDisplay(), GroupMembersPart.this); - userAdminWrapper.addListener(listener); - } - - @Override - public void dispose() { - userAdminWrapper.removeListener(listener); - super.dispose(); - } - - @Override - public void refresh() { - userViewer.refresh(); - super.refresh(); - } - } - - /** - * Defines this table as being a potential target to add group membership - * (roles) to this group - */ - private class GroupDropListener extends ViewerDropAdapter { - private static final long serialVersionUID = 2893468717831451621L; - - private final UserAdminWrapper userAdminWrapper; - // private final LdifUsersTable myUserViewerCmp; - private final Group myGroup; - - public GroupDropListener(UserAdminWrapper userAdminWrapper, LdifUsersTable userTableViewerCmp, Group group) { - super(userTableViewerCmp.getTableViewer()); - this.userAdminWrapper = userAdminWrapper; - this.myGroup = group; - // this.myUserViewerCmp = userTableViewerCmp; - } - - @Override - public boolean validateDrop(Object target, int operation, TransferData transferType) { - // Target is always OK in a list only view - // TODO check if not a string - boolean validDrop = true; - return validDrop; - } - - @Override - public void drop(DropTargetEvent event) { - // TODO Is there an opportunity to perform the check before? - String newUserName = (String) event.data; - UserAdmin myUserAdmin = userAdminWrapper.getUserAdmin(); - Role role = myUserAdmin.getRole(newUserName); - if (role.getType() == Role.GROUP) { - Group newGroup = (Group) role; - Shell shell = getViewer().getControl().getShell(); - // Sanity checks - if (myGroup == newGroup) { // Equality - MessageDialog.openError(shell, "Forbidden addition ", "A group cannot be a member of itself."); - return; - } - - // Cycle - String myName = myGroup.getName(); - List myMemberships = getFlatGroups(myGroup); - if (myMemberships.contains(newGroup)) { - MessageDialog.openError(shell, "Forbidden addition: cycle", - "Cannot add " + newUserName + " to group " + myName + ". This would create a cycle"); - return; - } - - // Already member - List newGroupMemberships = getFlatGroups(newGroup); - if (newGroupMemberships.contains(myGroup)) { - MessageDialog.openError(shell, "Forbidden addition", - "Cannot add " + newUserName + " to group " + myName + ", this membership already exists"); - return; - } - userAdminWrapper.beginTransactionIfNeeded(); - myGroup.addMember(newGroup); - userAdminWrapper.commitOrNotifyTransactionStateChange(); - userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, myGroup)); - } else if (role.getType() == Role.USER) { - // TODO check if the group is already member of this group - WorkTransaction transaction = userAdminWrapper.beginTransactionIfNeeded(); - User user = (User) role; - myGroup.addMember(user); - if (UserAdminWrapper.COMMIT_ON_SAVE) - try { - transaction.commit(); - } catch (Exception e) { - throw new IllegalStateException( - "Cannot commit transaction " + "after user group membership update", e); - } - userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, myGroup)); - } - super.drop(event); - } - - @Override - public boolean performDrop(Object data) { - // myUserViewerCmp.refresh(); - return true; - } - } - - // LOCAL HELPERS - // private Composite addSection(FormToolkit tk, Composite parent) { - // Section section = tk.createSection(parent, SWT.NO_FOCUS); - // section.setLayoutData(EclipseUiUtils.fillWidth()); - // Composite body = tk.createComposite(section, SWT.WRAP); - // body.setLayoutData(EclipseUiUtils.fillAll()); - // section.setClient(body); - // return body; - // } - - /** Creates label and text. */ - // private Text createLT(Composite parent, String label, String value) { - // FormToolkit toolkit = getManagedForm().getToolkit(); - // Label lbl = toolkit.createLabel(parent, label); - // lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false)); - // lbl.setFont(EclipseUiUtils.getBoldFont(parent)); - // Text text = toolkit.createText(parent, value, SWT.BORDER); - // text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); - // CmsUiUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT); - // return text; - // } - // - // Text createReadOnlyLT(Composite parent, String label, String value) { - // FormToolkit toolkit = getManagedForm().getToolkit(); - // Label lbl = toolkit.createLabel(parent, label); - // lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false)); - // lbl.setFont(EclipseUiUtils.getBoldFont(parent)); - // Text text = toolkit.createText(parent, value, SWT.NONE); - // text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); - // text.setEditable(false); - // CmsUiUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT); - // return text; - // } - -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupsView.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupsView.java deleted file mode 100644 index 3bf48918d..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupsView.java +++ /dev/null @@ -1,251 +0,0 @@ -package org.argeo.cms.e4.users; - -import java.util.ArrayList; -import java.util.List; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import javax.inject.Inject; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.api.cms.CmsLog; -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.e4.users.providers.CommonNameLP; -import org.argeo.cms.e4.users.providers.DomainNameLP; -import org.argeo.cms.e4.users.providers.RoleIconLP; -import org.argeo.cms.e4.users.providers.UserDragListener; -import org.argeo.cms.swt.CmsException; -//import org.argeo.cms.ui.workbench.WorkbenchUiPlugin; -//import org.argeo.cms.ui.workbench.internal.useradmin.UiUserAdminListener; -//import org.argeo.cms.ui.workbench.internal.useradmin.UserAdminWrapper; -//import org.argeo.cms.ui.workbench.internal.useradmin.providers.CommonNameLP; -//import org.argeo.cms.ui.workbench.internal.useradmin.providers.DomainNameLP; -//import org.argeo.cms.ui.workbench.internal.useradmin.providers.RoleIconLP; -//import org.argeo.cms.ui.workbench.internal.useradmin.providers.UserDragListener; -//import org.argeo.cms.ui.workbench.internal.useradmin.providers.UserTableDefaultDClickListener; -import org.argeo.eclipse.ui.ColumnDefinition; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.parts.LdifUsersTable; -import org.argeo.util.naming.LdapAttrs; -import org.argeo.util.naming.LdapObjs; -import org.eclipse.e4.ui.di.Focus; -import org.eclipse.e4.ui.workbench.modeling.EPartService; -import org.eclipse.e4.ui.workbench.modeling.ESelectionService; -import org.eclipse.jface.viewers.ISelectionChangedListener; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.SelectionChangedEvent; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.dnd.DND; -import org.eclipse.swt.dnd.TextTransfer; -import org.eclipse.swt.dnd.Transfer; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -//import org.eclipse.ui.part.ViewPart; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdminEvent; -import org.osgi.service.useradmin.UserAdminListener; - -/** List all groups with filter */ -public class GroupsView { - private final static CmsLog log = CmsLog.getLog(GroupsView.class); - // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".groupsView"; - - @Inject - private EPartService partService; - @Inject - private UserAdminWrapper userAdminWrapper; - - // UI Objects - private LdifUsersTable groupTableViewerCmp; - private TableViewer userViewer; - private List columnDefs = new ArrayList(); - - private UserAdminListener listener; - - @PostConstruct - public void createPartControl(Composite parent, ESelectionService selectionService) { - parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); - - // boolean isAdmin = CurrentUser.isInRole(NodeConstants.ROLE_ADMIN); - - // Define the displayed columns - columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 19)); - columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Name", 150)); - columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 100)); - // Only show technical DN to admin - // if (isAdmin) - // columnDefs.add(new ColumnDefinition(new UserNameLP(), - // "Distinguished Name", 300)); - - // Create and configure the table - groupTableViewerCmp = new MyUserTableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); - - groupTableViewerCmp.setColumnDefinitions(columnDefs); - // if (isAdmin) - // groupTableViewerCmp.populateWithStaticFilters(false, false); - // else - groupTableViewerCmp.populate(true, false); - - groupTableViewerCmp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - - // Links - userViewer = groupTableViewerCmp.getTableViewer(); - userViewer.addDoubleClickListener(new UserTableDefaultDClickListener(partService)); - // getViewSite().setSelectionProvider(userViewer); - userViewer.addSelectionChangedListener(new ISelectionChangedListener() { - - @Override - public void selectionChanged(SelectionChangedEvent event) { - IStructuredSelection selection = (IStructuredSelection) event.getSelection(); - selectionService.setSelection(selection.toList()); - } - }); - - // Really? - groupTableViewerCmp.refresh(); - - // Drag and drop - int operations = DND.DROP_COPY | DND.DROP_MOVE; - Transfer[] tt = new Transfer[] { TextTransfer.getInstance() }; - userViewer.addDragSupport(operations, tt, new UserDragListener(userViewer)); - - // // Register a useradmin listener - // listener = new UserAdminListener() { - // @Override - // public void roleChanged(UserAdminEvent event) { - // if (userViewer != null && !userViewer.getTable().isDisposed()) - // refresh(); - // } - // }; - // userAdminWrapper.addListener(listener); - // } - - // Register a useradmin listener - listener = new MyUiUAListener(parent.getDisplay()); - userAdminWrapper.addListener(listener); - } - - private class MyUiUAListener extends UiUserAdminListener { - public MyUiUAListener(Display display) { - super(display); - } - - @Override - public void roleChangedToUiThread(UserAdminEvent event) { - if (userViewer != null && !userViewer.getTable().isDisposed()) - refresh(); - } - } - - private class MyUserTableViewer extends LdifUsersTable { - private static final long serialVersionUID = 8467999509931900367L; - - private boolean showSystemRoles = true; - - private final String[] knownProps = { LdapAttrs.uid.name(), LdapAttrs.cn.name(), LdapAttrs.DN }; - - public MyUserTableViewer(Composite parent, int style) { - super(parent, style); - showSystemRoles = CurrentUser.isInRole(CmsConstants.ROLE_ADMIN); - } - - protected void populateStaticFilters(Composite staticFilterCmp) { - staticFilterCmp.setLayout(new GridLayout()); - final Button showSystemRoleBtn = new Button(staticFilterCmp, SWT.CHECK); - showSystemRoleBtn.setText("Show system roles"); - showSystemRoles = CurrentUser.isInRole(CmsConstants.ROLE_ADMIN); - showSystemRoleBtn.setSelection(showSystemRoles); - - showSystemRoleBtn.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = -7033424592697691676L; - - @Override - public void widgetSelected(SelectionEvent e) { - showSystemRoles = showSystemRoleBtn.getSelection(); - refresh(); - } - - }); - } - - @Override - protected List listFilteredElements(String filter) { - Role[] roles; - try { - StringBuilder builder = new StringBuilder(); - StringBuilder tmpBuilder = new StringBuilder(); - if (EclipseUiUtils.notEmpty(filter)) - for (String prop : knownProps) { - tmpBuilder.append("("); - tmpBuilder.append(prop); - tmpBuilder.append("=*"); - tmpBuilder.append(filter); - tmpBuilder.append("*)"); - } - if (tmpBuilder.length() > 1) { - builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=") - .append(LdapObjs.groupOfNames.name()).append(")"); - // hide tokens - builder.append("(!(").append(LdapAttrs.DN).append("=*").append(CmsConstants.TOKENS_BASEDN) - .append("))"); - - if (!showSystemRoles) - builder.append("(!(").append(LdapAttrs.DN).append("=*").append(CmsConstants.ROLES_BASEDN) - .append("))"); - builder.append("(|"); - builder.append(tmpBuilder.toString()); - builder.append("))"); - } else { - if (!showSystemRoles) - builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=") - .append(LdapObjs.groupOfNames.name()).append(")(!(").append(LdapAttrs.DN).append("=*") - .append(CmsConstants.ROLES_BASEDN).append("))(!(").append(LdapAttrs.DN).append("=*") - .append(CmsConstants.TOKENS_BASEDN).append(")))"); - else - builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=") - .append(LdapObjs.groupOfNames.name()).append(")(!(").append(LdapAttrs.DN).append("=*") - .append(CmsConstants.TOKENS_BASEDN).append(")))"); - - } - roles = userAdminWrapper.getUserAdmin().getRoles(builder.toString()); - } catch (InvalidSyntaxException e) { - throw new CmsException("Unable to get roles with filter: " + filter, e); - } - List users = new ArrayList(); - for (Role role : roles) - if (!users.contains(role)) - users.add((User) role); - else - log.warn("Duplicated role: " + role); - - return users; - } - } - - public void refresh() { - groupTableViewerCmp.refresh(); - } - - @PreDestroy - public void dispose() { - userAdminWrapper.removeListener(listener); - } - - @Focus - public void setFocus() { - groupTableViewerCmp.setFocus(); - } - - /* DEPENDENCY INJECTION */ - public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { - this.userAdminWrapper = userAdminWrapper; - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/SecurityAdminImages.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/SecurityAdminImages.java deleted file mode 100644 index 7bbe3c727..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/SecurityAdminImages.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.argeo.cms.e4.users; - -import org.argeo.cms.ui.theme.CmsImages; -import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.swt.graphics.Image; - -/** Shared icons that must be declared programmatically . */ -public class SecurityAdminImages extends CmsImages { - private final static String PREFIX = "icons/"; - - public final static ImageDescriptor ICON_REMOVE_DESC = createDesc(PREFIX + "delete.png"); - public final static ImageDescriptor ICON_USER_DESC = createDesc(PREFIX + "person.png"); - - public final static Image ICON_USER = ICON_USER_DESC.createImage(); - public final static Image ICON_GROUP = createImg(PREFIX + "group.png"); - public final static Image ICON_WORKGROUP = createImg(PREFIX + "workgroup.png"); - public final static Image ICON_ROLE = createImg(PREFIX + "role.gif"); - -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UiAdminUtils.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UiAdminUtils.java deleted file mode 100644 index fb48a47c3..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UiAdminUtils.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.argeo.cms.e4.users; - -import org.argeo.util.transaction.WorkTransaction; - -/** First effort to centralize back end methods used by the user admin UI */ -public class UiAdminUtils { - /* - * INTERNAL METHODS: Below methods are meant to stay here and are not part - * of a potential generic backend to manage the useradmin - */ - /** Easily notify the ActiveWindow that the transaction had a state change */ - public final static void notifyTransactionStateChange( - WorkTransaction userTransaction) { -// try { -// IWorkbenchWindow aww = PlatformUI.getWorkbench() -// .getActiveWorkbenchWindow(); -// ISourceProviderService sourceProviderService = (ISourceProviderService) aww -// .getService(ISourceProviderService.class); -// UserTransactionProvider esp = (UserTransactionProvider) sourceProviderService -// .getSourceProvider(UserTransactionProvider.TRANSACTION_STATE); -// esp.fireTransactionStateChange(); -// } catch (Exception e) { -// throw new CmsException("Unable to begin transaction", e); -// } - } - - /** - * Email addresses must match this regexp pattern ({@value #EMAIL_PATTERN}. - * Thanks to this tip. - */ - public final static String EMAIL_PATTERN = "^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$"; -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UiUserAdminListener.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UiUserAdminListener.java deleted file mode 100644 index eb64aba0e..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UiUserAdminListener.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.argeo.cms.e4.users; - -import org.eclipse.swt.widgets.Display; -import org.osgi.service.useradmin.UserAdminEvent; -import org.osgi.service.useradmin.UserAdminListener; - -/** Convenience class to insure the call to refresh is done in the UI thread */ -public abstract class UiUserAdminListener implements UserAdminListener { - - private final Display display; - - public UiUserAdminListener(Display display) { - this.display = display; - } - - @Override - public void roleChanged(final UserAdminEvent event) { - display.asyncExec(new Runnable() { - @Override - public void run() { - roleChangedToUiThread(event); - } - }); - } - - public abstract void roleChangedToUiThread(UserAdminEvent event); -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java deleted file mode 100644 index d120ae9a2..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java +++ /dev/null @@ -1,153 +0,0 @@ -package org.argeo.cms.e4.users; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.cms.swt.CmsException; -import org.argeo.osgi.useradmin.UserDirectory; -import org.argeo.util.directory.DirectoryConf; -import org.argeo.util.transaction.WorkTransaction; -import org.osgi.service.useradmin.UserAdmin; -import org.osgi.service.useradmin.UserAdminEvent; -import org.osgi.service.useradmin.UserAdminListener; - -/** Centralise interaction with the UserAdmin in this bundle */ -public class UserAdminWrapper { - - private UserAdmin userAdmin; - // private ServiceReference userAdminServiceReference; -// private Set uris; - private Map> userDirectories = Collections - .synchronizedMap(new LinkedHashMap<>()); - private WorkTransaction userTransaction; - - // First effort to simplify UX while managing users and groups - public final static boolean COMMIT_ON_SAVE = true; - - // Registered listeners - List listeners = new ArrayList(); - - /** - * Starts a transaction if necessary. Should always been called together with - * {@link UserAdminWrapper#commitOrNotifyTransactionStateChange()} once the - * security model changes have been performed. - */ - public WorkTransaction beginTransactionIfNeeded() { - try { - // UserTransaction userTransaction = getUserTransaction(); - if (userTransaction.isNoTransactionStatus()) { - userTransaction.begin(); - // UiAdminUtils.notifyTransactionStateChange(userTransaction); - } - return userTransaction; - } catch (Exception e) { - throw new CmsException("Unable to begin transaction", e); - } - } - - /** - * Depending on the current application configuration, it will either commit the - * current transaction or throw a notification that the transaction state has - * changed (In the later case, it must be called from the UI thread). - */ - public void commitOrNotifyTransactionStateChange() { - try { - // UserTransaction userTransaction = getUserTransaction(); - if (userTransaction.isNoTransactionStatus()) - return; - - if (UserAdminWrapper.COMMIT_ON_SAVE) - userTransaction.commit(); - else - UiAdminUtils.notifyTransactionStateChange(userTransaction); - } catch (Exception e) { - throw new CmsException("Unable to clean transaction", e); - } - } - - // TODO implement safer mechanism - public void addListener(UserAdminListener userAdminListener) { - if (!listeners.contains(userAdminListener)) - listeners.add(userAdminListener); - } - - public void removeListener(UserAdminListener userAdminListener) { - if (listeners.contains(userAdminListener)) - listeners.remove(userAdminListener); - } - - public void notifyListeners(UserAdminEvent event) { - for (UserAdminListener listener : listeners) - listener.roleChanged(event); - } - - public Map getKnownBaseDns(boolean onlyWritable) { - Map dns = new HashMap(); - for (UserDirectory userDirectory : userDirectories.keySet()) { - Boolean readOnly = userDirectory.isReadOnly(); - String baseDn = userDirectory.getContext(); - - if (onlyWritable && readOnly) - continue; - if (baseDn.equalsIgnoreCase(CmsConstants.ROLES_BASEDN)) - continue; - if (baseDn.equalsIgnoreCase(CmsConstants.TOKENS_BASEDN)) - continue; - dns.put(baseDn, DirectoryConf.propertiesAsUri(userDirectories.get(userDirectory)).toString()); - - } -// for (String uri : uris) { -// if (!uri.startsWith("/")) -// continue; -// Dictionary props = UserAdminConf.uriAsProperties(uri); -// String readOnly = UserAdminConf.readOnly.getValue(props); -// String baseDn = UserAdminConf.baseDn.getValue(props); -// -// if (onlyWritable && "true".equals(readOnly)) -// continue; -// if (baseDn.equalsIgnoreCase(NodeConstants.ROLES_BASEDN)) -// continue; -// if (baseDn.equalsIgnoreCase(NodeConstants.TOKENS_BASEDN)) -// continue; -// dns.put(baseDn, uri); -// } - return dns; - } - - public UserAdmin getUserAdmin() { - return userAdmin; - } - - public WorkTransaction getUserTransaction() { - return userTransaction; - } - - /* DEPENDENCY INJECTION */ - public void setUserAdmin(UserAdmin userAdmin, Map properties) { - this.userAdmin = userAdmin; -// this.uris = Collections.unmodifiableSortedSet(new TreeSet<>(properties.keySet())); - } - - public void setUserTransaction(WorkTransaction userTransaction) { - this.userTransaction = userTransaction; - } - - public void addUserDirectory(UserDirectory userDirectory, Map properties) { - userDirectories.put(userDirectory, new Hashtable<>(properties)); - } - - public void removeUserDirectory(UserDirectory userDirectory, Map properties) { - userDirectories.remove(userDirectory); - } - - // public void setUserAdminServiceReference( - // ServiceReference userAdminServiceReference) { - // this.userAdminServiceReference = userAdminServiceReference; - // } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserBatchUpdateWizard.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserBatchUpdateWizard.java deleted file mode 100644 index 4fc59d30d..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserBatchUpdateWizard.java +++ /dev/null @@ -1,606 +0,0 @@ -package org.argeo.cms.e4.users; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.api.cms.CmsLog; -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.auth.UserAdminUtils; -import org.argeo.cms.e4.users.providers.CommonNameLP; -import org.argeo.cms.e4.users.providers.DomainNameLP; -import org.argeo.cms.e4.users.providers.MailLP; -import org.argeo.cms.e4.users.providers.UserNameLP; -import org.argeo.cms.swt.CmsException; -import org.argeo.eclipse.ui.ColumnDefinition; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.parts.LdifUsersTable; -import org.argeo.util.naming.LdapAttrs; -import org.argeo.util.naming.LdapObjs; -import org.argeo.util.transaction.WorkTransaction; -import org.eclipse.jface.dialogs.IPageChangeProvider; -import org.eclipse.jface.dialogs.IPageChangedListener; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.dialogs.PageChangedEvent; -import org.eclipse.jface.wizard.IWizardContainer; -import org.eclipse.jface.wizard.Wizard; -import org.eclipse.jface.wizard.WizardPage; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Combo; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Text; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdminEvent; - -/** Wizard to update users */ -public class UserBatchUpdateWizard extends Wizard { - - private final static CmsLog log = CmsLog.getLog(UserBatchUpdateWizard.class); - private UserAdminWrapper userAdminWrapper; - - // pages - private ChooseCommandWizardPage chooseCommandPage; - private ChooseUsersWizardPage userListPage; - private ValidateAndLaunchWizardPage validatePage; - - // Various implemented commands keys - private final static String CMD_UPDATE_PASSWORD = "resetPassword"; - private final static String CMD_UPDATE_EMAIL = "resetEmail"; - private final static String CMD_GROUP_MEMBERSHIP = "groupMembership"; - - private final Map commands = new HashMap() { - private static final long serialVersionUID = 1L; - { - put("Reset password(s)", CMD_UPDATE_PASSWORD); - put("Reset email(s)", CMD_UPDATE_EMAIL); - // TODO implement role / group management - // put("Add/Remove from group", CMD_GROUP_MEMBERSHIP); - } - }; - - public UserBatchUpdateWizard(UserAdminWrapper userAdminWrapper) { - this.userAdminWrapper = userAdminWrapper; - } - - @Override - public void addPages() { - chooseCommandPage = new ChooseCommandWizardPage(); - addPage(chooseCommandPage); - userListPage = new ChooseUsersWizardPage(); - addPage(userListPage); - validatePage = new ValidateAndLaunchWizardPage(); - addPage(validatePage); - } - - @Override - public boolean performFinish() { - if (!canFinish()) - return false; - WorkTransaction ut = userAdminWrapper.getUserTransaction(); - if (!ut.isNoTransactionStatus() && !MessageDialog.openConfirm(getShell(), "Existing Transaction", - "A user transaction is already existing, " + "are you sure you want to proceed ?")) - return false; - - // We cannot use jobs, user modifications are still meant to be done in - // the UIThread - // UpdateJob job = null; - // if (job != null) - // job.schedule(); - - if (CMD_UPDATE_PASSWORD.equals(chooseCommandPage.getCommand())) { - char[] newValue = chooseCommandPage.getPwdValue(); - if (newValue == null) - throw new CmsException("Password cannot be null or an empty string"); - ResetPassword job = new ResetPassword(userAdminWrapper, userListPage.getSelectedUsers(), newValue); - job.doUpdate(); - } else if (CMD_UPDATE_EMAIL.equals(chooseCommandPage.getCommand())) { - String newValue = chooseCommandPage.getEmailValue(); - if (newValue == null) - throw new CmsException("Password cannot be null or an empty string"); - ResetEmail job = new ResetEmail(userAdminWrapper, userListPage.getSelectedUsers(), newValue); - job.doUpdate(); - } - return true; - } - - public boolean canFinish() { - if (this.getContainer().getCurrentPage() == validatePage) - return true; - return false; - } - - private class ResetPassword { - private char[] newPwd; - private UserAdminWrapper userAdminWrapper; - private List usersToUpdate; - - public ResetPassword(UserAdminWrapper userAdminWrapper, List usersToUpdate, char[] newPwd) { - this.newPwd = newPwd; - this.usersToUpdate = usersToUpdate; - this.userAdminWrapper = userAdminWrapper; - } - - @SuppressWarnings("unchecked") - protected void doUpdate() { - userAdminWrapper.beginTransactionIfNeeded(); - try { - for (User user : usersToUpdate) { - // the char array is emptied after being used. - user.getCredentials().put(null, newPwd.clone()); - } - userAdminWrapper.commitOrNotifyTransactionStateChange(); - } catch (Exception e) { - throw new CmsException("Cannot perform batch update on users", e); - } finally { - WorkTransaction ut = userAdminWrapper.getUserTransaction(); - if (!ut.isNoTransactionStatus()) - ut.rollback(); - } - } - } - - private class ResetEmail { - private String newEmail; - private UserAdminWrapper userAdminWrapper; - private List usersToUpdate; - - public ResetEmail(UserAdminWrapper userAdminWrapper, List usersToUpdate, String newEmail) { - this.newEmail = newEmail; - this.usersToUpdate = usersToUpdate; - this.userAdminWrapper = userAdminWrapper; - } - - @SuppressWarnings("unchecked") - protected void doUpdate() { - userAdminWrapper.beginTransactionIfNeeded(); - try { - for (User user : usersToUpdate) { - // the char array is emptied after being used. - user.getProperties().put(LdapAttrs.mail.name(), newEmail); - } - - userAdminWrapper.commitOrNotifyTransactionStateChange(); - if (!usersToUpdate.isEmpty()) - userAdminWrapper.notifyListeners( - new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, usersToUpdate.get(0))); - } catch (Exception e) { - throw new CmsException("Cannot perform batch update on users", e); - } finally { - WorkTransaction ut = userAdminWrapper.getUserTransaction(); - if (!ut.isNoTransactionStatus()) - ut.rollback(); - } - } - } - - // @SuppressWarnings("unused") - // private class AddToGroup extends UpdateJob { - // private String groupID; - // private Session session; - // - // public AddToGroup(Session session, List nodesToUpdate, - // String groupID) { - // super(session, nodesToUpdate); - // this.session = session; - // this.groupID = groupID; - // } - // - // protected void doUpdate(Node node) { - // log.info("Add/Remove to group actions are not yet implemented"); - // // TODO implement this - // // try { - // // throw new CmsException("Not yet implemented"); - // // } catch (RepositoryException re) { - // // throw new CmsException( - // // "Unable to update boolean value for node " + node, re); - // // } - // } - // } - - // /** - // * Base privileged job that will be run asynchronously to perform the - // batch - // * update - // */ - // private abstract class UpdateJob extends PrivilegedJob { - // - // private final UserAdminWrapper userAdminWrapper; - // private final List usersToUpdate; - // - // protected abstract void doUpdate(User user); - // - // public UpdateJob(UserAdminWrapper userAdminWrapper, - // List usersToUpdate) { - // super("Perform update"); - // this.usersToUpdate = usersToUpdate; - // this.userAdminWrapper = userAdminWrapper; - // } - // - // @Override - // protected IStatus doRun(IProgressMonitor progressMonitor) { - // try { - // JcrMonitor monitor = new EclipseJcrMonitor(progressMonitor); - // int total = usersToUpdate.size(); - // monitor.beginTask("Performing change", total); - // userAdminWrapper.beginTransactionIfNeeded(); - // for (User user : usersToUpdate) { - // doUpdate(user); - // monitor.worked(1); - // } - // userAdminWrapper.getUserTransaction().commit(); - // } catch (Exception e) { - // throw new CmsException( - // "Cannot perform batch update on users", e); - // } finally { - // UserTransaction ut = userAdminWrapper.getUserTransaction(); - // try { - // if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION) - // ut.rollback(); - // } catch (IllegalStateException | SecurityException - // | SystemException e) { - // log.error("Unable to rollback session in 'finally', " - // + "the system might be in a dirty state"); - // e.printStackTrace(); - // } - // } - // return Status.OK_STATUS; - // } - // } - - // PAGES - /** - * Displays a combo box that enables user to choose which action to perform - */ - private class ChooseCommandWizardPage extends WizardPage { - private static final long serialVersionUID = -8069434295293996633L; - private Combo chooseCommandCmb; - private Button trueChk; - private Text valueTxt; - private Text pwdTxt; - private Text pwd2Txt; - - public ChooseCommandWizardPage() { - super("Choose a command to run."); - setTitle("Choose a command to run."); - } - - @Override - public void createControl(Composite parent) { - GridLayout gl = new GridLayout(); - Composite container = new Composite(parent, SWT.NO_FOCUS); - container.setLayout(gl); - - chooseCommandCmb = new Combo(container, SWT.READ_ONLY); - chooseCommandCmb.setLayoutData(EclipseUiUtils.fillWidth()); - String[] values = commands.keySet().toArray(new String[0]); - chooseCommandCmb.setItems(values); - - final Composite bottomPart = new Composite(container, SWT.NO_FOCUS); - bottomPart.setLayoutData(EclipseUiUtils.fillAll()); - bottomPart.setLayout(EclipseUiUtils.noSpaceGridLayout()); - - chooseCommandCmb.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = 1L; - - @Override - public void widgetSelected(SelectionEvent e) { - if (getCommand().equals(CMD_UPDATE_PASSWORD)) - populatePasswordCmp(bottomPart); - else if (getCommand().equals(CMD_UPDATE_EMAIL)) - populateEmailCmp(bottomPart); - else if (getCommand().equals(CMD_GROUP_MEMBERSHIP)) - populateGroupCmp(bottomPart); - else - populateBooleanFlagCmp(bottomPart); - checkPageComplete(); - bottomPart.layout(true, true); - } - }); - setControl(container); - } - - private void populateBooleanFlagCmp(Composite parent) { - EclipseUiUtils.clear(parent); - trueChk = new Button(parent, SWT.CHECK); - trueChk.setText("Do it. (It will to the contrary if unchecked)"); - trueChk.setSelection(true); - trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false)); - } - - private void populatePasswordCmp(Composite parent) { - EclipseUiUtils.clear(parent); - Composite body = new Composite(parent, SWT.NO_FOCUS); - - ModifyListener ml = new ModifyListener() { - private static final long serialVersionUID = -1558726363536729634L; - - @Override - public void modifyText(ModifyEvent event) { - checkPageComplete(); - } - }; - - body.setLayout(new GridLayout(2, false)); - body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - pwdTxt = EclipseUiUtils.createGridLP(body, "New password", ml); - pwd2Txt = EclipseUiUtils.createGridLP(body, "Repeat password", ml); - } - - private void populateEmailCmp(Composite parent) { - EclipseUiUtils.clear(parent); - Composite body = new Composite(parent, SWT.NO_FOCUS); - - ModifyListener ml = new ModifyListener() { - private static final long serialVersionUID = 2147704227294268317L; - - @Override - public void modifyText(ModifyEvent event) { - checkPageComplete(); - } - }; - - body.setLayout(new GridLayout(2, false)); - body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - valueTxt = EclipseUiUtils.createGridLT(body, "New e-mail", ml); - } - - private void checkPageComplete() { - String errorMsg = null; - if (chooseCommandCmb.getSelectionIndex() < 0) - errorMsg = "Please select an action"; - else if (CMD_UPDATE_EMAIL.equals(getCommand())) { - if (!valueTxt.getText().matches(UiAdminUtils.EMAIL_PATTERN)) - errorMsg = "Not a valid e-mail address"; - } else if (CMD_UPDATE_PASSWORD.equals(getCommand())) { - if (EclipseUiUtils.isEmpty(pwdTxt.getText()) || pwdTxt.getText().length() < 4) - errorMsg = "Please enter a password that is at least 4 character long"; - else if (!pwdTxt.getText().equals(pwd2Txt.getText())) - errorMsg = "Passwords are different"; - } - if (EclipseUiUtils.notEmpty(errorMsg)) { - setMessage(errorMsg, WizardPage.ERROR); - setPageComplete(false); - } else { - setMessage("Page complete, you can proceed to user choice", WizardPage.INFORMATION); - setPageComplete(true); - } - - getContainer().updateButtons(); - } - - private void populateGroupCmp(Composite parent) { - EclipseUiUtils.clear(parent); - trueChk = new Button(parent, SWT.CHECK); - trueChk.setText("Add to group. (It will remove user(s) from the " + "corresponding group if unchecked)"); - trueChk.setSelection(true); - trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false)); - } - - protected String getCommand() { - return commands.get(chooseCommandCmb.getItem(chooseCommandCmb.getSelectionIndex())); - } - - protected String getCommandLbl() { - return chooseCommandCmb.getItem(chooseCommandCmb.getSelectionIndex()); - } - - @SuppressWarnings("unused") - protected boolean getBoleanValue() { - // FIXME this is not consistent and will lead to errors. - if ("argeo:enabled".equals(getCommand())) - return trueChk.getSelection(); - else - return !trueChk.getSelection(); - } - - @SuppressWarnings("unused") - protected String getStringValue() { - String value = null; - if (valueTxt != null) { - value = valueTxt.getText(); - if ("".equals(value.trim())) - value = null; - } - return value; - } - - protected char[] getPwdValue() { - // We do not directly reset the password text fields: There is no - // need to over secure this process: setting a pwd to multi users - // at the same time is anyhow a bad practice and should be used only - // in test environment or for temporary access - if (pwdTxt == null || pwdTxt.isDisposed()) - return null; - else - return pwdTxt.getText().toCharArray(); - } - - protected String getEmailValue() { - // We do not directly reset the password text fields: There is no - // need to over secure this process: setting a pwd to multi users - // at the same time is anyhow a bad practice and should be used only - // in test environment or for temporary access - if (valueTxt == null || valueTxt.isDisposed()) - return null; - else - return valueTxt.getText(); - } - } - - /** - * Displays a list of users with a check box to be able to choose some of them - */ - private class ChooseUsersWizardPage extends WizardPage implements IPageChangedListener { - private static final long serialVersionUID = 7651807402211214274L; - private ChooseUserTableViewer userTableCmp; - - public ChooseUsersWizardPage() { - super("Choose Users"); - setTitle("Select users who will be impacted"); - } - - @Override - public void createControl(Composite parent) { - Composite pageCmp = new Composite(parent, SWT.NONE); - pageCmp.setLayout(EclipseUiUtils.noSpaceGridLayout()); - - // Define the displayed columns - List columnDefs = new ArrayList(); - columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name", 150)); - columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150)); - columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200)); - - // Only show technical DN to admin - if (CurrentUser.isInRole(CmsConstants.ROLE_ADMIN)) - columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", 300)); - - userTableCmp = new ChooseUserTableViewer(pageCmp, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); - userTableCmp.setLayoutData(EclipseUiUtils.fillAll()); - userTableCmp.setColumnDefinitions(columnDefs); - userTableCmp.populate(true, true); - userTableCmp.refresh(); - - setControl(pageCmp); - - // Add listener to update message when shown - final IWizardContainer wContainer = this.getContainer(); - if (wContainer instanceof IPageChangeProvider) { - ((IPageChangeProvider) wContainer).addPageChangedListener(this); - } - - } - - @Override - public void pageChanged(PageChangedEvent event) { - if (event.getSelectedPage() == this) { - String msg = "Chosen batch action: " + chooseCommandPage.getCommandLbl(); - ((WizardPage) event.getSelectedPage()).setMessage(msg); - } - } - - protected List getSelectedUsers() { - return userTableCmp.getSelectedUsers(); - } - - private class ChooseUserTableViewer extends LdifUsersTable { - private static final long serialVersionUID = 5080437561015853124L; - private final String[] knownProps = { LdapAttrs.uid.name(), LdapAttrs.DN, LdapAttrs.cn.name(), - LdapAttrs.givenName.name(), LdapAttrs.sn.name(), LdapAttrs.mail.name() }; - - public ChooseUserTableViewer(Composite parent, int style) { - super(parent, style); - } - - @Override - protected List listFilteredElements(String filter) { - Role[] roles; - - try { - StringBuilder builder = new StringBuilder(); - - StringBuilder tmpBuilder = new StringBuilder(); - if (EclipseUiUtils.notEmpty(filter)) - for (String prop : knownProps) { - tmpBuilder.append("("); - tmpBuilder.append(prop); - tmpBuilder.append("=*"); - tmpBuilder.append(filter); - tmpBuilder.append("*)"); - } - if (tmpBuilder.length() > 1) { - builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=") - .append(LdapObjs.inetOrgPerson.name()).append(")(|"); - builder.append(tmpBuilder.toString()); - builder.append("))"); - } else - builder.append("(").append(LdapAttrs.objectClass.name()).append("=") - .append(LdapObjs.inetOrgPerson.name()).append(")"); - roles = userAdminWrapper.getUserAdmin().getRoles(builder.toString()); - } catch (InvalidSyntaxException e) { - throw new CmsException("Unable to get roles with filter: " + filter, e); - } - List users = new ArrayList(); - for (Role role : roles) - // Prevent current logged in user to perform batch on - // himself - if (!UserAdminUtils.isCurrentUser((User) role)) - users.add((User) role); - return users; - } - } - } - - /** Summary of input data before launching the process */ - private class ValidateAndLaunchWizardPage extends WizardPage implements IPageChangedListener { - private static final long serialVersionUID = 7098918351451743853L; - private ChosenUsersTableViewer userTableCmp; - - public ValidateAndLaunchWizardPage() { - super("Validate and launch"); - setTitle("Validate and launch"); - } - - @Override - public void createControl(Composite parent) { - Composite pageCmp = new Composite(parent, SWT.NO_FOCUS); - pageCmp.setLayout(EclipseUiUtils.noSpaceGridLayout()); - - List columnDefs = new ArrayList(); - columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name", 150)); - columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150)); - columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200)); - // Only show technical DN to admin - if (CurrentUser.isInRole(CmsConstants.ROLE_ADMIN)) - columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", 300)); - userTableCmp = new ChosenUsersTableViewer(pageCmp, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); - userTableCmp.setLayoutData(EclipseUiUtils.fillAll()); - userTableCmp.setColumnDefinitions(columnDefs); - userTableCmp.populate(false, false); - userTableCmp.refresh(); - setControl(pageCmp); - // Add listener to update message when shown - final IWizardContainer wContainer = this.getContainer(); - if (wContainer instanceof IPageChangeProvider) { - ((IPageChangeProvider) wContainer).addPageChangedListener(this); - } - } - - @Override - public void pageChanged(PageChangedEvent event) { - if (event.getSelectedPage() == this) { - @SuppressWarnings({ "unchecked", "rawtypes" }) - Object[] values = ((ArrayList) userListPage.getSelectedUsers()) - .toArray(new Object[userListPage.getSelectedUsers().size()]); - userTableCmp.getTableViewer().setInput(values); - String msg = "Following batch action: [" + chooseCommandPage.getCommandLbl() - + "] will be perfomed on the users listed below.\n"; - // + "Are you sure you want to proceed?"; - setMessage(msg); - } - } - - private class ChosenUsersTableViewer extends LdifUsersTable { - private static final long serialVersionUID = 7814764735794270541L; - - public ChosenUsersTableViewer(Composite parent, int style) { - super(parent, style); - } - - @Override - protected List listFilteredElements(String filter) { - return userListPage.getSelectedUsers(); - } - } - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserEditor.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserEditor.java deleted file mode 100644 index 66f442082..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserEditor.java +++ /dev/null @@ -1,535 +0,0 @@ -package org.argeo.cms.e4.users; - -import static org.argeo.cms.auth.UserAdminUtils.getProperty; -import static org.argeo.util.naming.LdapAttrs.cn; -import static org.argeo.util.naming.LdapAttrs.givenName; -import static org.argeo.util.naming.LdapAttrs.mail; -import static org.argeo.util.naming.LdapAttrs.sn; -import static org.argeo.util.naming.LdapAttrs.uid; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import javax.inject.Inject; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.auth.UserAdminUtils; -import org.argeo.cms.e4.users.providers.CommonNameLP; -import org.argeo.cms.e4.users.providers.DomainNameLP; -import org.argeo.cms.e4.users.providers.RoleIconLP; -import org.argeo.cms.e4.users.providers.UserFilter; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ui.eclipse.forms.AbstractFormPart; -//import org.argeo.cms.ui.eclipse.forms.FormToolkit; -import org.argeo.cms.ui.eclipse.forms.IManagedForm; -import org.argeo.eclipse.ui.ColumnDefinition; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.parts.LdifUsersTable; -import org.argeo.util.naming.LdapAttrs; -import org.eclipse.e4.ui.workbench.modeling.EPartService; -import org.eclipse.jface.action.Action; -import org.eclipse.jface.action.ToolBarManager; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.dialogs.TrayDialog; -import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.jface.viewers.ViewerDropAdapter; -import org.eclipse.swt.SWT; -import org.eclipse.swt.dnd.DND; -import org.eclipse.swt.dnd.DropTargetEvent; -import org.eclipse.swt.dnd.TextTransfer; -import org.eclipse.swt.dnd.Transfer; -import org.eclipse.swt.dnd.TransferData; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Link; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Text; -import org.eclipse.swt.widgets.ToolBar; -import org.osgi.service.useradmin.Group; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdmin; -import org.osgi.service.useradmin.UserAdminEvent; - -/** Display/edit the properties of a given user */ -public class UserEditor extends AbstractRoleEditor { - // final static String ID = "UserEditor.mainPage"; - - @Inject - private EPartService partService; - - // private final UserEditor editor; - // private UserAdminWrapper userAdminWrapper; - - // Local configuration - // private final int PRE_TITLE_INDENT = 10; - - // public UserMainPage(FormEditor editor, UserAdminWrapper userAdminWrapper) { - // super(editor, ID, "Main"); - // this.editor = (UserEditor) editor; - // this.userAdminWrapper = userAdminWrapper; - // } - - // protected void createFormContent(final IManagedForm mf) { - // ScrolledForm form = mf.getForm(); - // Composite body = form.getBody(); - // GridLayout mainLayout = new GridLayout(); - // // mainLayout.marginRight = 10; - // body.setLayout(mainLayout); - // User user = editor.getDisplayedUser(); - // appendOverviewPart(body, user); - // // Remove to ability to force the password for his own user. The user - // // must then use the change pwd feature - // appendMemberOfPart(body, user); - // } - - @Override - protected void createUi(Composite body) { - // Composite body = new Composite(parent, SWT.BORDER); - GridLayout mainLayout = new GridLayout(); - // mainLayout.marginRight = 10; - body.setLayout(mainLayout); - // body.getParent().setLayout(new GridLayout()); - // body.setLayoutData(CmsUiUtils.fillAll()); - User user = getDisplayedUser(); - appendOverviewPart(body, user); - // Remove to ability to force the password for his own user. The user - // must then use the change pwd feature - appendMemberOfPart(body, user); - } - - /** Creates the general section */ - private void appendOverviewPart(final Composite parent, final User user) { - // FormToolkit tk = getManagedForm().getToolkit(); - - // Section section = tk.createSection(parent, SWT.NO_FOCUS); - // GridData gd = EclipseUiUtils.fillWidth(); - // // gd.verticalAlignment = PRE_TITLE_INDENT; - // section.setLayoutData(gd); - Composite body = new Composite(parent, SWT.NONE); - body.setLayoutData(EclipseUiUtils.fillWidth()); - // section.setClient(body); - // body.setLayout(new GridLayout(6, false)); - body.setLayout(new GridLayout(2, false)); - - Text commonName = createReadOnlyLT(body, "Name", getProperty(user, cn)); - Text distinguishedName = createReadOnlyLT(body, "Login", getProperty(user, uid)); - Text firstName = createLT(body, "First name", getProperty(user, givenName)); - Text lastName = createLT(body, "Last name", getProperty(user, sn)); - Text email = createLT(body, "Email", getProperty(user, mail)); - - Link resetPwdLk = new Link(body, SWT.NONE); - if (!UserAdminUtils.isCurrentUser(user)) { - resetPwdLk.setText("Reset password"); - } - resetPwdLk.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1)); - - // create form part (controller) - AbstractFormPart part = new AbstractFormPart() { - private MainInfoListener listener; - - @Override - public void initialize(IManagedForm form) { - super.initialize(form); - listener = new MainInfoListener(parent.getDisplay(), this); - userAdminWrapper.addListener(listener); - } - - @Override - public void dispose() { - userAdminWrapper.removeListener(listener); - super.dispose(); - } - - @SuppressWarnings("unchecked") - public void commit(boolean onSave) { - // TODO Sanity checks (mail validity...) - user.getProperties().put(LdapAttrs.givenName.name(), firstName.getText()); - user.getProperties().put(LdapAttrs.sn.name(), lastName.getText()); - user.getProperties().put(LdapAttrs.cn.name(), commonName.getText()); - user.getProperties().put(LdapAttrs.mail.name(), email.getText()); - super.commit(onSave); - } - - @Override - public void refresh() { - distinguishedName.setText(UserAdminUtils.getProperty(user, LdapAttrs.uid.name())); - commonName.setText(UserAdminUtils.getProperty(user, LdapAttrs.cn.name())); - firstName.setText(UserAdminUtils.getProperty(user, LdapAttrs.givenName.name())); - lastName.setText(UserAdminUtils.getProperty(user, LdapAttrs.sn.name())); - email.setText(UserAdminUtils.getProperty(user, LdapAttrs.mail.name())); - refreshFormTitle(user); - super.refresh(); - } - }; - - // Improve this: automatically generate CN when first or last name - // changes - ModifyListener cnML = new ModifyListener() { - private static final long serialVersionUID = 4298649222869835486L; - - @Override - public void modifyText(ModifyEvent event) { - String first = firstName.getText(); - String last = lastName.getText(); - String cn = first.trim() + " " + last.trim() + " "; - cn = cn.trim(); - commonName.setText(cn); - // getManagedForm().getForm().setText(cn); - updateEditorTitle(cn); - } - }; - firstName.addModifyListener(cnML); - lastName.addModifyListener(cnML); - - ModifyListener defaultListener = new FormPartML(part); - firstName.addModifyListener(defaultListener); - lastName.addModifyListener(defaultListener); - email.addModifyListener(defaultListener); - - if (!UserAdminUtils.isCurrentUser(user)) - resetPwdLk.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = 5881800534589073787L; - - @Override - public void widgetSelected(SelectionEvent e) { - new ChangePasswordDialog(user, "Reset password").open(); - } - }); - - getManagedForm().addPart(part); - } - - private class ChangePasswordDialog extends TrayDialog { - private static final long serialVersionUID = 2843538207460082349L; - - private User user; - private Text password1; - private Text password2; - private String title; - // private FormToolkit tk; - - public ChangePasswordDialog(User user, String title) { - super(Display.getDefault().getActiveShell()); - // this.tk = tk; - this.user = user; - this.title = title; - } - - protected Control createDialogArea(Composite parent) { - Composite dialogarea = (Composite) super.createDialogArea(parent); - dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - Composite body = new Composite(dialogarea, SWT.NO_FOCUS); - body.setLayoutData(EclipseUiUtils.fillAll()); - GridLayout layout = new GridLayout(2, false); - body.setLayout(layout); - - password1 = createLP(body, "New password", ""); - password2 = createLP(body, "Repeat password", ""); - parent.pack(); - return body; - } - - @SuppressWarnings("unchecked") - @Override - protected void okPressed() { - String msg = null; - - if (password1.getText().equals("")) - msg = "Password cannot be empty"; - else if (password1.getText().equals(password2.getText())) { - char[] newPassword = password1.getText().toCharArray(); - // userAdminWrapper.beginTransactionIfNeeded(); - userAdminWrapper.beginTransactionIfNeeded(); - user.getCredentials().put(null, newPassword); - userAdminWrapper.commitOrNotifyTransactionStateChange(); - super.okPressed(); - } else { - msg = "Passwords are not equals"; - } - - if (EclipseUiUtils.notEmpty(msg)) - MessageDialog.openError(getParentShell(), "Cannot reset pasword", msg); - } - - protected void configureShell(Shell shell) { - super.configureShell(shell); - shell.setText(title); - } - } - - private LdifUsersTable appendMemberOfPart(final Composite parent, User user) { - // Section section = addSection(tk, parent, "Roles"); - // Composite body = (Composite) section.getClient(); - // Composite body= parent; - Composite body = new Composite(parent, SWT.BORDER); - body.setLayout(new GridLayout()); - body.setLayoutData(CmsSwtUtils.fillAll()); - - // boolean isAdmin = CurrentUser.isInRole(NodeConstants.ROLE_ADMIN); - - // Displayed columns - List columnDefs = new ArrayList(); - columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 0, 24)); - columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Name", 150)); - columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 100)); - // Only show technical DN to administrators - // if (isAdmin) - // columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", - // 300)); - - // Create and configure the table - final LdifUsersTable userViewerCmp = new MyUserTableViewer(body, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL, user); - - userViewerCmp.setColumnDefinitions(columnDefs); - // if (isAdmin) - // userViewerCmp.populateWithStaticFilters(false, false); - // else - userViewerCmp.populate(true, false); - GridData gd = EclipseUiUtils.fillAll(); - gd.heightHint = 500; - userViewerCmp.setLayoutData(gd); - - // Controllers - TableViewer userViewer = userViewerCmp.getTableViewer(); - userViewer.addDoubleClickListener(new UserTableDefaultDClickListener(partService)); - int operations = DND.DROP_COPY | DND.DROP_MOVE; - Transfer[] tt = new Transfer[] { TextTransfer.getInstance() }; - GroupDropListener dropL = new GroupDropListener(userAdminWrapper, userViewer, user); - userViewer.addDropSupport(operations, tt, dropL); - - AbstractFormPart part = new AbstractFormPart() { - - private GroupChangeListener listener; - - @Override - public void initialize(IManagedForm form) { - super.initialize(form); - listener = new GroupChangeListener(parent.getDisplay(), this); - userAdminWrapper.addListener(listener); - } - - public void commit(boolean onSave) { - super.commit(onSave); - } - - @Override - public void dispose() { - userAdminWrapper.removeListener(listener); - super.dispose(); - } - - @Override - public void refresh() { - userViewerCmp.refresh(); - super.refresh(); - } - }; - getManagedForm().addPart(part); - // addRemoveAbitily(body, userViewer, user); - // userViewerCmp.refresh(); - String tooltip = "Remove " + UserAdminUtils.getUserLocalId(user.getName()) + " from the below selected groups"; - Action action = new RemoveMembershipAction(userViewer, user, tooltip, SecurityAdminImages.ICON_REMOVE_DESC); - ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT); - ToolBar toolBar = toolBarManager.createControl(body); - toolBar.setLayoutData(CmsSwtUtils.fillWidth()); - toolBarManager.add(action); - toolBarManager.update(true); - return userViewerCmp; - } - - private class MyUserTableViewer extends LdifUsersTable { - private static final long serialVersionUID = 2653790051461237329L; - - private Button showSystemRoleBtn; - - private final User user; - private final UserFilter userFilter; - - public MyUserTableViewer(Composite parent, int style, User user) { - super(parent, style, true); - this.user = user; - userFilter = new UserFilter(); - } - - protected void populateStaticFilters(Composite staticFilterCmp) { - staticFilterCmp.setLayout(new GridLayout()); - showSystemRoleBtn = new Button(staticFilterCmp, SWT.CHECK); - showSystemRoleBtn.setText("Show system roles"); - boolean showSysRole = CurrentUser.isInRole(CmsConstants.ROLE_ADMIN); - showSystemRoleBtn.setSelection(showSysRole); - userFilter.setShowSystemRole(showSysRole); - showSystemRoleBtn.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = -7033424592697691676L; - - @Override - public void widgetSelected(SelectionEvent e) { - userFilter.setShowSystemRole(showSystemRoleBtn.getSelection()); - refresh(); - } - }); - } - - @Override - protected List listFilteredElements(String filter) { - List users = (List) getFlatGroups(null); - List filteredUsers = new ArrayList(); - if (users.contains(user)) - users.remove(user); - userFilter.setSearchText(filter); - for (User user : users) - if (userFilter.select(null, null, user)) - filteredUsers.add(user); - return filteredUsers; - } - } - - // private void addRemoveAbility(Composite parent, TableViewer userViewer, User - // user) { - // // Section section = sectionPart.getSection(); - // ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT); - // ToolBar toolbar = toolBarManager.createControl(parent); - // final Cursor handCursor = new Cursor(Display.getCurrent(), SWT.CURSOR_HAND); - // toolbar.setCursor(handCursor); - // toolbar.addDisposeListener(new DisposeListener() { - // private static final long serialVersionUID = 3882131405820522925L; - // - // public void widgetDisposed(DisposeEvent e) { - // if ((handCursor != null) && (handCursor.isDisposed() == false)) { - // handCursor.dispose(); - // } - // } - // }); - // - // String tooltip = "Remove " + UserAdminUtils.getUserLocalId(user.getName()) + - // " from the below selected groups"; - // Action action = new RemoveMembershipAction(userViewer, user, tooltip, - // SecurityAdminImages.ICON_REMOVE_DESC); - // toolBarManager.add(action); - // toolBarManager.update(true); - // // section.setTextClient(toolbar); - // } - - private class RemoveMembershipAction extends Action { - private static final long serialVersionUID = -1337713097184522588L; - - private final TableViewer userViewer; - private final User user; - - RemoveMembershipAction(TableViewer userViewer, User user, String name, ImageDescriptor img) { - super(name, img); - this.userViewer = userViewer; - this.user = user; - } - - @Override - public void run() { - ISelection selection = userViewer.getSelection(); - if (selection.isEmpty()) - return; - - @SuppressWarnings("unchecked") - Iterator it = ((IStructuredSelection) selection).iterator(); - List groups = new ArrayList(); - while (it.hasNext()) { - Group currGroup = it.next(); - groups.add(currGroup); - } - - userAdminWrapper.beginTransactionIfNeeded(); - for (Group group : groups) { - group.removeMember(user); - } - userAdminWrapper.commitOrNotifyTransactionStateChange(); - for (Group group : groups) { - userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, group)); - } - } - } - - /** - * Defines the table as being a potential target to add group memberships - * (roles) to this user - */ - private class GroupDropListener extends ViewerDropAdapter { - private static final long serialVersionUID = 2893468717831451621L; - - private final UserAdminWrapper myUserAdminWrapper; - private final User myUser; - - public GroupDropListener(UserAdminWrapper userAdminWrapper, Viewer userViewer, User user) { - super(userViewer); - this.myUserAdminWrapper = userAdminWrapper; - this.myUser = user; - } - - @Override - public boolean validateDrop(Object target, int operation, TransferData transferType) { - // Target is always OK in a list only view - // TODO check if not a string - boolean validDrop = true; - return validDrop; - } - - @Override - public void drop(DropTargetEvent event) { - String name = (String) event.data; - UserAdmin myUserAdmin = myUserAdminWrapper.getUserAdmin(); - Role role = myUserAdmin.getRole(name); - // TODO this check should be done before. - if (role.getType() == Role.GROUP) { - // TODO check if the user is already member of this group - - myUserAdminWrapper.beginTransactionIfNeeded(); - Group group = (Group) role; - group.addMember(myUser); - userAdminWrapper.commitOrNotifyTransactionStateChange(); - myUserAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, group)); - } - super.drop(event); - } - - @Override - public boolean performDrop(Object data) { - // userTableViewerCmp.refresh(); - return true; - } - } - - // LOCAL HELPERS - private void refreshFormTitle(User group) { - // getManagedForm().getForm().setText(UserAdminUtils.getProperty(group, - // LdapAttrs.cn.name())); - } - - /** Appends a section with a title */ - // private Section addSection(FormToolkit tk, Composite parent, String title) { - // Section section = tk.createSection(parent, Section.TITLE_BAR); - // GridData gd = EclipseUiUtils.fillWidth(); - // gd.verticalAlignment = PRE_TITLE_INDENT; - // section.setLayoutData(gd); - // section.setText(title); - // // section.getMenu().setVisible(true); - // - // Composite body = tk.createComposite(section, SWT.WRAP); - // body.setLayoutData(EclipseUiUtils.fillAll()); - // section.setClient(body); - // - // return section; - // } - -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserTableDefaultDClickListener.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserTableDefaultDClickListener.java deleted file mode 100644 index c6d024ebc..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserTableDefaultDClickListener.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.argeo.cms.e4.users; - -import org.argeo.cms.e4.CmsE4Utils; -import org.argeo.util.naming.LdapAttrs; -import org.eclipse.e4.ui.workbench.modeling.EPartService; -import org.eclipse.jface.viewers.DoubleClickEvent; -import org.eclipse.jface.viewers.IDoubleClickListener; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.osgi.service.useradmin.Group; -import org.osgi.service.useradmin.User; - -/** - * Default double click listener for the various user tables, will open the - * clicked item in the editor - */ -public class UserTableDefaultDClickListener implements IDoubleClickListener { - private final EPartService partService; - - public UserTableDefaultDClickListener(EPartService partService) { - this.partService = partService; - } - - public void doubleClick(DoubleClickEvent evt) { - if (evt.getSelection().isEmpty()) - return; - Object obj = ((IStructuredSelection) evt.getSelection()).getFirstElement(); - User user = (User) obj; - - String editorId = getEditorId(user); - CmsE4Utils.openEditor(partService, editorId, LdapAttrs.uid.name(), user.getName()); - } - - protected String getEditorId(User user) { - if (user instanceof Group) - return "org.argeo.cms.e4.partdescriptor.groupEditor"; - else - return "org.argeo.cms.e4.partdescriptor.userEditor"; - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UsersView.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UsersView.java deleted file mode 100644 index 720945c4c..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UsersView.java +++ /dev/null @@ -1,182 +0,0 @@ -package org.argeo.cms.e4.users; - -import java.util.ArrayList; -import java.util.List; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import javax.inject.Inject; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.e4.users.providers.CommonNameLP; -import org.argeo.cms.e4.users.providers.DomainNameLP; -import org.argeo.cms.e4.users.providers.MailLP; -import org.argeo.cms.e4.users.providers.UserDragListener; -import org.argeo.cms.e4.users.providers.UserNameLP; -import org.argeo.cms.swt.CmsException; -import org.argeo.eclipse.ui.ColumnDefinition; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.parts.LdifUsersTable; -import org.argeo.util.naming.LdapAttrs; -import org.argeo.util.naming.LdapObjs; -import org.eclipse.e4.ui.di.Focus; -import org.eclipse.e4.ui.workbench.modeling.EPartService; -import org.eclipse.e4.ui.workbench.modeling.ESelectionService; -import org.eclipse.jface.viewers.ISelectionChangedListener; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.SelectionChangedEvent; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.dnd.DND; -import org.eclipse.swt.dnd.TextTransfer; -import org.eclipse.swt.dnd.Transfer; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdminEvent; -import org.osgi.service.useradmin.UserAdminListener; - -/** List all users with filter - based on Ldif userAdmin */ -public class UsersView { - // private final static Log log = LogFactory.getLog(UsersView.class); - - // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".usersView"; - - @Inject - private UserAdminWrapper userAdminWrapper; - @Inject - private EPartService partService; - - // UI Objects - private LdifUsersTable userTableViewerCmp; - private TableViewer userViewer; - private List columnDefs = new ArrayList(); - - private UserAdminListener listener; - - @PostConstruct - public void createPartControl(Composite parent, ESelectionService selectionService) { - - parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); - // Define the displayed columns - columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name", 150)); - columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150)); - columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200)); - // Only show technical DN to admin - if (CurrentUser.isInRole(CmsConstants.ROLE_ADMIN)) - columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", 300)); - - // Create and configure the table - userTableViewerCmp = new MyUserTableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); - userTableViewerCmp.setLayoutData(EclipseUiUtils.fillAll()); - userTableViewerCmp.setColumnDefinitions(columnDefs); - userTableViewerCmp.populate(true, false); - - // Links - userViewer = userTableViewerCmp.getTableViewer(); - userViewer.addDoubleClickListener(new UserTableDefaultDClickListener(partService)); - userViewer.addSelectionChangedListener(new ISelectionChangedListener() { - - @Override - public void selectionChanged(SelectionChangedEvent event) { - IStructuredSelection selection = (IStructuredSelection) event.getSelection(); - selectionService.setSelection(selection.toList()); - } - }); - // getViewSite().setSelectionProvider(userViewer); - - // Really? - userTableViewerCmp.refresh(); - - // Drag and drop - int operations = DND.DROP_COPY | DND.DROP_MOVE; - Transfer[] tt = new Transfer[] { TextTransfer.getInstance() }; - userViewer.addDragSupport(operations, tt, new UserDragListener(userViewer)); - - // Register a useradmin listener - listener = new MyUiUAListener(parent.getDisplay()); - userAdminWrapper.addListener(listener); - } - - private class MyUiUAListener extends UiUserAdminListener { - public MyUiUAListener(Display display) { - super(display); - } - - @Override - public void roleChangedToUiThread(UserAdminEvent event) { - if (userViewer != null && !userViewer.getTable().isDisposed()) - refresh(); - } - } - - private class MyUserTableViewer extends LdifUsersTable { - private static final long serialVersionUID = 8467999509931900367L; - - private final String[] knownProps = { LdapAttrs.DN, LdapAttrs.uid.name(), LdapAttrs.cn.name(), - LdapAttrs.givenName.name(), LdapAttrs.sn.name(), LdapAttrs.mail.name() }; - - public MyUserTableViewer(Composite parent, int style) { - super(parent, style); - } - - @Override - protected List listFilteredElements(String filter) { - Role[] roles; - - try { - StringBuilder builder = new StringBuilder(); - - StringBuilder tmpBuilder = new StringBuilder(); - if (EclipseUiUtils.notEmpty(filter)) - for (String prop : knownProps) { - tmpBuilder.append("("); - tmpBuilder.append(prop); - tmpBuilder.append("=*"); - tmpBuilder.append(filter); - tmpBuilder.append("*)"); - } - if (tmpBuilder.length() > 1) { - builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=") - .append(LdapObjs.inetOrgPerson.name()).append(")(|"); - builder.append(tmpBuilder.toString()); - builder.append("))"); - } else - builder.append("(").append(LdapAttrs.objectClass.name()).append("=") - .append(LdapObjs.inetOrgPerson.name()).append(")"); - roles = userAdminWrapper.getUserAdmin().getRoles(builder.toString()); - } catch (InvalidSyntaxException e) { - throw new CmsException("Unable to get roles with filter: " + filter, e); - } - List users = new ArrayList(); - for (Role role : roles) - // if (role.getType() == Role.USER && role.getType() != - // Role.GROUP) - users.add((User) role); - return users; - } - } - - public void refresh() { - userTableViewerCmp.refresh(); - } - - // Override generic view methods - @PreDestroy - public void dispose() { - userAdminWrapper.removeListener(listener); - } - - @Focus - public void setFocus() { - userTableViewerCmp.setFocus(); - } - - /* DEPENDENCY INJECTION */ - public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { - this.userAdminWrapper = userAdminWrapper; - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/DeleteGroups.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/DeleteGroups.java deleted file mode 100644 index 742bc3f5f..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/DeleteGroups.java +++ /dev/null @@ -1,95 +0,0 @@ -package org.argeo.cms.e4.users.handlers; - -import java.util.List; - -import javax.inject.Inject; -import javax.inject.Named; - -import org.argeo.cms.auth.UserAdminUtils; -import org.argeo.cms.e4.users.GroupsView; -import org.argeo.cms.e4.users.UserAdminWrapper; -import org.eclipse.e4.core.di.annotations.CanExecute; -import org.eclipse.e4.core.di.annotations.Execute; -import org.eclipse.e4.ui.model.application.ui.basic.MPart; -import org.eclipse.e4.ui.services.IServiceConstants; -import org.eclipse.e4.ui.workbench.modeling.ESelectionService; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.swt.widgets.Display; -import org.osgi.service.useradmin.Group; -import org.osgi.service.useradmin.UserAdmin; -import org.osgi.service.useradmin.UserAdminEvent; - -/** Delete the selected groups */ -public class DeleteGroups { - // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + - // ".deleteGroups"; - - /* DEPENDENCY INJECTION */ - @Inject - private UserAdminWrapper userAdminWrapper; - - @Inject - ESelectionService selectionService; - - @SuppressWarnings("unchecked") - @Execute - public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) { - // ISelection selection = null;// HandlerUtil.getCurrentSelection(event); - // if (selection.isEmpty()) - // return null; - // - // List groups = new ArrayList(); - // Iterator it = ((IStructuredSelection) selection).iterator(); - - List selection = (List) selectionService.getSelection(); - if (selection == null) - return; - - StringBuilder builder = new StringBuilder(); - for (Group group : selection) { - Group currGroup = group; - String groupName = UserAdminUtils.getUserLocalId(currGroup.getName()); - // TODO add checks - builder.append(groupName).append("; "); - // groups.add(currGroup); - } - - if (!MessageDialog.openQuestion(Display.getCurrent().getActiveShell(), "Delete Groups", "Are you sure that you " - + "want to delete these groups?\n" + builder.substring(0, builder.length() - 2))) - return; - - userAdminWrapper.beginTransactionIfNeeded(); - UserAdmin userAdmin = userAdminWrapper.getUserAdmin(); - // IWorkbenchPage iwp = - // HandlerUtil.getActiveWorkbenchWindow(event).getActivePage(); - for (Group group : selection) { - String groupName = group.getName(); - // TODO find a way to close the editor cleanly if opened. Cannot be - // done through the UserAdminListeners, it causes a - // java.util.ConcurrentModificationException because disposing the - // editor unregisters and disposes the listener - // IEditorPart part = iwp.findEditor(new UserEditorInput(groupName)); - // if (part != null) - // iwp.closeEditor(part, false); - userAdmin.removeRole(groupName); - } - userAdminWrapper.commitOrNotifyTransactionStateChange(); - - // Update the view - for (Group group : selection) { - userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_REMOVED, group)); - } - - // return null; - } - - @CanExecute - public boolean canExecute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) { - return part.getObject() instanceof GroupsView && selectionService.getSelection() != null; - } - - /* DEPENDENCY INJECTION */ - // public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { - // this.userAdminWrapper = userAdminWrapper; - // } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/DeleteUsers.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/DeleteUsers.java deleted file mode 100644 index d1afd2210..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/DeleteUsers.java +++ /dev/null @@ -1,88 +0,0 @@ -package org.argeo.cms.e4.users.handlers; - -import java.util.List; - -import javax.inject.Inject; -import javax.inject.Named; - -import org.argeo.cms.auth.UserAdminUtils; -import org.argeo.cms.e4.users.UserAdminWrapper; -import org.argeo.cms.e4.users.UsersView; -import org.eclipse.e4.core.di.annotations.CanExecute; -import org.eclipse.e4.core.di.annotations.Execute; -import org.eclipse.e4.ui.model.application.ui.basic.MPart; -import org.eclipse.e4.ui.services.IServiceConstants; -import org.eclipse.e4.ui.workbench.modeling.ESelectionService; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.swt.widgets.Display; -import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdmin; -import org.osgi.service.useradmin.UserAdminEvent; - -/** Delete the selected users */ -public class DeleteUsers { - // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".deleteUsers"; - - /* DEPENDENCY INJECTION */ - @Inject - private UserAdminWrapper userAdminWrapper; - - @SuppressWarnings("unchecked") - @Execute - public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) { - // ISelection selection = null;// HandlerUtil.getCurrentSelection(event); - // if (selection.isEmpty()) - // return null; - List selection = (List) selectionService.getSelection(); - if (selection == null) - return; - -// Iterator it = ((IStructuredSelection) selection).iterator(); -// List users = new ArrayList(); - StringBuilder builder = new StringBuilder(); - - for(User user:selection) { - User currUser = user; -// User currUser = it.next(); - String userName = UserAdminUtils.getUserLocalId(currUser.getName()); - if (UserAdminUtils.isCurrentUser(currUser)) { - MessageDialog.openError(Display.getCurrent().getActiveShell(), "Deletion forbidden", - "You cannot delete your own user this way."); - return; - } - builder.append(userName).append("; "); -// users.add(currUser); - } - - if (!MessageDialog.openQuestion(Display.getCurrent().getActiveShell(), "Delete Users", - "Are you sure that you want to delete these users?\n" + builder.substring(0, builder.length() - 2))) - return; - - userAdminWrapper.beginTransactionIfNeeded(); - UserAdmin userAdmin = userAdminWrapper.getUserAdmin(); - // IWorkbenchPage iwp = - // HandlerUtil.getActiveWorkbenchWindow(event).getActivePage(); - - for (User user : selection) { - String userName = user.getName(); - // TODO find a way to close the editor cleanly if opened. Cannot be - // done through the UserAdminListeners, it causes a - // java.util.ConcurrentModificationException because disposing the - // editor unregisters and disposes the listener - // IEditorPart part = iwp.findEditor(new UserEditorInput(userName)); - // if (part != null) - // iwp.closeEditor(part, false); - userAdmin.removeRole(userName); - } - userAdminWrapper.commitOrNotifyTransactionStateChange(); - - for (User user : selection) { - userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_REMOVED, user)); - } - } - - @CanExecute - public boolean canExecute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) { - return part.getObject() instanceof UsersView && selectionService.getSelection() != null; - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewGroup.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewGroup.java deleted file mode 100644 index 41e14e097..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewGroup.java +++ /dev/null @@ -1,212 +0,0 @@ -package org.argeo.cms.e4.users.handlers; - -import java.util.Dictionary; -import java.util.Map; - -import javax.inject.Inject; - -import org.argeo.cms.e4.users.UserAdminWrapper; -import org.argeo.cms.swt.CmsException; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.dialogs.ErrorFeedback; -import org.argeo.util.directory.DirectoryConf; -import org.argeo.util.naming.LdapAttrs; -import org.eclipse.e4.core.di.annotations.Execute; -import org.eclipse.jface.wizard.Wizard; -import org.eclipse.jface.wizard.WizardDialog; -import org.eclipse.jface.wizard.WizardPage; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.FocusEvent; -import org.eclipse.swt.events.FocusListener; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Combo; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; -import org.osgi.service.useradmin.Group; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.UserAdminEvent; - -/** Create a new group */ -public class NewGroup { - // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".newGroup"; - - /* DEPENDENCY INJECTION */ - @Inject - private UserAdminWrapper userAdminWrapper; - - @Execute - public Object execute() { - NewGroupWizard newGroupWizard = new NewGroupWizard(); - newGroupWizard.setWindowTitle("Group creation"); - WizardDialog dialog = new WizardDialog(Display.getCurrent().getActiveShell(), newGroupWizard); - dialog.open(); - return null; - } - - private class NewGroupWizard extends Wizard { - - // Pages - private MainGroupInfoWizardPage mainGroupInfo; - - // UI fields - private Text dNameTxt, commonNameTxt, descriptionTxt; - private Combo baseDnCmb; - - public NewGroupWizard() { - } - - @Override - public void addPages() { - mainGroupInfo = new MainGroupInfoWizardPage(); - addPage(mainGroupInfo); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Override - public boolean performFinish() { - if (!canFinish()) - return false; - String commonName = commonNameTxt.getText(); - try { - userAdminWrapper.beginTransactionIfNeeded(); - String dn = getDn(commonName); - Group group = (Group) userAdminWrapper.getUserAdmin().createRole(dn, Role.GROUP); - Dictionary props = group.getProperties(); - String descStr = descriptionTxt.getText(); - if (EclipseUiUtils.notEmpty(descStr)) - props.put(LdapAttrs.description.name(), descStr); - userAdminWrapper.commitOrNotifyTransactionStateChange(); - userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CREATED, group)); - return true; - } catch (Exception e) { - ErrorFeedback.show("Cannot create new group " + commonName, e); - return false; - } - } - - private class MainGroupInfoWizardPage extends WizardPage implements FocusListener { - private static final long serialVersionUID = -3150193365151601807L; - - public MainGroupInfoWizardPage() { - super("Main"); - setTitle("General information"); - setMessage("Please choose a domain, provide a common name " + "and a free description"); - } - - @Override - public void createControl(Composite parent) { - Composite bodyCmp = new Composite(parent, SWT.NONE); - setControl(bodyCmp); - bodyCmp.setLayout(new GridLayout(2, false)); - - dNameTxt = EclipseUiUtils.createGridLT(bodyCmp, "Distinguished name"); - dNameTxt.setEnabled(false); - - baseDnCmb = createGridLC(bodyCmp, "Base DN"); - // Initialise before adding the listener to avoid NPE - initialiseDnCmb(baseDnCmb); - baseDnCmb.addFocusListener(this); - - commonNameTxt = EclipseUiUtils.createGridLT(bodyCmp, "Common name"); - commonNameTxt.addFocusListener(this); - - Label descLbl = new Label(bodyCmp, SWT.LEAD); - descLbl.setText("Description"); - descLbl.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false)); - descriptionTxt = new Text(bodyCmp, SWT.LEAD | SWT.MULTI | SWT.WRAP | SWT.BORDER); - descriptionTxt.setLayoutData(EclipseUiUtils.fillAll()); - descriptionTxt.addFocusListener(this); - - // Initialize buttons - setPageComplete(false); - getContainer().updateButtons(); - } - - @Override - public void focusLost(FocusEvent event) { - String name = commonNameTxt.getText(); - if (EclipseUiUtils.isEmpty(name)) - dNameTxt.setText(""); - else - dNameTxt.setText(getDn(name)); - - String message = checkComplete(); - if (message != null) { - setMessage(message, WizardPage.ERROR); - setPageComplete(false); - } else { - setMessage("Complete", WizardPage.INFORMATION); - setPageComplete(true); - } - getContainer().updateButtons(); - } - - @Override - public void focusGained(FocusEvent event) { - } - - /** @return the error message or null if complete */ - protected String checkComplete() { - String name = commonNameTxt.getText(); - - if (name.trim().equals("")) - return "Common name must not be empty"; - Role role = userAdminWrapper.getUserAdmin().getRole(getDn(name)); - if (role != null) - return "Group " + name + " already exists"; - return null; - } - - @Override - public void setVisible(boolean visible) { - super.setVisible(visible); - if (visible) - if (baseDnCmb.getSelectionIndex() == -1) - baseDnCmb.setFocus(); - else - commonNameTxt.setFocus(); - } - } - - private Map getDns() { - return userAdminWrapper.getKnownBaseDns(true); - } - - private String getDn(String cn) { - Map dns = getDns(); - String bdn = baseDnCmb.getText(); - if (EclipseUiUtils.notEmpty(bdn)) { - Dictionary props = DirectoryConf.uriAsProperties(dns.get(bdn)); - String dn = LdapAttrs.cn.name() + "=" + cn + "," + DirectoryConf.groupBase.getValue(props) + "," + bdn; - return dn; - } - return null; - } - - private void initialiseDnCmb(Combo combo) { - Map dns = userAdminWrapper.getKnownBaseDns(true); - if (dns.isEmpty()) - throw new CmsException("No writable base dn found. Cannot create group"); - combo.setItems(dns.keySet().toArray(new String[0])); - if (dns.size() == 1) - combo.select(0); - } - } - - private Combo createGridLC(Composite parent, String label) { - Label lbl = new Label(parent, SWT.LEAD); - lbl.setText(label); - lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); - Combo combo = new Combo(parent, SWT.LEAD | SWT.BORDER | SWT.READ_ONLY); - combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); - return combo; - } - - /* DEPENDENCY INJECTION */ - public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { - this.userAdminWrapper = userAdminWrapper; - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewUser.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewUser.java deleted file mode 100644 index 40a446006..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewUser.java +++ /dev/null @@ -1,287 +0,0 @@ -package org.argeo.cms.e4.users.handlers; - -import java.util.Dictionary; -import java.util.List; -import java.util.Map; - -import javax.inject.Inject; -import javax.naming.InvalidNameException; -import javax.naming.ldap.LdapName; -import javax.naming.ldap.Rdn; - -import org.argeo.cms.auth.UserAdminUtils; -import org.argeo.cms.e4.users.UiAdminUtils; -import org.argeo.cms.e4.users.UserAdminWrapper; -import org.argeo.cms.swt.CmsException; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.dialogs.ErrorFeedback; -import org.argeo.util.directory.DirectoryConf; -import org.argeo.util.naming.LdapAttrs; -import org.eclipse.e4.core.di.annotations.Execute; -import org.eclipse.jface.wizard.Wizard; -import org.eclipse.jface.wizard.WizardDialog; -import org.eclipse.jface.wizard.WizardPage; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Combo; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdminEvent; - -/** Open a wizard that enables creation of a new user. */ -public class NewUser { - // private final static Log log = LogFactory.getLog(NewUser.class); - // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".newUser"; - - /* DEPENDENCY INJECTION */ - @Inject - private UserAdminWrapper userAdminWrapper; - - @Execute - public Object execute() { - NewUserWizard newUserWizard = new NewUserWizard(); - newUserWizard.setWindowTitle("User creation"); - WizardDialog dialog = new WizardDialog(Display.getCurrent().getActiveShell(), newUserWizard); - dialog.open(); - return null; - } - - private class NewUserWizard extends Wizard { - - // pages - private MainUserInfoWizardPage mainUserInfo; - - // End user fields - private Text dNameTxt, usernameTxt, firstNameTxt, lastNameTxt, primaryMailTxt, pwd1Txt, pwd2Txt; - private Combo baseDnCmb; - - public NewUserWizard() { - - } - - @Override - public void addPages() { - mainUserInfo = new MainUserInfoWizardPage(); - addPage(mainUserInfo); - String message = "Default wizard that also eases user creation tests:\n " - + "Mail and last name are automatically " - + "generated form the uid. Password are defauted to 'demo'."; - mainUserInfo.setMessage(message, WizardPage.WARNING); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Override - public boolean performFinish() { - if (!canFinish()) - return false; - String username = mainUserInfo.getUsername(); - userAdminWrapper.beginTransactionIfNeeded(); - try { - User user = (User) userAdminWrapper.getUserAdmin().createRole(getDn(username), Role.USER); - - Dictionary props = user.getProperties(); - - String lastNameStr = lastNameTxt.getText(); - if (EclipseUiUtils.notEmpty(lastNameStr)) - props.put(LdapAttrs.sn.name(), lastNameStr); - - String firstNameStr = firstNameTxt.getText(); - if (EclipseUiUtils.notEmpty(firstNameStr)) - props.put(LdapAttrs.givenName.name(), firstNameStr); - - String cn = UserAdminUtils.buildDefaultCn(firstNameStr, lastNameStr); - if (EclipseUiUtils.notEmpty(cn)) - props.put(LdapAttrs.cn.name(), cn); - - String mailStr = primaryMailTxt.getText(); - if (EclipseUiUtils.notEmpty(mailStr)) - props.put(LdapAttrs.mail.name(), mailStr); - - char[] password = mainUserInfo.getPassword(); - user.getCredentials().put(null, password); - userAdminWrapper.commitOrNotifyTransactionStateChange(); - userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CREATED, user)); - return true; - } catch (Exception e) { - ErrorFeedback.show("Cannot create new user " + username, e); - return false; - } - } - - private class MainUserInfoWizardPage extends WizardPage implements ModifyListener { - private static final long serialVersionUID = -3150193365151601807L; - - public MainUserInfoWizardPage() { - super("Main"); - setTitle("Required Information"); - } - - @Override - public void createControl(Composite parent) { - Composite composite = new Composite(parent, SWT.NONE); - composite.setLayout(new GridLayout(2, false)); - dNameTxt = EclipseUiUtils.createGridLT(composite, "Distinguished name", this); - dNameTxt.setEnabled(false); - - baseDnCmb = createGridLC(composite, "Base DN"); - initialiseDnCmb(baseDnCmb); - baseDnCmb.addModifyListener(this); - baseDnCmb.addModifyListener(new ModifyListener() { - private static final long serialVersionUID = -1435351236582736843L; - - @Override - public void modifyText(ModifyEvent event) { - String name = usernameTxt.getText(); - dNameTxt.setText(getDn(name)); - } - }); - - usernameTxt = EclipseUiUtils.createGridLT(composite, "Local ID", this); - usernameTxt.addModifyListener(new ModifyListener() { - private static final long serialVersionUID = -1435351236582736843L; - - @Override - public void modifyText(ModifyEvent event) { - String name = usernameTxt.getText(); - if (name.trim().equals("")) { - dNameTxt.setText(""); - lastNameTxt.setText(""); - primaryMailTxt.setText(""); - pwd1Txt.setText(""); - pwd2Txt.setText(""); - } else { - dNameTxt.setText(getDn(name)); - lastNameTxt.setText(name.toUpperCase()); - primaryMailTxt.setText(getMail(name)); - pwd1Txt.setText("demo"); - pwd2Txt.setText("demo"); - } - } - }); - - primaryMailTxt = EclipseUiUtils.createGridLT(composite, "Email", this); - firstNameTxt = EclipseUiUtils.createGridLT(composite, "First name", this); - lastNameTxt = EclipseUiUtils.createGridLT(composite, "Last name", this); - pwd1Txt = EclipseUiUtils.createGridLP(composite, "Password", this); - pwd2Txt = EclipseUiUtils.createGridLP(composite, "Repeat password", this); - setControl(composite); - - // Initialize buttons - setPageComplete(false); - getContainer().updateButtons(); - } - - @Override - public void modifyText(ModifyEvent event) { - String message = checkComplete(); - if (message != null) { - setMessage(message, WizardPage.ERROR); - setPageComplete(false); - } else { - setMessage("Complete", WizardPage.INFORMATION); - setPageComplete(true); - } - getContainer().updateButtons(); - } - - /** @return error message or null if complete */ - protected String checkComplete() { - String name = usernameTxt.getText(); - - if (name.trim().equals("")) - return "User name must not be empty"; - Role role = userAdminWrapper.getUserAdmin().getRole(getDn(name)); - if (role != null) - return "User " + name + " already exists"; - if (!primaryMailTxt.getText().matches(UiAdminUtils.EMAIL_PATTERN)) - return "Not a valid email address"; - if (lastNameTxt.getText().trim().equals("")) - return "Specify a last name"; - if (pwd1Txt.getText().trim().equals("")) - return "Specify a password"; - if (pwd2Txt.getText().trim().equals("")) - return "Repeat the password"; - if (!pwd2Txt.getText().equals(pwd1Txt.getText())) - return "Passwords are different"; - return null; - } - - @Override - public void setVisible(boolean visible) { - super.setVisible(visible); - if (visible) - if (baseDnCmb.getSelectionIndex() == -1) - baseDnCmb.setFocus(); - else - usernameTxt.setFocus(); - } - - public String getUsername() { - return usernameTxt.getText(); - } - - public char[] getPassword() { - return pwd1Txt.getTextChars(); - } - - } - - private Map getDns() { - return userAdminWrapper.getKnownBaseDns(true); - } - - private String getDn(String uid) { - Map dns = getDns(); - String bdn = baseDnCmb.getText(); - if (EclipseUiUtils.notEmpty(bdn)) { - Dictionary props = DirectoryConf.uriAsProperties(dns.get(bdn)); - String dn = LdapAttrs.uid.name() + "=" + uid + "," + DirectoryConf.userBase.getValue(props) + "," + bdn; - return dn; - } - return null; - } - - private void initialiseDnCmb(Combo combo) { - Map dns = userAdminWrapper.getKnownBaseDns(true); - if (dns.isEmpty()) - throw new CmsException("No writable base dn found. Cannot create user"); - combo.setItems(dns.keySet().toArray(new String[0])); - if (dns.size() == 1) - combo.select(0); - } - - private String getMail(String username) { - if (baseDnCmb.getSelectionIndex() == -1) - return null; - String baseDn = baseDnCmb.getText(); - try { - LdapName name = new LdapName(baseDn); - List rdns = name.getRdns(); - return username + "@" + (String) rdns.get(1).getValue() + '.' + (String) rdns.get(0).getValue(); - } catch (InvalidNameException e) { - throw new CmsException("Unable to generate mail for " + username + " with base dn " + baseDn, e); - } - } - } - - private Combo createGridLC(Composite parent, String label) { - Label lbl = new Label(parent, SWT.LEAD); - lbl.setText(label); - lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); - Combo combo = new Combo(parent, SWT.LEAD | SWT.BORDER | SWT.READ_ONLY); - combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); - return combo; - } - - /* DEPENDENCY INJECTION */ - public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { - this.userAdminWrapper = userAdminWrapper; - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/package-info.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/package-info.java deleted file mode 100644 index cf3db1d16..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Users management handlers. */ -package org.argeo.cms.e4.users.handlers; \ No newline at end of file diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/package-info.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/package-info.java deleted file mode 100644 index c6f14b0cf..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Users management perspective. */ -package org.argeo.cms.e4.users; \ No newline at end of file diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/CommonNameLP.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/CommonNameLP.java deleted file mode 100644 index 2d8db67d7..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/CommonNameLP.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.argeo.cms.e4.users.providers; - -import org.argeo.cms.auth.UserAdminUtils; -import org.argeo.util.naming.LdapAttrs; -import org.osgi.service.useradmin.User; - -/** Simply declare a label provider that returns the common name of a user */ -public class CommonNameLP extends UserAdminAbstractLP { - private static final long serialVersionUID = 5256703081044911941L; - - @Override - public String getText(User user) { - return UserAdminUtils.getProperty(user, LdapAttrs.cn.name()); - } - - @Override - public String getToolTipText(Object element) { - return UserAdminUtils.getProperty((User) element, LdapAttrs.DN); - } - -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/DomainNameLP.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/DomainNameLP.java deleted file mode 100644 index e23729da8..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/DomainNameLP.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.argeo.cms.e4.users.providers; - -import org.argeo.cms.auth.UserAdminUtils; -import org.osgi.service.useradmin.User; - -/** The human friendly domain name for the corresponding user. */ -public class DomainNameLP extends UserAdminAbstractLP { - private static final long serialVersionUID = 5256703081044911941L; - - @Override - public String getText(User user) { - return UserAdminUtils.getDomainName(user); - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/MailLP.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/MailLP.java deleted file mode 100644 index 52d3b858f..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/MailLP.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.argeo.cms.e4.users.providers; - -import org.argeo.cms.auth.UserAdminUtils; -import org.argeo.util.naming.LdapAttrs; -import org.osgi.service.useradmin.User; - -/** Simply declare a label provider that returns the Primary Mail of a user */ -public class MailLP extends UserAdminAbstractLP { - private static final long serialVersionUID = 8329764452141982707L; - - @Override - public String getText(User user) { - return UserAdminUtils.getProperty(user, LdapAttrs.mail.name()); - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/RoleIconLP.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/RoleIconLP.java deleted file mode 100644 index 8c94093e4..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/RoleIconLP.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.argeo.cms.e4.users.providers; - -import org.argeo.api.cms.CmsContext; -import org.argeo.api.cms.CmsConstants; -import org.argeo.cms.auth.UserAdminUtils; -import org.argeo.cms.e4.users.SecurityAdminImages; -import org.argeo.util.naming.LdapAttrs; -import org.eclipse.swt.graphics.Image; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; - -/** Provide a bundle specific image depending on the current user type */ -public class RoleIconLP extends UserAdminAbstractLP { - private static final long serialVersionUID = 6550449442061090388L; - - @Override - public String getText(User user) { - return ""; - } - - @Override - public Image getImage(Object element) { - User user = (User) element; - String dn = user.getName(); - if (dn.endsWith(CmsConstants.ROLES_BASEDN)) - return SecurityAdminImages.ICON_ROLE; - else if (user.getType() == Role.GROUP) { - String businessCategory = UserAdminUtils.getProperty(user, LdapAttrs.businessCategory); - if (businessCategory != null && businessCategory.equals(CmsContext.WORKGROUP)) - return SecurityAdminImages.ICON_WORKGROUP; - return SecurityAdminImages.ICON_GROUP; - } else - return SecurityAdminImages.ICON_USER; - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserAdminAbstractLP.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserAdminAbstractLP.java deleted file mode 100644 index 29873db2f..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserAdminAbstractLP.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.argeo.cms.e4.users.providers; - -import javax.naming.InvalidNameException; -import javax.naming.ldap.LdapName; - -import org.argeo.cms.auth.UserAdminUtils; -import org.argeo.cms.swt.CmsException; -import org.eclipse.jface.resource.JFaceResources; -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Font; -import org.eclipse.swt.widgets.Display; -import org.osgi.service.useradmin.User; - -/** - * Utility class that add font modifications to a column label provider - * depending on the given user properties - */ -public abstract class UserAdminAbstractLP extends ColumnLabelProvider { - private static final long serialVersionUID = 137336765024922368L; - - // private Font italic; - private Font bold; - - @Override - public Font getFont(Object element) { - // Self as bold - try { - LdapName selfUserName = UserAdminUtils.getCurrentUserLdapName(); - String userName = ((User) element).getName(); - LdapName userLdapName = new LdapName(userName); - if (userLdapName.equals(selfUserName)) { - if (bold == null) - bold = JFaceResources.getFontRegistry() - .defaultFontDescriptor().setStyle(SWT.BOLD) - .createFont(Display.getCurrent()); - return bold; - } - } catch (InvalidNameException e) { - throw new CmsException("cannot parse dn for " + element, e); - } - - // Disabled as Italic - // Node userProfile = (Node) elem; - // if (!userProfile.getProperty(ARGEO_ENABLED).getBoolean()) - // return italic; - - return null; - // return super.getFont(element); - } - - @Override - public String getText(Object element) { - User user = (User) element; - return getText(user); - } - - public void setDisplay(Display display) { - // italic = JFaceResources.getFontRegistry().defaultFontDescriptor() - // .setStyle(SWT.ITALIC).createFont(display); - bold = JFaceResources.getFontRegistry().defaultFontDescriptor() - .setStyle(SWT.BOLD).createFont(Display.getCurrent()); - } - - public abstract String getText(User user); -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserDragListener.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserDragListener.java deleted file mode 100644 index 56a26244b..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserDragListener.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.argeo.cms.e4.users.providers; - -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.swt.dnd.DragSourceEvent; -import org.eclipse.swt.dnd.DragSourceListener; -import org.osgi.service.useradmin.User; - -/** Default drag listener to modify group and users via the UI */ -public class UserDragListener implements DragSourceListener { - private static final long serialVersionUID = -2074337775033781454L; - private final Viewer viewer; - - public UserDragListener(Viewer viewer) { - this.viewer = viewer; - } - - public void dragStart(DragSourceEvent event) { - // TODO implement finer checks - IStructuredSelection selection = (IStructuredSelection) viewer - .getSelection(); - if (selection.isEmpty() || selection.size() > 1) - event.doit = false; - else - event.doit = true; - } - - public void dragSetData(DragSourceEvent event) { - // TODO Support multiple selection - Object obj = ((IStructuredSelection) viewer.getSelection()) - .getFirstElement(); - if (obj != null) { - User user = (User) obj; - event.data = user.getName(); - } - } - - public void dragFinished(DragSourceEvent event) { - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserFilter.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserFilter.java deleted file mode 100644 index 154b04725..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserFilter.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.argeo.cms.e4.users.providers; - -import static org.argeo.eclipse.ui.EclipseUiUtils.notEmpty; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.cms.auth.UserAdminUtils; -import org.argeo.util.naming.LdapAttrs; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.jface.viewers.ViewerFilter; -import org.osgi.service.useradmin.User; - -/** - * Filter user list using JFace mechanism on the client (yet on the server) side - * rather than having the UserAdmin to process the search - */ -public class UserFilter extends ViewerFilter { - private static final long serialVersionUID = 5082509381672880568L; - - private String searchString; - private boolean showSystemRole = true; - - private final String[] knownProps = { LdapAttrs.DN, LdapAttrs.cn.name(), LdapAttrs.givenName.name(), - LdapAttrs.sn.name(), LdapAttrs.uid.name(), LdapAttrs.description.name(), LdapAttrs.mail.name() }; - - public void setSearchText(String s) { - // ensure that the value can be used for matching - if (notEmpty(s)) - searchString = ".*" + s.toLowerCase() + ".*"; - else - searchString = ".*"; - } - - public void setShowSystemRole(boolean showSystemRole) { - this.showSystemRole = showSystemRole; - } - - @Override - public boolean select(Viewer viewer, Object parentElement, Object element) { - User user = (User) element; - if (!showSystemRole && user.getName().matches(".*(" + CmsConstants.ROLES_BASEDN + ")")) - // UserAdminUtils.getProperty(user, LdifName.dn.name()) - // .toLowerCase().endsWith(AuthConstants.ROLES_BASEDN)) - return false; - - if (searchString == null || searchString.length() == 0) - return true; - - if (user.getName().matches(searchString)) - return true; - - for (String key : knownProps) { - String currVal = UserAdminUtils.getProperty(user, key); - if (notEmpty(currVal) && currVal.toLowerCase().matches(searchString)) - return true; - } - return false; - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserNameLP.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserNameLP.java deleted file mode 100644 index 3cd00eb2b..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserNameLP.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.argeo.cms.e4.users.providers; - -import org.osgi.service.useradmin.User; - -/** Simply declare a label provider that returns the username of a user */ -public class UserNameLP extends UserAdminAbstractLP { - private static final long serialVersionUID = 6550449442061090388L; - - @Override - public String getText(User user) { - return user.getName(); - } -} diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/package-info.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/package-info.java deleted file mode 100644 index 33bef8dee..000000000 --- a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Users management content providers. */ -package org.argeo.cms.e4.users.providers; \ No newline at end of file diff --git a/eclipse/org.argeo.cms.swt/.classpath b/eclipse/org.argeo.cms.swt/.classpath deleted file mode 100644 index e03d341b1..000000000 --- a/eclipse/org.argeo.cms.swt/.classpath +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - diff --git a/eclipse/org.argeo.cms.swt/.project b/eclipse/org.argeo.cms.swt/.project deleted file mode 100644 index 8ac021b59..000000000 --- a/eclipse/org.argeo.cms.swt/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.argeo.cms.swt - - - - - - 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/eclipse/org.argeo.cms.swt/META-INF/.gitignore b/eclipse/org.argeo.cms.swt/META-INF/.gitignore deleted file mode 100644 index 4854a41b9..000000000 --- a/eclipse/org.argeo.cms.swt/META-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/MANIFEST.MF diff --git a/eclipse/org.argeo.cms.swt/OSGI-INF/cmsUserApp.xml b/eclipse/org.argeo.cms.swt/OSGI-INF/cmsUserApp.xml deleted file mode 100644 index 4f2a405d5..000000000 --- a/eclipse/org.argeo.cms.swt/OSGI-INF/cmsUserApp.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/eclipse/org.argeo.cms.swt/bnd.bnd b/eclipse/org.argeo.cms.swt/bnd.bnd deleted file mode 100644 index 2dda08b2a..000000000 --- a/eclipse/org.argeo.cms.swt/bnd.bnd +++ /dev/null @@ -1,11 +0,0 @@ -Import-Package: org.eclipse.swt,\ -org.eclipse.jface.window,\ -org.eclipse.core.commands.common,\ -javax.servlet.*;version="[3,5)",\ -* - -Bundle-ActivationPolicy: lazy - -Service-Component: \ -OSGI-INF/cmsUserApp.xml - \ No newline at end of file diff --git a/eclipse/org.argeo.cms.swt/build.properties b/eclipse/org.argeo.cms.swt/build.properties deleted file mode 100644 index 5f0f21af9..000000000 --- a/eclipse/org.argeo.cms.swt/build.properties +++ /dev/null @@ -1,5 +0,0 @@ -output.. = bin/ -bin.includes = META-INF/,\ - .,\ - OSGI-INF/cmsUserApp.xml -source.. = src/ diff --git a/eclipse/org.argeo.cms.swt/icons/actions/add.png b/eclipse/org.argeo.cms.swt/icons/actions/add.png deleted file mode 100644 index 5c06bf082..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/actions/add.png and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/actions/close-all.png b/eclipse/org.argeo.cms.swt/icons/actions/close-all.png deleted file mode 100644 index 81bfc950b..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/actions/close-all.png and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/actions/delete.png b/eclipse/org.argeo.cms.swt/icons/actions/delete.png deleted file mode 100644 index 9712723d7..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/actions/delete.png and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/actions/edit.png b/eclipse/org.argeo.cms.swt/icons/actions/edit.png deleted file mode 100644 index ad3db9f42..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/actions/edit.png and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/actions/save-all.png b/eclipse/org.argeo.cms.swt/icons/actions/save-all.png deleted file mode 100644 index f48ed320b..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/actions/save-all.png and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/actions/save.png b/eclipse/org.argeo.cms.swt/icons/actions/save.png deleted file mode 100644 index 1c58ada49..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/actions/save.png and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/active.gif b/eclipse/org.argeo.cms.swt/icons/active.gif deleted file mode 100644 index 7d24707ee..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/active.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/add.gif b/eclipse/org.argeo.cms.swt/icons/add.gif deleted file mode 100644 index 252d7ebcb..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/add.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/add.png b/eclipse/org.argeo.cms.swt/icons/add.png deleted file mode 100644 index c7edfecaa..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/add.png and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/addFolder.gif b/eclipse/org.argeo.cms.swt/icons/addFolder.gif deleted file mode 100644 index d3f43d977..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/addFolder.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/addPrivileges.gif b/eclipse/org.argeo.cms.swt/icons/addPrivileges.gif deleted file mode 100644 index a6b251fc8..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/addPrivileges.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/addRepo.gif b/eclipse/org.argeo.cms.swt/icons/addRepo.gif deleted file mode 100644 index 26d81c065..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/addRepo.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/addWorkspace.png b/eclipse/org.argeo.cms.swt/icons/addWorkspace.png deleted file mode 100644 index bbee7755f..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/addWorkspace.png and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/adminLog.gif b/eclipse/org.argeo.cms.swt/icons/adminLog.gif deleted file mode 100644 index 6ef3bca66..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/adminLog.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/batch.gif b/eclipse/org.argeo.cms.swt/icons/batch.gif deleted file mode 100644 index b8ca14a8b..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/batch.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/begin.gif b/eclipse/org.argeo.cms.swt/icons/begin.gif deleted file mode 100755 index feb8e94a7..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/begin.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/binary.png b/eclipse/org.argeo.cms.swt/icons/binary.png deleted file mode 100644 index fdf4f82be..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/binary.png and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/browser.gif b/eclipse/org.argeo.cms.swt/icons/browser.gif deleted file mode 100644 index 6c7320c69..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/browser.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/bundles.gif b/eclipse/org.argeo.cms.swt/icons/bundles.gif deleted file mode 100644 index e9a6bd966..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/bundles.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/changePassword.gif b/eclipse/org.argeo.cms.swt/icons/changePassword.gif deleted file mode 100644 index 274a850e4..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/changePassword.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/clear.gif b/eclipse/org.argeo.cms.swt/icons/clear.gif deleted file mode 100644 index 6bc10f9d0..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/clear.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/close-all.png b/eclipse/org.argeo.cms.swt/icons/close-all.png deleted file mode 100644 index 85d4d429b..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/close-all.png and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/commit.gif b/eclipse/org.argeo.cms.swt/icons/commit.gif deleted file mode 100755 index 876f3eb16..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/commit.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/delete.png b/eclipse/org.argeo.cms.swt/icons/delete.png deleted file mode 100644 index 676a39dcf..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/delete.png and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/dumpNode.gif b/eclipse/org.argeo.cms.swt/icons/dumpNode.gif deleted file mode 100644 index 14eb1be09..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/dumpNode.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/file.gif b/eclipse/org.argeo.cms.swt/icons/file.gif deleted file mode 100644 index ef3028807..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/file.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/folder.gif b/eclipse/org.argeo.cms.swt/icons/folder.gif deleted file mode 100644 index 42e027c93..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/folder.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/getSize.gif b/eclipse/org.argeo.cms.swt/icons/getSize.gif deleted file mode 100644 index b05bf3e3d..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/getSize.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/group.png b/eclipse/org.argeo.cms.swt/icons/group.png deleted file mode 100644 index cc6683aff..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/group.png and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/home.gif b/eclipse/org.argeo.cms.swt/icons/home.gif deleted file mode 100644 index fd0c66950..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/home.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/home.png b/eclipse/org.argeo.cms.swt/icons/home.png deleted file mode 100644 index 5eb096790..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/home.png and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/import_fs.png b/eclipse/org.argeo.cms.swt/icons/import_fs.png deleted file mode 100644 index d7c890c81..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/import_fs.png and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/installed.gif b/eclipse/org.argeo.cms.swt/icons/installed.gif deleted file mode 100644 index 298871653..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/installed.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/log.gif b/eclipse/org.argeo.cms.swt/icons/log.gif deleted file mode 100644 index e3ecc5535..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/log.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/logout.png b/eclipse/org.argeo.cms.swt/icons/logout.png deleted file mode 100644 index f2952fa5b..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/logout.png and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/maintenance.gif b/eclipse/org.argeo.cms.swt/icons/maintenance.gif deleted file mode 100644 index e5690ecb1..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/maintenance.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/node.gif b/eclipse/org.argeo.cms.swt/icons/node.gif deleted file mode 100644 index 364c0e70b..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/node.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/nodes.gif b/eclipse/org.argeo.cms.swt/icons/nodes.gif deleted file mode 100644 index bba3dbc69..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/nodes.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/osgi_explorer.gif b/eclipse/org.argeo.cms.swt/icons/osgi_explorer.gif deleted file mode 100644 index e9a6bd966..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/osgi_explorer.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/password.gif b/eclipse/org.argeo.cms.swt/icons/password.gif deleted file mode 100644 index a6b251fc8..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/password.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/person-logged-in.png b/eclipse/org.argeo.cms.swt/icons/person-logged-in.png deleted file mode 100644 index 87acc1435..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/person-logged-in.png and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/person.png b/eclipse/org.argeo.cms.swt/icons/person.png deleted file mode 100644 index 7d979a531..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/person.png and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/query.png b/eclipse/org.argeo.cms.swt/icons/query.png deleted file mode 100644 index 54c089de1..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/query.png and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/refresh.png b/eclipse/org.argeo.cms.swt/icons/refresh.png deleted file mode 100644 index 71b3481c9..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/refresh.png and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/remote_connected.gif b/eclipse/org.argeo.cms.swt/icons/remote_connected.gif deleted file mode 100644 index 1492b4efa..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/remote_connected.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/remote_disconnected.gif b/eclipse/org.argeo.cms.swt/icons/remote_disconnected.gif deleted file mode 100644 index 6c54da9ad..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/remote_disconnected.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/remove.gif b/eclipse/org.argeo.cms.swt/icons/remove.gif deleted file mode 100644 index 0ae6decd0..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/remove.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/removePrivileges.gif b/eclipse/org.argeo.cms.swt/icons/removePrivileges.gif deleted file mode 100644 index aa78fd2fa..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/removePrivileges.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/rename.gif b/eclipse/org.argeo.cms.swt/icons/rename.gif deleted file mode 100644 index 8048405a7..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/rename.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/repositories.gif b/eclipse/org.argeo.cms.swt/icons/repositories.gif deleted file mode 100644 index c13bea1ca..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/repositories.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/repository_connected.gif b/eclipse/org.argeo.cms.swt/icons/repository_connected.gif deleted file mode 100644 index a15fa5538..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/repository_connected.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/repository_disconnected.gif b/eclipse/org.argeo.cms.swt/icons/repository_disconnected.gif deleted file mode 100644 index 4576dc563..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/repository_disconnected.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/resolved.gif b/eclipse/org.argeo.cms.swt/icons/resolved.gif deleted file mode 100644 index f4a1ea150..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/resolved.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/role.gif b/eclipse/org.argeo.cms.swt/icons/role.gif deleted file mode 100644 index 274a850e4..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/role.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/rollback.gif b/eclipse/org.argeo.cms.swt/icons/rollback.gif deleted file mode 100755 index c75399599..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/rollback.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/save-all.png b/eclipse/org.argeo.cms.swt/icons/save-all.png deleted file mode 100644 index b68a29b2c..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/save-all.png and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/save.gif b/eclipse/org.argeo.cms.swt/icons/save.gif deleted file mode 100644 index 654ad7b42..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/save.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/save.png b/eclipse/org.argeo.cms.swt/icons/save.png deleted file mode 100644 index f27ef2d26..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/save.png and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/save_security.png b/eclipse/org.argeo.cms.swt/icons/save_security.png deleted file mode 100644 index ca41dc92b..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/save_security.png and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/save_security_disabled.png b/eclipse/org.argeo.cms.swt/icons/save_security_disabled.png deleted file mode 100644 index fb7d08d9a..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/save_security_disabled.png and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/security.gif b/eclipse/org.argeo.cms.swt/icons/security.gif deleted file mode 100644 index 57fb95edc..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/security.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/service_published.gif b/eclipse/org.argeo.cms.swt/icons/service_published.gif deleted file mode 100644 index 17f771aff..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/service_published.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/service_referenced.gif b/eclipse/org.argeo.cms.swt/icons/service_referenced.gif deleted file mode 100644 index c24a95fba..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/service_referenced.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/sort.gif b/eclipse/org.argeo.cms.swt/icons/sort.gif deleted file mode 100644 index 23c5d0b11..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/sort.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/starting.gif b/eclipse/org.argeo.cms.swt/icons/starting.gif deleted file mode 100644 index 563743d39..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/starting.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/sync.gif b/eclipse/org.argeo.cms.swt/icons/sync.gif deleted file mode 100644 index b4fa052de..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/sync.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/user.gif b/eclipse/org.argeo.cms.swt/icons/user.gif deleted file mode 100644 index 90a00147b..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/user.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/users.gif b/eclipse/org.argeo.cms.swt/icons/users.gif deleted file mode 100644 index 2de7edd64..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/users.gif and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/workgroup.png b/eclipse/org.argeo.cms.swt/icons/workgroup.png deleted file mode 100644 index 7fef996df..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/workgroup.png and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/workgroup.xcf b/eclipse/org.argeo.cms.swt/icons/workgroup.xcf deleted file mode 100644 index f517c827c..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/workgroup.xcf and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/workspace_connected.png b/eclipse/org.argeo.cms.swt/icons/workspace_connected.png deleted file mode 100644 index 0430baaf5..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/workspace_connected.png and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/icons/workspace_disconnected.png b/eclipse/org.argeo.cms.swt/icons/workspace_disconnected.png deleted file mode 100644 index fddcb8c4e..000000000 Binary files a/eclipse/org.argeo.cms.swt/icons/workspace_disconnected.png and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/jface/dialog/CmsWizardDialog.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/jface/dialog/CmsWizardDialog.java deleted file mode 100644 index 33841a1bb..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/jface/dialog/CmsWizardDialog.java +++ /dev/null @@ -1,222 +0,0 @@ -package org.argeo.cms.jface.dialog; - -import java.lang.reflect.InvocationTargetException; - -import org.argeo.cms.CmsMsg; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.swt.Selected; -import org.argeo.cms.swt.dialogs.LightweightDialog; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.eclipse.jface.operation.IRunnableWithProgress; -import org.eclipse.jface.wizard.IWizard; -import org.eclipse.jface.wizard.IWizardContainer2; -import org.eclipse.jface.wizard.IWizardPage; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.FormAttachment; -import org.eclipse.swt.layout.FormData; -import org.eclipse.swt.layout.FormLayout; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.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.Shell; - -/** A wizard dialog based on {@link LightweightDialog}. */ -public class CmsWizardDialog extends LightweightDialog implements IWizardContainer2 { - private static final long serialVersionUID = -2123153353654812154L; - - private IWizard wizard; - private IWizardPage currentPage; - private int currentPageIndex; - - private Label titleBar; - private Label message; - private Composite[] pageBodies; - private Composite buttons; - private Button back; - private Button next; - private Button finish; - - public CmsWizardDialog(Shell parentShell, IWizard wizard) { - super(parentShell); - this.wizard = wizard; - wizard.setContainer(this); - // create the pages - wizard.addPages(); - currentPage = wizard.getStartingPage(); - if (currentPage == null) - throw new IllegalArgumentException("At least one wizard page is required"); - } - - @Override - protected Control createDialogArea(Composite parent) { - updateWindowTitle(); - - Composite messageArea = new Composite(parent, SWT.NONE); - messageArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); - { - messageArea.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(2, false))); - titleBar = new Label(messageArea, SWT.WRAP); - titleBar.setFont(EclipseUiUtils.getBoldFont(parent)); - titleBar.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, true, false)); - updateTitleBar(); - Button cancelButton = new Button(messageArea, SWT.FLAT); - cancelButton.setText(CmsMsg.cancel.lead()); - cancelButton.setLayoutData(new GridData(SWT.END, SWT.TOP, false, false, 1, 3)); - cancelButton.addSelectionListener((Selected) (e) -> closeShell(CANCEL)); - message = new Label(messageArea, SWT.WRAP); - message.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 2)); - updateMessage(); - } - - Composite body = new Composite(parent, SWT.BORDER); - body.setLayout(new FormLayout()); - body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - pageBodies = new Composite[wizard.getPageCount()]; - IWizardPage[] pages = wizard.getPages(); - for (int i = 0; i < pages.length; i++) { - pageBodies[i] = new Composite(body, SWT.NONE); - pageBodies[i].setLayout(CmsSwtUtils.noSpaceGridLayout()); - setSwitchingFormData(pageBodies[i]); - pages[i].createControl(pageBodies[i]); - } - showPage(currentPage); - - buttons = new Composite(parent, SWT.NONE); - buttons.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false)); - { - boolean singlePage = wizard.getPageCount() == 1; - // singlePage = false;// dev - GridLayout layout = new GridLayout(singlePage ? 1 : 3, true); - layout.marginWidth = 0; - layout.marginHeight = 0; - buttons.setLayout(layout); - // TODO revert order for right-to-left languages - - if (!singlePage) { - back = new Button(buttons, SWT.PUSH); - back.setText(CmsMsg.wizardBack.lead()); - back.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); - back.addSelectionListener((Selected) (e) -> backPressed()); - - next = new Button(buttons, SWT.PUSH); - next.setText(CmsMsg.wizardNext.lead()); - next.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); - next.addSelectionListener((Selected) (e) -> nextPressed()); - } - finish = new Button(buttons, SWT.PUSH); - finish.setText(CmsMsg.wizardFinish.lead()); - finish.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); - finish.addSelectionListener((Selected) (e) -> finishPressed()); - - updateButtons(); - } - return body; - } - - @Override - public IWizardPage getCurrentPage() { - return currentPage; - } - - @Override - public Shell getShell() { - return getForegoundShell(); - } - - @Override - public void showPage(IWizardPage page) { - IWizardPage[] pages = wizard.getPages(); - int index = -1; - for (int i = 0; i < pages.length; i++) { - if (page == pages[i]) { - index = i; - break; - } - } - if (index < 0) - throw new IllegalArgumentException("Cannot find index of wizard page " + page); - pageBodies[index].moveAbove(pageBodies[currentPageIndex]); - - // // clear - // for (Control c : body.getChildren()) - // c.dispose(); - // page.createControl(body); - // body.layout(true, true); - currentPageIndex = index; - currentPage = page; - } - - @Override - public void updateButtons() { - if (back != null) - back.setEnabled(wizard.getPreviousPage(currentPage) != null); - if (next != null) - next.setEnabled(wizard.getNextPage(currentPage) != null && currentPage.canFlipToNextPage()); - if (finish != null) { - finish.setEnabled(wizard.canFinish()); - } - } - - @Override - public void updateMessage() { - if (currentPage.getMessage() != null) - message.setText(currentPage.getMessage()); - } - - @Override - public void updateTitleBar() { - if (currentPage.getTitle() != null) - titleBar.setText(currentPage.getTitle()); - } - - @Override - public void updateWindowTitle() { - setTitle(wizard.getWindowTitle()); - } - - @Override - public void run(boolean fork, boolean cancelable, IRunnableWithProgress runnable) - throws InvocationTargetException, InterruptedException { - // FIXME it creates a dependency to Eclipse Core Runtime - // runnable.run(null); - } - - @Override - public void updateSize() { - // TODO pack? - } - - protected boolean onCancel() { - return wizard.performCancel(); - } - - protected void nextPressed() { - IWizardPage page = wizard.getNextPage(currentPage); - showPage(page); - updateButtons(); - } - - protected void backPressed() { - IWizardPage page = wizard.getPreviousPage(currentPage); - showPage(page); - updateButtons(); - } - - protected void finishPressed() { - if (wizard.performFinish()) - closeShell(OK); - } - - private static void setSwitchingFormData(Composite composite) { - FormData fdLabel = new FormData(); - fdLabel.top = new FormAttachment(0, 0); - fdLabel.left = new FormAttachment(0, 0); - fdLabel.right = new FormAttachment(100, 0); - fdLabel.bottom = new FormAttachment(100, 0); - composite.setLayoutData(fdLabel); - } - -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsException.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsException.java deleted file mode 100644 index 874ea9691..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsException.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.argeo.cms.swt; - -/** @deprecated Use standard Java {@link RuntimeException} instead. */ -@Deprecated -public class CmsException extends RuntimeException { - private static final long serialVersionUID = -5341764743356771313L; - - public CmsException(String message) { - super(message); - } - - public CmsException(String message, Throwable e) { - super(message, e); - } - -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsStyles.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsStyles.java deleted file mode 100644 index 9eba6f6ec..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsStyles.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.argeo.cms.swt; - -/** Styles references in the CSS. */ -@Deprecated -public interface CmsStyles { - // General - public final static String CMS_SHELL = "cms_shell"; - public final static String CMS_MENU_LINK = "cms_menu_link"; - - // Header - public final static String CMS_HEADER = "cms_header"; - public final static String CMS_HEADER_LEAD = "cms_header-lead"; - public final static String CMS_HEADER_CENTER = "cms_header-center"; - public final static String CMS_HEADER_END = "cms_header-end"; - - public final static String CMS_LEAD = "cms_lead"; - public final static String CMS_END = "cms_end"; - public final static String CMS_FOOTER = "cms_footer"; - - public final static String CMS_USER_MENU = "cms_user_menu"; - public final static String CMS_USER_MENU_LINK = "cms_user_menu-link"; - public final static String CMS_USER_MENU_ITEM = "cms_user_menu-item"; - public final static String CMS_LOGIN_DIALOG = "cms_login_dialog"; - public final static String CMS_LOGIN_DIALOG_USERNAME = "cms_login_dialog-username"; - public final static String CMS_LOGIN_DIALOG_PASSWORD = "cms_login_dialog-password"; - - // Body - public final static String CMS_SCROLLED_AREA = "cms_scrolled_area"; - public final static String CMS_BODY = "cms_body"; - public final static String CMS_STATIC_TEXT = "cms_static-text"; - public final static String CMS_LINK = "cms_link"; -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtTheme.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtTheme.java deleted file mode 100644 index 7669b1554..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtTheme.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.argeo.cms.swt; - -import org.argeo.api.cms.ux.CmsIcon; -import org.argeo.api.cms.ux.CmsTheme; -import org.eclipse.swt.graphics.Image; - -/** SWT specific {@link CmsTheme}. */ -public interface CmsSwtTheme extends CmsTheme { -// /** The image registered at this path, or null if not found. */ -// Image getImage(String path); - - /** - * And icon with this file name (without the extension), with a best effort to - * find the appropriate size, or null if not found. - * - * @param name An icon file name without path and extension. - * @param preferredSize the preferred size, if null, - * {@link #getDefaultIconSize()} will be tried. - */ - Image getIcon(String name, Integer preferredSize); - - Image getSmallIcon(CmsIcon icon); - - Image getBigIcon(CmsIcon icon); -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtUi.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtUi.java deleted file mode 100644 index 2fb79f443..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtUi.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.argeo.cms.swt; - -import org.argeo.api.cms.ux.CmsUi; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; - -/** A basic {@link CmsUi}, based on an SWT {@link Composite}. */ -public class CmsSwtUi extends Composite implements CmsUi { - - private static final long serialVersionUID = -107939076610406448L; - - public CmsSwtUi(Composite parent, int style) { - super(parent, style); - setLayout(new GridLayout()); - } - -} \ No newline at end of file diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtUtils.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtUtils.java deleted file mode 100644 index 5d964090b..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtUtils.java +++ /dev/null @@ -1,315 +0,0 @@ -package org.argeo.cms.swt; - -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; -import java.util.StringTokenizer; - -import org.argeo.api.cms.ux.CmsIcon; -import org.argeo.api.cms.ux.CmsStyle; -import org.argeo.api.cms.ux.CmsTheme; -import org.argeo.api.cms.ux.CmsView; -import org.argeo.eclipse.ui.specific.EclipseUiSpecificUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.layout.FormAttachment; -import org.eclipse.swt.layout.FormData; -import org.eclipse.swt.layout.FormLayout; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.layout.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.Layout; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Text; -import org.eclipse.swt.widgets.Widget; - -/** SWT utilities. */ -public class CmsSwtUtils { - /* - * THEME AND VIEW - */ - - public static CmsSwtTheme getCmsTheme(Composite parent) { - CmsSwtTheme theme = (CmsSwtTheme) parent.getData(CmsTheme.class.getName()); - if (theme == null) { - // find parent shell - Shell topShell = parent.getShell(); - while (topShell.getParent() != null) - topShell = (Shell) topShell.getParent(); - theme = (CmsSwtTheme) topShell.getData(CmsTheme.class.getName()); - parent.setData(CmsTheme.class.getName(), theme); - } - return theme; - } - - public static void registerCmsTheme(Shell shell, CmsTheme theme) { - // find parent shell - Shell topShell = shell; - while (topShell.getParent() != null) - topShell = (Shell) topShell.getParent(); - // check if already set - if (topShell.getData(CmsTheme.class.getName()) != null) { - CmsTheme registeredTheme = (CmsTheme) topShell.getData(CmsTheme.class.getName()); - throw new IllegalArgumentException( - "Theme " + registeredTheme.getThemeId() + " already registered in this shell"); - } - topShell.setData(CmsTheme.class.getName(), theme); - } - - public static CmsView getCmsView(Control parent) { - // find parent shell - Shell topShell = parent.getShell(); - while (topShell.getParent() != null) - topShell = (Shell) topShell.getParent(); - return (CmsView) topShell.getData(CmsView.class.getName()); - } - - public static void registerCmsView(Shell shell, CmsView view) { - // find parent shell - Shell topShell = shell; - while (topShell.getParent() != null) - topShell = (Shell) topShell.getParent(); - // check if already set - if (topShell.getData(CmsView.class.getName()) != null) { - CmsView registeredView = (CmsView) topShell.getData(CmsView.class.getName()); - throw new IllegalArgumentException("Cms view " + registeredView + " already registered in this shell"); - } - shell.setData(CmsView.class.getName(), view); - } - - /* - * EVENTS - */ - - /** Sends an event via {@link CmsView#sendEvent(String, Map)}. */ - public static void sendEventOnSelect(Control control, String topic, Map properties) { - SelectionListener listener = (Selected) (e) -> { - getCmsView(control.getParent()).sendEvent(topic, properties); - }; - if (control instanceof Button) { - ((Button) control).addSelectionListener(listener); - } else - throw new UnsupportedOperationException("Control type " + control.getClass() + " is not supported."); - } - - /** - * Convenience method to sends an event via - * {@link CmsView#sendEvent(String, Map)}. - */ - public static void sendEventOnSelect(Control control, String topic, String key, Object value) { - Map properties = new HashMap<>(); - properties.put(key, value); - sendEventOnSelect(control, topic, properties); - } - - /* - * ICONS - */ - /** Get a small icon from this theme. */ - public static Image getSmallIcon(CmsTheme theme, CmsIcon icon) { - return ((CmsSwtTheme) theme).getSmallIcon(icon); - } - - /** Get a big icon from this theme. */ - public static Image getBigIcon(CmsTheme theme, CmsIcon icon) { - return ((CmsSwtTheme) theme).getBigIcon(icon); - } - - /* - * LAYOUT INDEPENDENT - */ - /** Takes the most space possible, depending on parent layout. */ - public static void fill(Control control) { - Layout parentLayout = control.getParent().getLayout(); - if (parentLayout == null) - throw new IllegalStateException("Parent layout is not set"); - if (parentLayout instanceof GridLayout) { - control.setLayoutData(fillAll()); - } else if (parentLayout instanceof FormLayout) { - control.setLayoutData(coverAll()); - } else { - throw new IllegalArgumentException("Unsupported parent layout " + parentLayout.getClass().getName()); - } - } - - /* - * GRID LAYOUT - */ - /** A {@link GridLayout} without any spacing and one column. */ - public static GridLayout noSpaceGridLayout() { - return noSpaceGridLayout(new GridLayout()); - } - - /** - * A {@link GridLayout} without any spacing and multiple columns of unequal - * width. - */ - public static GridLayout noSpaceGridLayout(int columns) { - return noSpaceGridLayout(new GridLayout(columns, false)); - } - - /** @return the same layout, with spaces removed. */ - public static GridLayout noSpaceGridLayout(GridLayout layout) { - layout.horizontalSpacing = 0; - layout.verticalSpacing = 0; - layout.marginWidth = 0; - layout.marginHeight = 0; - return layout; - } - - public static GridData fillAll() { - return new GridData(SWT.FILL, SWT.FILL, true, true); - } - - public static GridData fillWidth() { - return grabWidth(SWT.FILL, SWT.FILL); - } - - public static GridData grabWidth(int horizontalAlignment, int verticalAlignment) { - return new GridData(horizontalAlignment, horizontalAlignment, true, false); - } - - public static GridData fillHeight() { - return grabHeight(SWT.FILL, SWT.FILL); - } - - public static GridData grabHeight(int horizontalAlignment, int verticalAlignment) { - return new GridData(horizontalAlignment, horizontalAlignment, false, true); - } - - /* - * ROW LAYOUT - */ - /** @return the same layout, with margins removed. */ - public static RowLayout noMarginsRowLayout(RowLayout rowLayout) { - rowLayout.marginTop = 0; - rowLayout.marginBottom = 0; - rowLayout.marginLeft = 0; - rowLayout.marginRight = 0; - return rowLayout; - } - - public static RowLayout noMarginsRowLayout(int type) { - return noMarginsRowLayout(new RowLayout(type)); - } - - public static RowData rowData16px() { - return new RowData(16, 16); - } - - /* - * FORM LAYOUT - */ - public static FormData coverAll() { - FormData fdLabel = new FormData(); - fdLabel.top = new FormAttachment(0, 0); - fdLabel.left = new FormAttachment(0, 0); - fdLabel.right = new FormAttachment(100, 0); - fdLabel.bottom = new FormAttachment(100, 0); - return fdLabel; - } - - /* - * STYLING - */ - - /** Style widget */ - public static T style(T widget, String style) { - if (style == null) - return widget;// does nothing - EclipseUiSpecificUtils.setStyleData(widget, style); - if (widget instanceof Control) { - CmsView cmsView = getCmsView((Control) widget); - if (cmsView != null) - cmsView.applyStyles(widget); - } - return widget; - } - - /** Style widget */ - public static T style(T widget, CmsStyle style) { - return style(widget, style.style()); - } - - /** Enable markups on widget */ - public static T markup(T widget) { - EclipseUiSpecificUtils.setMarkupData(widget); - return widget; - } - - /** Disable markup validation. */ - public static T disableMarkupValidation(T widget) { - EclipseUiSpecificUtils.setMarkupValidationDisabledData(widget); - return widget; - } - - /** - * Apply markup and set text on {@link Label}, {@link Button}, {@link Text}. - * - * @param widget the widget to style and to use in order to display text - * @param txt the object to display via its toString() method. - * This argument should not be null, but if it is null and - * assertions are disabled "" is displayed instead; if - * assertions are enabled the call will fail. - * - * @see markup - */ - public static T text(T widget, Object txt) { - assert txt != null; - String str = txt != null ? txt.toString() : ""; - markup(widget); - if (widget instanceof Label) - ((Label) widget).setText(str); - else if (widget instanceof Button) - ((Button) widget).setText(str); - else if (widget instanceof Text) - ((Text) widget).setText(str); - else - throw new IllegalArgumentException("Unsupported widget type " + widget.getClass()); - return widget; - } - - /** A {@link Label} with markup activated. */ - public static Label lbl(Composite parent, Object txt) { - return text(new Label(parent, SWT.NONE), txt); - } - - /** A read-only {@link Text} whose content can be copy/pasted. */ - public static Text txt(Composite parent, Object txt) { - return text(new Text(parent, SWT.NONE), txt); - } - - /** Dispose all children of a Composite */ - public static void clear(Composite composite) { - if (composite.isDisposed()) - return; - for (Control child : composite.getChildren()) - child.dispose(); - } - - /** Clean reserved URL characters for use in HTTP links. */ - public static String cleanPathForUrl(String path) { - StringTokenizer st = new StringTokenizer(path, "/"); - StringBuilder sb = new StringBuilder(); - while (st.hasMoreElements()) { - sb.append('/'); - String encoded = URLEncoder.encode(st.nextToken(), StandardCharsets.UTF_8); - encoded = encoded.replace("+", "%20"); - sb.append(encoded); - - } - return sb.toString(); - } - - /** Singleton. */ - private CmsSwtUtils() { - } - -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/MouseDoubleClick.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/MouseDoubleClick.java deleted file mode 100644 index b818b06d9..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/MouseDoubleClick.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.argeo.cms.swt; - -import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.events.MouseListener; - -/** - * {@link MouseListener#mouseDoubleClick(MouseEvent)} as a functional interface - * in order to use as a short lambda expression in UI code. - * {@link MouseListener#mouseDownouseEvent)} and - * {@link MouseListener#mouseUp(MouseEvent)} do nothing by default. - */ -@FunctionalInterface -public interface MouseDoubleClick extends MouseListener { - @Override - void mouseDoubleClick(MouseEvent e); - - @Override - default void mouseDown(MouseEvent e) { - // does nothing - } - - @Override - default void mouseUp(MouseEvent e) { - // does nothing - } -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/MouseDown.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/MouseDown.java deleted file mode 100644 index baecb0072..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/MouseDown.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.argeo.cms.swt; - -import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.events.MouseListener; - -/** - * {@link MouseListener#mouseDown(MouseEvent)} as a functional interface in - * order to use as a short lambda expression in UI code. - * {@link MouseListener#mouseDoubleClick(MouseEvent)} and - * {@link MouseListener#mouseUp(MouseEvent)} do nothing by default. - */ -@FunctionalInterface -public interface MouseDown extends MouseListener { - @Override - void mouseDown(MouseEvent e); - - @Override - default void mouseDoubleClick(MouseEvent e) { - // does nothing - } - - @Override - default void mouseUp(MouseEvent e) { - // does nothing - } -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/Selected.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/Selected.java deleted file mode 100644 index 03fbad01e..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/Selected.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.argeo.cms.swt; - -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.SelectionListener; - -/** - * {@link SelectionListener} as a functional interface in order to use as a - * short lambda expression in UI code. - * {@link SelectionListener#widgetDefaultSelected(SelectionEvent)} does nothing - * by default. - */ -@FunctionalInterface -public interface Selected extends SelectionListener { - @Override - public void widgetSelected(SelectionEvent e); - - default public void widgetDefaultSelected(SelectionEvent e) { - // does nothing - } - -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/SimpleSwtUxContext.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/SimpleSwtUxContext.java deleted file mode 100644 index e468c6d52..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/SimpleSwtUxContext.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.argeo.cms.swt; - -import org.argeo.api.cms.ux.UxContext; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.widgets.Display; - -public class SimpleSwtUxContext implements UxContext { - private Point size; - private Point small = new Point(400, 400); - - public SimpleSwtUxContext() { - this(Display.getCurrent().getBounds()); - } - - public SimpleSwtUxContext(Rectangle rect) { - this.size = new Point(rect.width, rect.height); - } - - public SimpleSwtUxContext(Point size) { - this.size = size; - } - - @Override - public boolean isPortrait() { - return size.x >= size.y; - } - - @Override - public boolean isLandscape() { - return size.x < size.y; - } - - @Override - public boolean isSquare() { - return size.x == size.y; - } - - @Override - public boolean isSmall() { - return size.x <= small.x || size.y <= small.y; - } - - @Override - public boolean isMasterData() { - // TODO make it configurable - return true; - } - -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/SwtEditablePart.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/SwtEditablePart.java deleted file mode 100644 index f2cceef07..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/SwtEditablePart.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.argeo.cms.swt; - -import org.argeo.cms.ux.widgets.EditablePart; -import org.eclipse.swt.widgets.Control; - -/** Manages whether an editable or non editable control is shown. */ -public interface SwtEditablePart extends EditablePart { - public Control getControl(); -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/ContentComposite.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/ContentComposite.java deleted file mode 100644 index 951889eee..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/ContentComposite.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.argeo.cms.swt.acr; - -import org.argeo.api.acr.Content; -import org.argeo.api.acr.spi.ProvidedContent; -import org.eclipse.swt.widgets.Composite; - -/** A composite which can (optionally) manage a content. */ -public class ContentComposite extends Composite { - private static final long serialVersionUID = -1447009015451153367L; - - public ContentComposite(Composite parent, int style, Content item) { - super(parent, style); - setData(item); - } - - public Content getContent() { - return (Content) getData(); - } - - @Deprecated - public Content getNode() { - return getContent(); - } - - protected ProvidedContent getProvidedContent() { - return (ProvidedContent) getContent(); - } - - public String getSessionLocalId() { - return getProvidedContent().getSessionLocalId(); - } - - protected void itemUpdated() { - layout(); - } - - public void setContent(Content content) { - setData(content); - itemUpdated(); - } -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtSection.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtSection.java deleted file mode 100644 index 89d003870..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtSection.java +++ /dev/null @@ -1,158 +0,0 @@ -package org.argeo.cms.swt.acr; - -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; - -import org.argeo.api.acr.Content; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ux.widgets.EditablePart; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -/** A structured UI related to a JCR context. */ -public class SwtSection extends ContentComposite { - private static final long serialVersionUID = -5933796173755739207L; - - private final SwtSection parentSection; - private Composite sectionHeader; - private final Integer relativeDepth; - - public SwtSection(Composite parent, int style, Content node) { - this(parent, findSection(parent), style, node); - } - - public SwtSection(SwtSection section, int style, Content node) { - this(section, section, style, node); - } - - protected SwtSection(Composite parent, SwtSection parentSection, int style, Content node) { - super(parent, style, node); - this.parentSection = parentSection; - if (parentSection != null) { - relativeDepth = getProvidedContent().getDepth() - parentSection.getProvidedContent().getDepth(); - } else { - relativeDepth = 0; - } - setLayout(CmsSwtUtils.noSpaceGridLayout()); - } - - public Map getSubSections() { - LinkedHashMap result = new LinkedHashMap(); - for (Control child : getChildren()) { - if (child instanceof Composite) { - collectDirectSubSections((Composite) child, result); - } - } - return Collections.unmodifiableMap(result); - } - - private void collectDirectSubSections(Composite composite, LinkedHashMap subSections) { - if (composite == sectionHeader || composite instanceof EditablePart) - return; - if (composite instanceof SwtSection) { - SwtSection section = (SwtSection) composite; - subSections.put(section.getProvidedContent().getSessionLocalId(), section); - return; - } - - for (Control child : composite.getChildren()) - if (child instanceof Composite) - collectDirectSubSections((Composite) child, subSections); - } - - public Composite createHeader() { - return createHeader(this); - } - - public Composite createHeader(Composite parent) { - if (sectionHeader != null) - sectionHeader.dispose(); - - sectionHeader = new Composite(parent, SWT.NONE); - sectionHeader.setLayoutData(CmsSwtUtils.fillWidth()); - sectionHeader.setLayout(CmsSwtUtils.noSpaceGridLayout()); - // sectionHeader.moveAbove(null); - // layout(); - return sectionHeader; - } - - public Composite getHeader() { - if (sectionHeader != null && sectionHeader.isDisposed()) - sectionHeader = null; - return sectionHeader; - } - - // SECTION PARTS - public SwtSectionPart getSectionPart(String partId) { - for (Control child : getChildren()) { - if (child instanceof SwtSectionPart) { - SwtSectionPart sectionPart = (SwtSectionPart) child; - if (sectionPart.getPartId().equals(partId)) - return sectionPart; - } - } - return null; - } - - public SwtSectionPart nextSectionPart(SwtSectionPart sectionPart) { - Control[] children = getChildren(); - for (int i = 0; i < children.length; i++) { - if (sectionPart == children[i]) { - for (int j = i + 1; j < children.length; j++) { - if (children[i + 1] instanceof SwtSectionPart) { - return (SwtSectionPart) children[i + 1]; - } - } - -// if (i + 1 < children.length) { -// Composite next = (Composite) children[i + 1]; -// return (SectionPart) next; -// } else { -// // next section -// } - } - } - return null; - } - - public SwtSectionPart previousSectionPart(SwtSectionPart sectionPart) { - Control[] children = getChildren(); - for (int i = 0; i < children.length; i++) { - if (sectionPart == children[i]) - if (i != 0) { - Composite previous = (Composite) children[i - 1]; - return (SwtSectionPart) previous; - } else { - // previous section - } - } - return null; - } - - @Override - public String toString() { - if (parentSection == null) - return "Main section " + getContent(); - return "Section " + getContent(); - } - - public SwtSection getParentSection() { - return parentSection; - } - - public Integer getRelativeDepth() { - return relativeDepth; - } - - /** Recursively finds the related section in the parents (can be itself) */ - public static SwtSection findSection(Control control) { - if (control == null) - return null; - if (control instanceof SwtSection) - return (SwtSection) control; - else - return findSection(control.getParent()); - } -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtSectionPart.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtSectionPart.java deleted file mode 100644 index 7fbf4bbca..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtSectionPart.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.argeo.cms.swt.acr; - -import org.argeo.cms.ux.acr.ContentPart; -import org.argeo.cms.ux.widgets.EditablePart; - -/** An editable part dynamically related to a Section */ -public interface SwtSectionPart extends EditablePart, ContentPart { - public String getPartId(); - - public SwtSection getSection(); -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtTabbedArea.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtTabbedArea.java deleted file mode 100644 index cd4e37d19..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtTabbedArea.java +++ /dev/null @@ -1,258 +0,0 @@ -package org.argeo.cms.swt.acr; - -import java.util.ArrayList; -import java.util.List; - -import org.argeo.api.acr.Content; -import org.argeo.api.acr.spi.ProvidedContent; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.swt.Selected; -import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.StackLayout; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.ToolBar; -import org.eclipse.swt.widgets.ToolItem; - -/** Manages {@link SwtSection} in a tab-like structure. */ -public class SwtTabbedArea extends Composite { - private static final long serialVersionUID = 8659669229482033444L; - - private Composite headers; - private Composite body; - - private List sections = new ArrayList<>(); - - private ProvidedContent previousNode; - private SwtUiProvider previousUiProvider; - private SwtUiProvider currentUiProvider; - - private String tabStyle; - private String tabSelectedStyle; - private String bodyStyle; - private Image closeIcon; - - private StackLayout stackLayout; - - private boolean singleTab = false; - - public SwtTabbedArea(Composite parent, int style) { - super(parent, SWT.NONE); - CmsSwtUtils.style(parent, bodyStyle); - - setLayout(CmsSwtUtils.noSpaceGridLayout()); - - // TODO manage tabs at bottom or sides - headers = new Composite(this, SWT.NONE); - headers.setLayoutData(CmsSwtUtils.fillWidth()); - body = new Composite(this, SWT.NONE); - body.setLayoutData(CmsSwtUtils.fillAll()); - // body.setLayout(new FormLayout()); - stackLayout = new StackLayout(); - body.setLayout(stackLayout); - emptyState(); - } - - protected void refreshTabHeaders() { - int tabCount = sections.size() > 0 ? sections.size() : 1; - for (Control tab : headers.getChildren()) - tab.dispose(); - - headers.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(tabCount, true))); - - if (sections.size() == 0) { - Composite emptyHeader = new Composite(headers, SWT.NONE); - emptyHeader.setLayoutData(CmsSwtUtils.fillAll()); - emptyHeader.setLayout(new GridLayout()); - Label lbl = new Label(emptyHeader, SWT.NONE); - lbl.setText(""); - lbl.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false)); - - } - - SwtSection currentSection = getCurrentSection(); - for (SwtSection section : sections) { - boolean selected = section == currentSection; - Composite sectionHeader = section.createHeader(headers); - CmsSwtUtils.style(sectionHeader, selected ? tabSelectedStyle : tabStyle); - int headerColumns = singleTab ? 1 : 2; - sectionHeader.setLayout(new GridLayout(headerColumns, false)); - sectionHeader.setLayout(CmsSwtUtils.noSpaceGridLayout(headerColumns)); - Button title = new Button(sectionHeader, SWT.FLAT); - CmsSwtUtils.style(title, selected ? tabSelectedStyle : tabStyle); - title.setLayoutData(CmsSwtUtils.fillWidth()); - title.addSelectionListener((Selected) (e) -> showTab(tabIndex(section.getNode()))); - Content node = section.getContent(); - - // FIXME find a standard way to display titles - String titleStr = node.getName().getLocalPart(); - - // TODO internationalize - title.setText(titleStr); - if (!singleTab) { - ToolBar toolBar = new ToolBar(sectionHeader, SWT.NONE); - ToolItem closeItem = new ToolItem(toolBar, SWT.FLAT); - if (closeIcon != null) - closeItem.setImage(closeIcon); - else - closeItem.setText("X"); - CmsSwtUtils.style(closeItem, selected ? tabSelectedStyle : tabStyle); - closeItem.addSelectionListener((Selected) (e) -> closeTab(section)); - } - } - - } - - public void view(SwtUiProvider uiProvider, Content context) { - if (body.isDisposed()) - return; - int index = tabIndex(context); - if (index >= 0) { - showTab(index); - previousNode = (ProvidedContent) context; - previousUiProvider = uiProvider; - return; - } - SwtSection section = (SwtSection) body.getChildren()[0]; - previousNode = (ProvidedContent) section.getNode(); - if (previousNode == null) {// empty state - previousNode = (ProvidedContent) context; - previousUiProvider = uiProvider; - } else { - previousUiProvider = currentUiProvider; - } - currentUiProvider = uiProvider; - section.setContent(context); - // section.setLayoutData(CmsUiUtils.coverAll()); - build(section, uiProvider, context); - if (sections.size() == 0) - sections.add(section); - refreshTabHeaders(); - index = tabIndex(context); - showTab(index); - layout(true, true); - } - - public void open(SwtUiProvider uiProvider, Content context) { - if (singleTab) - throw new UnsupportedOperationException("Open is not supported in single tab mode."); - - if (previousNode != null - && previousNode.getSessionLocalId().equals(((ProvidedContent) context).getSessionLocalId())) { - // does nothing - return; - } - if (sections.size() == 0) - CmsSwtUtils.clear(body); - SwtSection currentSection = getCurrentSection(); - int currentIndex = sections.indexOf(currentSection); - SwtSection previousSection = new SwtSection(body, SWT.NONE, context); - build(previousSection, previousUiProvider, previousNode); - // previousSection.setLayoutData(CmsUiUtils.coverAll()); - int newIndex = currentIndex + 1; - sections.add(currentIndex, previousSection); -// sections.add(newIndex, previousSection); - showTab(newIndex); - refreshTabHeaders(); - layout(true, true); - } - - public void showTab(int index) { - SwtSection sectionToShow = sections.get(index); - // sectionToShow.moveAbove(null); - stackLayout.topControl = sectionToShow; - refreshTabHeaders(); - layout(true, true); - } - - protected void build(SwtSection section, SwtUiProvider uiProvider, Content context) { - for (Control child : section.getChildren()) - child.dispose(); - CmsSwtUtils.style(section, bodyStyle); - section.setContent(context); - uiProvider.createUiPart(section, context); - - } - - private int tabIndex(Content context) { - for (int i = 0; i < sections.size(); i++) { - SwtSection section = sections.get(i); - if (section.getSessionLocalId().equals(((ProvidedContent) context).getSessionLocalId())) - return i; - } - return -1; - } - - public void closeTab(SwtSection section) { - int currentIndex = sections.indexOf(section); - int nextIndex = currentIndex == 0 ? 0 : currentIndex - 1; - sections.remove(section); - section.dispose(); - if (sections.size() == 0) { - emptyState(); - refreshTabHeaders(); - layout(true, true); - return; - } - refreshTabHeaders(); - showTab(nextIndex); - } - - public void closeAllTabs() { - for (SwtSection section : sections) { - section.dispose(); - } - sections.clear(); - emptyState(); - refreshTabHeaders(); - layout(true, true); - } - - protected void emptyState() { - new SwtSection(body, SWT.NONE, null); - refreshTabHeaders(); - } - - public Composite getCurrent() { - return getCurrentSection(); - } - - protected SwtSection getCurrentSection() { - return (SwtSection) stackLayout.topControl; - } - - public Content getCurrentContext() { - SwtSection section = getCurrentSection(); - if (section != null) { - return section.getNode(); - } else { - return null; - } - } - - public void setTabStyle(String tabStyle) { - this.tabStyle = tabStyle; - } - - public void setTabSelectedStyle(String tabSelectedStyle) { - this.tabSelectedStyle = tabSelectedStyle; - } - - public void setBodyStyle(String bodyStyle) { - this.bodyStyle = bodyStyle; - } - - public void setCloseIcon(Image closeIcon) { - this.closeIcon = closeIcon; - } - - public void setSingleTab(boolean singleTab) { - this.singleTab = singleTab; - } - -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtUiProvider.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtUiProvider.java deleted file mode 100644 index 4988fc6b8..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtUiProvider.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.argeo.cms.swt.acr; - -import org.argeo.api.acr.Content; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -@FunctionalInterface -public interface SwtUiProvider { - Control createUiPart(Composite parent, Content context); -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/app/AcrContentTreeView.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/app/AcrContentTreeView.java deleted file mode 100644 index fb1a79d44..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/app/AcrContentTreeView.java +++ /dev/null @@ -1,158 +0,0 @@ -package org.argeo.cms.swt.app; - -import static org.argeo.api.acr.NamespaceUtils.toPrefixedName; - -import java.util.ArrayList; -import java.util.List; - -import javax.xml.namespace.QName; - -import org.argeo.api.acr.Content; -import org.argeo.api.acr.NamespaceUtils; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.swt.widgets.SwtHierarchicalPart; -import org.argeo.cms.swt.widgets.SwtTabularPart; -import org.argeo.cms.ux.acr.ContentHierarchicalPart; -import org.argeo.cms.ux.widgets.Column; -import org.argeo.cms.ux.widgets.DefaultTabularPart; -import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.SashForm; -import org.eclipse.swt.widgets.Composite; - -public class AcrContentTreeView extends Composite { - private static final long serialVersionUID = -3707881216246077323L; - - private Content rootContent; - -// private Content selected; - - public AcrContentTreeView(Composite parent, int style, Content content) { - super(parent, style); - this.rootContent = content; - // this.selected = rootContent; - setLayout(CmsSwtUtils.noSpaceGridLayout()); - - SashForm split = new SashForm(this, SWT.HORIZONTAL); - split.setLayoutData(CmsSwtUtils.fillAll()); - - ContentHierarchicalPart contentPart = new ContentHierarchicalPart(); - contentPart.setInput(rootContent); - - SwtHierarchicalPart hPart = new SwtHierarchicalPart<>(split, getStyle(), contentPart); - - Composite area = new Composite(split, SWT.BORDER); - area.setLayout(CmsSwtUtils.noSpaceGridLayout(2)); - split.setWeights(new int[] { 30, 70 }); - - // attributes - DefaultTabularPart attributesPart = new DefaultTabularPart<>() { - - @Override - protected List asList(Content input) { - return new ArrayList<>(input.keySet()); - } - }; - - attributesPart.addColumn(new Column() { - - @Override - public String getText(QName model) { - try { - return NamespaceUtils.toPrefixedName(model); - } catch (IllegalStateException e) { - return model.toString(); - } - } - }); - attributesPart.addColumn(new Column() { - - @Override - public String getText(QName model) { - return attributesPart.getInput().get(model).toString(); - } - - @Override - public int getWidth() { - return 400; - } - - }); - // attributesPart.setInput(selected); - - SwtTabularPart attributeTable = new SwtTabularPart<>(area, style, attributesPart); - attributeTable.setLayoutData(CmsSwtUtils.fillAll()); - - // types - DefaultTabularPart typesPart = new DefaultTabularPart<>() { - - @Override - protected List asList(Content input) { - return input.getContentClasses(); - } - }; - typesPart.addColumn(new Column() { - - @Override - public String getText(QName model) { - return toPrefixedName(model); - } - - }); - - // typesPart.setInput(selected); - - SwtTabularPart typesTable = new SwtTabularPart<>(area, style, typesPart); - typesTable.setLayoutData(CmsSwtUtils.fillAll()); - - // controller - contentPart.setInput(rootContent); - contentPart.onSelected((o) -> { - Content c = (Content) o; -// selected = c; - attributesPart.setInput(c); - typesPart.setInput(c); - }); - - attributesPart.refresh(); - typesPart.refresh(); - } - -// protected void refreshTable() { -// for (TableItem item : table.getItems()) { -// item.dispose(); -// } -// for (QName key : selected.keySet()) { -// TableItem item = new TableItem(table, 0); -// item.setText(0, key.toString()); -// Object value = selected.get(key); -// item.setText(1, value.toString()); -// } -// table.getColumn(0).pack(); -// table.getColumn(1).pack(); -// } - -// public static void main(String[] args) { -// Path basePath; -// if (args.length > 0) { -// basePath = Paths.get(args[0]); -// } else { -// basePath = Paths.get(System.getProperty("user.home")); -// } -// -// final Display display = new Display(); -// final Shell shell = new Shell(display); -// shell.setText(basePath.toString()); -// shell.setLayout(new FillLayout()); -// -// FsContentProvider contentSession = new FsContentProvider("/", basePath); -//// GcrContentTreeView treeView = new GcrContentTreeView(shell, 0, contentSession.get("/")); -// -// shell.setSize(shell.computeSize(800, 600)); -// shell.open(); -// while (!shell.isDisposed()) { -// if (!display.readAndDispatch()) -// display.sleep(); -// } -// display.dispose(); -// } -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/app/CmsUserApp.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/app/CmsUserApp.java deleted file mode 100644 index add6e9edb..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/app/CmsUserApp.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.argeo.cms.swt.app; - -import java.util.HashSet; -import java.util.Set; - -import org.argeo.api.acr.Content; -import org.argeo.api.acr.ContentRepository; -import org.argeo.api.cms.CmsContext; -import org.argeo.api.cms.ux.CmsUi; -import org.argeo.api.cms.ux.CmsView; -import org.argeo.cms.AbstractCmsApp; -import org.argeo.cms.swt.CmsSwtUi; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.swt.auth.CmsLogin; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; - -public class CmsUserApp extends AbstractCmsApp { - private ContentRepository contentRepository; - - @Override - public Set getUiNames() { - Set uiNames = new HashSet<>(); - uiNames.add("login"); - uiNames.add("data"); - return uiNames; - } - - @Override - public CmsUi initUi(Object uiParent) { - Composite parent = (Composite) uiParent; - String uiName = parent.getData(UI_NAME_PROPERTY) != null ? parent.getData(UI_NAME_PROPERTY).toString() : null; - CmsSwtUi cmsUi = new CmsSwtUi(parent, SWT.NONE); - if ("login".equals(uiName)) { - CmsView cmsView = CmsSwtUtils.getCmsView(cmsUi); - CmsLogin cmsLogin = new CmsLogin(cmsView, getCmsContext()); - cmsLogin.createUi(cmsUi); - - } else if ("data".equals(uiName)) { - Content rootContent = contentRepository.get().get("/"); - AcrContentTreeView view = new AcrContentTreeView(cmsUi, 0, rootContent); - view.setLayoutData(CmsSwtUtils.fillAll()); - - } - return cmsUi; - } - - @Override - public void refreshUi(CmsUi cmsUi, String state) { - } - - @Override - public void setState(CmsUi cmsUi, String state) { - // TODO Auto-generated method stub - - } - - public void setContentRepository(ContentRepository contentRepository) { - this.contentRepository = contentRepository; - } - -} \ No newline at end of file diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CmsLogin.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CmsLogin.java deleted file mode 100644 index 6cc410ced..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CmsLogin.java +++ /dev/null @@ -1,338 +0,0 @@ -package org.argeo.cms.swt.auth; - -import static org.argeo.cms.CmsMsg.password; -import static org.argeo.cms.CmsMsg.username; - -import java.io.IOException; -import java.util.List; -import java.util.Locale; - -import javax.security.auth.Subject; -import javax.security.auth.callback.Callback; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.callback.LanguageCallback; -import javax.security.auth.callback.NameCallback; -import javax.security.auth.callback.PasswordCallback; -import javax.security.auth.callback.UnsupportedCallbackException; -import javax.security.auth.login.LoginContext; -import javax.security.auth.login.LoginException; - -import org.argeo.api.cms.CmsAuth; -import org.argeo.api.cms.CmsContext; -import org.argeo.api.cms.CmsLog; -import org.argeo.api.cms.ux.CmsView; -import org.argeo.cms.CmsMsg; -import org.argeo.cms.LocaleUtils; -import org.argeo.cms.auth.RemoteAuthCallback; -import org.argeo.cms.servlet.ServletHttpRequest; -import org.argeo.cms.servlet.ServletHttpResponse; -import org.argeo.cms.swt.CmsStyles; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.eclipse.ui.specific.UiContext; -import org.eclipse.swt.SWT; -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.events.SelectionListener; -import org.eclipse.swt.events.TraverseEvent; -import org.eclipse.swt.events.TraverseListener; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.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.Shell; -import org.eclipse.swt.widgets.Text; - -public class CmsLogin implements CmsStyles, CallbackHandler { - private final static CmsLog log = CmsLog.getLog(CmsLogin.class); - - private Composite parent; - private Text usernameT, passwordT; - private Composite credentialsBlock; - private final SelectionListener loginSelectionListener; - - private final Locale defaultLocale; - private LocaleChoice localeChoice = null; - - private final CmsView cmsView; - - // optional subject to be set explicitly - private Subject subject = null; - - private CmsContext cmsContext; - - public CmsLogin(CmsView cmsView, CmsContext cmsContext) { - this.cmsView = cmsView; - this.cmsContext = cmsContext; - if (this.cmsContext != null) { - defaultLocale = this.cmsContext.getDefaultLocale(); - List locales = this.cmsContext.getLocales(); - if (locales != null) - localeChoice = new LocaleChoice(locales, defaultLocale); - } else { - defaultLocale = Locale.getDefault(); - } - loginSelectionListener = new SelectionListener() { - private static final long serialVersionUID = -8832133363830973578L; - - @Override - public void widgetSelected(SelectionEvent e) { - login(); - } - - @Override - public void widgetDefaultSelected(SelectionEvent e) { - } - }; - } - - protected boolean isAnonymous() { - return cmsView.isAnonymous(); - } - - public final void createUi(Composite parent) { - this.parent = parent; - createContents(parent); - } - - protected void createContents(Composite parent) { - defaultCreateContents(parent); - } - - public final void defaultCreateContents(Composite parent) { - parent.setLayout(CmsSwtUtils.noSpaceGridLayout()); - Composite credentialsBlock = createCredentialsBlock(parent); - if (parent instanceof Shell) { - credentialsBlock.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true)); - } - } - - public final Composite createCredentialsBlock(Composite parent) { - if (isAnonymous()) { - return anonymousUi(parent); - } else { - return userUi(parent); - } - } - - public Composite getCredentialsBlock() { - return credentialsBlock; - } - - protected Composite userUi(Composite parent) { - Locale locale = localeChoice == null ? this.defaultLocale : localeChoice.getSelectedLocale(); - credentialsBlock = new Composite(parent, SWT.NONE); - credentialsBlock.setLayout(new GridLayout()); - // credentialsBlock.setLayoutData(CmsUiUtils.fillAll()); - - specificUserUi(credentialsBlock); - - Label l = new Label(credentialsBlock, SWT.NONE); - CmsSwtUtils.style(l, CMS_USER_MENU_ITEM); - l.setText(CmsMsg.logout.lead(locale)); - GridData lData = CmsSwtUtils.fillWidth(); - lData.widthHint = 120; - l.setLayoutData(lData); - - l.addMouseListener(new MouseAdapter() { - private static final long serialVersionUID = 6444395812777413116L; - - public void mouseDown(MouseEvent e) { - logout(); - } - }); - return credentialsBlock; - } - - /** To be overridden */ - protected void specificUserUi(Composite parent) { - - } - - protected Composite anonymousUi(Composite parent) { - Locale locale = localeChoice == null ? this.defaultLocale : localeChoice.getSelectedLocale(); - // We need a composite for the traversal - credentialsBlock = new Composite(parent, SWT.NONE); - credentialsBlock.setLayout(new GridLayout()); - // credentialsBlock.setLayoutData(CmsUiUtils.fillAll()); - CmsSwtUtils.style(credentialsBlock, CMS_LOGIN_DIALOG); - - Integer textWidth = 120; - if (parent instanceof Shell) - CmsSwtUtils.style(parent, CMS_USER_MENU); - // new Label(this, SWT.NONE).setText(CmsMsg.username.lead()); - usernameT = new Text(credentialsBlock, SWT.BORDER); - usernameT.setMessage(username.lead(locale)); - CmsSwtUtils.style(usernameT, CMS_LOGIN_DIALOG_USERNAME); - GridData gd = CmsSwtUtils.fillWidth(); - gd.widthHint = textWidth; - usernameT.setLayoutData(gd); - - // new Label(this, SWT.NONE).setText(CmsMsg.password.lead()); - passwordT = new Text(credentialsBlock, SWT.BORDER | SWT.PASSWORD); - passwordT.setMessage(password.lead(locale)); - CmsSwtUtils.style(passwordT, CMS_LOGIN_DIALOG_PASSWORD); - gd = CmsSwtUtils.fillWidth(); - gd.widthHint = textWidth; - passwordT.setLayoutData(gd); - - TraverseListener tl = new TraverseListener() { - private static final long serialVersionUID = -1158892811534971856L; - - public void keyTraversed(TraverseEvent e) { - if (e.detail == SWT.TRAVERSE_RETURN) - login(); - } - }; - credentialsBlock.addTraverseListener(tl); - usernameT.addTraverseListener(tl); - passwordT.addTraverseListener(tl); - parent.setTabList(new Control[] { credentialsBlock }); - credentialsBlock.setTabList(new Control[] { usernameT, passwordT }); - - // Button - Button loginButton = new Button(credentialsBlock, SWT.PUSH); - loginButton.setText(CmsMsg.login.lead(locale)); - loginButton.setLayoutData(CmsSwtUtils.fillWidth()); - loginButton.addSelectionListener(loginSelectionListener); - - extendsCredentialsBlock(credentialsBlock, locale, loginSelectionListener); - if (localeChoice != null) - createLocalesBlock(credentialsBlock); - return credentialsBlock; - } - - /** - * To be overridden in order to provide custom login button and other links. - */ - protected void extendsCredentialsBlock(Composite credentialsBlock, Locale selectedLocale, - SelectionListener loginSelectionListener) { - - } - - protected void updateLocale(Locale selectedLocale) { - // save already entered values - String usernameStr = usernameT.getText(); - char[] pwd = passwordT.getTextChars(); - - for (Control child : parent.getChildren()) - child.dispose(); - createContents(parent); - if (parent.getParent() != null) - parent.getParent().layout(true, true); - else - parent.layout(); - usernameT.setText(usernameStr); - passwordT.setTextChars(pwd); - } - - protected Composite createLocalesBlock(final Composite parent) { - Composite c = new Composite(parent, SWT.NONE); - CmsSwtUtils.style(c, CMS_USER_MENU_ITEM); - c.setLayout(CmsSwtUtils.noSpaceGridLayout()); - c.setLayoutData(CmsSwtUtils.fillAll()); - - SelectionListener selectionListener = new SelectionAdapter() { - private static final long serialVersionUID = 4891637813567806762L; - - public void widgetSelected(SelectionEvent event) { - Button button = (Button) event.widget; - if (button.getSelection()) { - localeChoice.setSelectedIndex((Integer) event.widget.getData()); - updateLocale(localeChoice.getSelectedLocale()); - } - }; - }; - - List locales = localeChoice.getLocales(); - for (Integer i = 0; i < locales.size(); i++) { - Locale locale = locales.get(i); - Button button = new Button(c, SWT.RADIO); - CmsSwtUtils.style(button, CMS_USER_MENU_ITEM); - button.setData(i); - button.setText(LocaleUtils.toLead(locale.getDisplayName(locale), locale) + " (" + locale + ")"); - // button.addListener(SWT.Selection, listener); - button.addSelectionListener(selectionListener); - if (i == localeChoice.getSelectedIndex()) - button.setSelection(true); - } - return c; - } - - protected boolean login() { - // TODO use CmsVie in order to retrieve subject? - // Subject subject = cmsView.getLoginContext().getSubject(); - // LoginContext loginContext = cmsView.getLoginContext(); - try { - // - // LOGIN - // - // loginContext.logout(); - LoginContext loginContext; - if (subject == null) - loginContext = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER, this); - else - loginContext = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER, subject, this); - loginContext.login(); - cmsView.authChange(loginContext); - return true; - } catch (LoginException e) { - if (log.isTraceEnabled()) - log.warn("Login failed: " + e.getMessage(), e); - else - log.warn("Login failed: " + e.getMessage()); - - try { - Thread.sleep(3000); - } catch (InterruptedException e2) { - // silent - } - // ErrorFeedback.show("Login failed", e); - return false; - } - // catch (LoginException e) { - // log.error("Cannot login", e); - // return false; - // } - } - - - protected void logout() { - cmsView.logout(); - cmsView.navigateTo("~"); - } - - @Override - public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { - for (Callback callback : callbacks) { - if (callback instanceof NameCallback && usernameT != null) - ((NameCallback) callback).setName(usernameT.getText()); - else if (callback instanceof PasswordCallback && passwordT != null) - ((PasswordCallback) callback).setPassword(passwordT.getTextChars()); - else if (callback instanceof RemoteAuthCallback) { - ((RemoteAuthCallback) callback).setRequest(new ServletHttpRequest(UiContext.getHttpRequest())); - ((RemoteAuthCallback) callback).setResponse(new ServletHttpResponse(UiContext.getHttpResponse())); - } else if (callback instanceof LanguageCallback) { - Locale toUse = null; - if (localeChoice != null) - toUse = localeChoice.getSelectedLocale(); - else if (defaultLocale != null) - toUse = defaultLocale; - - if (toUse != null) { - ((LanguageCallback) callback).setLocale(toUse); - UiContext.setLocale(toUse); - } - - } - } - } - - public void setSubject(Subject subject) { - this.subject = subject; - } - -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CmsLoginShell.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CmsLoginShell.java deleted file mode 100644 index 39cf82afc..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CmsLoginShell.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.argeo.cms.swt.auth; - -import org.argeo.api.cms.CmsContext; -import org.argeo.api.cms.ux.CmsView; -import org.argeo.cms.swt.CmsSwtUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Shell; - -/** The site-related user menu */ -public class CmsLoginShell extends CmsLogin { - private final Shell shell; - - public CmsLoginShell(CmsView cmsView, CmsContext cmsContext) { - super(cmsView, cmsContext); - shell = createShell(); -// createUi(shell); - } - - /** To be overridden. */ - protected Shell createShell() { - Shell shell = new Shell(Display.getCurrent(), SWT.NO_TRIM); - shell.setMaximized(true); - return shell; - } - - /** To be overridden. */ - public void open() { - CmsSwtUtils.style(shell, CMS_USER_MENU); - shell.open(); - } - - @Override - protected boolean login() { - boolean success = false; - try { - success = super.login(); - return success; - } finally { - if (success) - closeShell(); - else { - for (Control child : shell.getChildren()) - child.dispose(); - createUi(shell); - shell.layout(); - // TODO error message - } - } - } - - @Override - protected void logout() { - closeShell(); - super.logout(); - } - - protected void closeShell() { - if (!shell.isDisposed()) { - shell.close(); - shell.dispose(); - } - } - - public Shell getShell() { - return shell; - } - - public void createUi() { - createUi(shell); - } -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CompositeCallbackHandler.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CompositeCallbackHandler.java deleted file mode 100644 index 495007cb2..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CompositeCallbackHandler.java +++ /dev/null @@ -1,273 +0,0 @@ -package org.argeo.cms.swt.auth; - -import java.io.IOException; -import java.util.Arrays; - -import javax.security.auth.callback.Callback; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.callback.NameCallback; -import javax.security.auth.callback.PasswordCallback; -import javax.security.auth.callback.TextOutputCallback; -import javax.security.auth.callback.UnsupportedCallbackException; - -import org.eclipse.swt.SWT; -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.SelectionEvent; -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Combo; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; - -/** - * A composite that can populate itself based on {@link Callback}s. It can be - * used directly as a {@link CallbackHandler} or be used by one by calling the - * {@link #createCallbackHandlers(Callback[])}. Supported standard - * {@link Callback}s are:
- *
    - *
  • {@link PasswordCallback}
  • - *
  • {@link NameCallback}
  • - *
  • {@link TextOutputCallback}
  • - *
- * Supported Argeo {@link Callback}s are:
- *
    - *
  • {@link LocaleChoice}
  • - *
- */ -public class CompositeCallbackHandler extends Composite implements CallbackHandler { - private static final long serialVersionUID = -928223893722723777L; - - private boolean wasUsedAlready = false; - private boolean isSubmitted = false; - private boolean isCanceled = false; - - public CompositeCallbackHandler(Composite parent, int style) { - super(parent, style); - } - - @Override - public synchronized void handle(final Callback[] callbacks) throws IOException, UnsupportedCallbackException { - // reset - if (wasUsedAlready && !isSubmitted() && !isCanceled()) { - cancel(); - for (Control control : getChildren()) - control.dispose(); - isSubmitted = false; - isCanceled = false; - } - - for (Callback callback : callbacks) - checkCallbackSupported(callback); - // create controls synchronously in the UI thread - getDisplay().syncExec(new Runnable() { - - @Override - public void run() { - createCallbackHandlers(callbacks); - } - }); - - if (!wasUsedAlready) - wasUsedAlready = true; - - // while (!isSubmitted() && !isCanceled()) { - // try { - // wait(1000l); - // } catch (InterruptedException e) { - // // silent - // } - // } - - // cleanCallbacksAfterCancel(callbacks); - } - - public void checkCallbackSupported(Callback callback) throws UnsupportedCallbackException { - if (callback instanceof TextOutputCallback || callback instanceof NameCallback - || callback instanceof PasswordCallback || callback instanceof LocaleChoice) { - return; - } else { - throw new UnsupportedCallbackException(callback); - } - } - - /** - * Set writable callbacks to null if the handle is canceled (check is done - * by the method) - */ - public void cleanCallbacksAfterCancel(Callback[] callbacks) { - if (isCanceled()) { - for (Callback callback : callbacks) { - if (callback instanceof NameCallback) { - ((NameCallback) callback).setName(null); - } else if (callback instanceof PasswordCallback) { - PasswordCallback pCallback = (PasswordCallback) callback; - char[] arr = pCallback.getPassword(); - if (arr != null) { - Arrays.fill(arr, '*'); - pCallback.setPassword(null); - } - } - } - } - } - - public void createCallbackHandlers(Callback[] callbacks) { - Composite composite = this; - for (int i = 0; i < callbacks.length; i++) { - Callback callback = callbacks[i]; - if (callback instanceof TextOutputCallback) { - createLabelTextoutputHandler(composite, (TextOutputCallback) callback); - } else if (callback instanceof NameCallback) { - createNameHandler(composite, (NameCallback) callback); - } else if (callback instanceof PasswordCallback) { - createPasswordHandler(composite, (PasswordCallback) callback); - } else if (callback instanceof LocaleChoice) { - createLocaleHandler(composite, (LocaleChoice) callback); - } - } - } - - protected Text createNameHandler(Composite composite, final NameCallback callback) { - Label label = new Label(composite, SWT.NONE); - label.setText(callback.getPrompt()); - final Text text = new Text(composite, SWT.SINGLE | SWT.LEAD | SWT.BORDER); - if (callback.getDefaultName() != null) { - // set default value, if provided - text.setText(callback.getDefaultName()); - callback.setName(callback.getDefaultName()); - } - text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - text.addModifyListener(new ModifyListener() { - private static final long serialVersionUID = 7300032545287292973L; - - public void modifyText(ModifyEvent event) { - callback.setName(text.getText()); - } - }); - text.addSelectionListener(new SelectionListener() { - private static final long serialVersionUID = 1820530045857665111L; - - @Override - public void widgetSelected(SelectionEvent e) { - } - - @Override - public void widgetDefaultSelected(SelectionEvent e) { - submit(); - } - }); - - text.addKeyListener(new KeyListener() { - private static final long serialVersionUID = -8698107785092095713L; - - @Override - public void keyReleased(KeyEvent e) { - } - - @Override - public void keyPressed(KeyEvent e) { - } - }); - return text; - } - - protected Text createPasswordHandler(Composite composite, final PasswordCallback callback) { - Label label = new Label(composite, SWT.NONE); - label.setText(callback.getPrompt()); - final Text passwordText = new Text(composite, SWT.SINGLE | SWT.LEAD | SWT.PASSWORD | SWT.BORDER); - passwordText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - passwordText.addModifyListener(new ModifyListener() { - private static final long serialVersionUID = -7099363995047686732L; - - public void modifyText(ModifyEvent event) { - callback.setPassword(passwordText.getTextChars()); - } - }); - passwordText.addSelectionListener(new SelectionListener() { - private static final long serialVersionUID = 1820530045857665111L; - - @Override - public void widgetSelected(SelectionEvent e) { - } - - @Override - public void widgetDefaultSelected(SelectionEvent e) { - submit(); - } - }); - return passwordText; - } - - protected Combo createLocaleHandler(Composite composite, final LocaleChoice callback) { - String[] labels = callback.getSupportedLocalesLabels(); - if (labels.length == 0) - return null; - Label label = new Label(composite, SWT.NONE); - label.setText("Language"); - - final Combo combo = new Combo(composite, SWT.READ_ONLY); - combo.setItems(labels); - combo.select(callback.getDefaultIndex()); - combo.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - combo.addSelectionListener(new SelectionListener() { - private static final long serialVersionUID = 38678989091946277L; - - @Override - public void widgetSelected(SelectionEvent e) { - callback.setSelectedIndex(combo.getSelectionIndex()); - } - - @Override - public void widgetDefaultSelected(SelectionEvent e) { - } - }); - return combo; - } - - protected Label createLabelTextoutputHandler(Composite composite, final TextOutputCallback callback) { - Label label = new Label(composite, SWT.NONE); - label.setText(callback.getMessage()); - GridData data = new GridData(SWT.FILL, SWT.FILL, true, true); - data.horizontalSpan = 2; - label.setLayoutData(data); - return label; - // TODO: find a way to pass this information - // int messageType = callback.getMessageType(); - // int dialogMessageType = IMessageProvider.NONE; - // switch (messageType) { - // case TextOutputCallback.INFORMATION: - // dialogMessageType = IMessageProvider.INFORMATION; - // break; - // case TextOutputCallback.WARNING: - // dialogMessageType = IMessageProvider.WARNING; - // break; - // case TextOutputCallback.ERROR: - // dialogMessageType = IMessageProvider.ERROR; - // break; - // } - // setMessage(callback.getMessage(), dialogMessageType); - } - - synchronized boolean isSubmitted() { - return isSubmitted; - } - - synchronized boolean isCanceled() { - return isCanceled; - } - - protected synchronized void submit() { - isSubmitted = true; - notifyAll(); - } - - protected synchronized void cancel() { - isCanceled = true; - notifyAll(); - } -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/DynamicCallbackHandler.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/DynamicCallbackHandler.java deleted file mode 100644 index b0c36c602..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/DynamicCallbackHandler.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.argeo.cms.swt.auth; - -import java.io.IOException; - -import javax.security.auth.callback.Callback; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.callback.UnsupportedCallbackException; - -import org.argeo.eclipse.ui.dialogs.LightweightDialog; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Shell; - -public class DynamicCallbackHandler implements CallbackHandler { - - @Override - public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { - Shell activeShell = Display.getCurrent().getActiveShell(); - LightweightDialog dialog = new LightweightDialog(activeShell) { - - @Override - protected Control createDialogArea(Composite parent) { - CompositeCallbackHandler cch = new CompositeCallbackHandler(parent, SWT.NONE); - cch.createCallbackHandlers(callbacks); - return cch; - } - }; - dialog.setBlockOnOpen(true); - dialog.open(); - } - -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/LocaleChoice.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/LocaleChoice.java deleted file mode 100644 index 3ce5ae516..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/LocaleChoice.java +++ /dev/null @@ -1,86 +0,0 @@ -package org.argeo.cms.swt.auth; - -import java.util.Collections; -import java.util.List; -import java.util.Locale; - -import javax.security.auth.callback.LanguageCallback; - -import org.argeo.cms.swt.CmsException; - -/** Choose in a list of locales. TODO: replace with {@link LanguageCallback} */ -@Deprecated -public class LocaleChoice { - private final List locales; - - private Integer selectedIndex = null; - private final Integer defaultIndex; - - public LocaleChoice(List locales, Locale defaultLocale) { - Integer defaultIndex = null; - this.locales = Collections.unmodifiableList(locales); - for (int i = 0; i < locales.size(); i++) - if (locales.get(i).equals(defaultLocale)) - defaultIndex = i; - - // based on language only - if (defaultIndex == null) - for (int i = 0; i < locales.size(); i++) - if (locales.get(i).getLanguage().equals(defaultLocale.getLanguage())) - defaultIndex = i; - - if (defaultIndex == null) - throw new CmsException("Default locale " + defaultLocale + " is not in available locales " + locales); - this.defaultIndex = defaultIndex; - - this.selectedIndex = defaultIndex; - } - -// /** -// * Convenience constructor based on a comma separated list of iso codes (en, -// * en_US, fr_CA, etc.). Default selection is default locale. -// */ -// public LocaleChoice(String locales, Locale defaultLocale) { -// this(LocaleUtils.asLocaleList(locales), defaultLocale); -// } - - public String[] getSupportedLocalesLabels() { - String[] labels = new String[locales.size()]; - for (int i = 0; i < locales.size(); i++) { - Locale locale = locales.get(i); - if (locale.getCountry().equals("")) - labels[i] = locale.getDisplayLanguage(locale) + " [" + locale.getLanguage() + "]"; - else - labels[i] = locale.getDisplayLanguage(locale) + " (" + locale.getDisplayCountry(locale) + ") [" - + locale.getLanguage() + "_" + locale.getCountry() + "]"; - - } - return labels; - } - - public Locale getSelectedLocale() { - if (selectedIndex == null) - return null; - return locales.get(selectedIndex); - } - - public void setSelectedIndex(Integer selectedIndex) { - this.selectedIndex = selectedIndex; - } - - public Integer getSelectedIndex() { - return selectedIndex; - } - - public Integer getDefaultIndex() { - return defaultIndex; - } - - public List getLocales() { - return locales; - } - - public Locale getDefaultLocale() { - return locales.get(getDefaultIndex()); - } -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/package-info.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/package-info.java deleted file mode 100644 index b431423d8..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Argeo CMS authentication widgets, based on SWT. */ -package org.argeo.cms.swt.auth; \ No newline at end of file diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/ChangePasswordDialog.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/ChangePasswordDialog.java deleted file mode 100644 index 06e4d0f9f..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/ChangePasswordDialog.java +++ /dev/null @@ -1,83 +0,0 @@ -package org.argeo.cms.swt.dialogs; - -import java.security.PrivilegedAction; -import java.util.Arrays; - -import org.argeo.api.cms.CmsLog; -import org.argeo.api.cms.ux.CmsView; -import org.argeo.cms.CmsMsg; -import org.argeo.cms.CmsUserManager; -import org.argeo.cms.swt.CmsSwtUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Text; - -/** Dialog to change a password. */ -public class ChangePasswordDialog extends CmsMessageDialog { - private final static CmsLog log = CmsLog.getLog(ChangePasswordDialog.class); - - private CmsUserManager cmsUserManager; - private CmsView cmsView; - - private PrivilegedAction doIt; - - public ChangePasswordDialog(Shell parentShell, String message, int kind, CmsUserManager cmsUserManager) { - super(parentShell, message, kind); - this.cmsUserManager = cmsUserManager; - cmsView = CmsSwtUtils.getCmsView(parentShell); - } - - @Override - protected Control createInputArea(Composite userSection) { - addFormLabel(userSection, CmsMsg.currentPassword.lead()); - Text previousPassword = new Text(userSection, SWT.BORDER | SWT.PASSWORD); - previousPassword.setLayoutData(CmsSwtUtils.fillWidth()); - addFormLabel(userSection, CmsMsg.newPassword.lead()); - Text newPassword = new Text(userSection, SWT.BORDER | SWT.PASSWORD); - newPassword.setLayoutData(CmsSwtUtils.fillWidth()); - addFormLabel(userSection, CmsMsg.repeatNewPassword.lead()); - Text confirmPassword = new Text(userSection, SWT.BORDER | SWT.PASSWORD); - confirmPassword.setLayoutData(CmsSwtUtils.fillWidth()); - - doIt = () -> { - if (Arrays.equals(newPassword.getTextChars(), confirmPassword.getTextChars())) { - try { - cmsUserManager.changeOwnPassword(previousPassword.getTextChars(), newPassword.getTextChars()); - return OK; - } catch (Exception e1) { - log.error("Could not change password", e1); - cancel(); - CmsMessageDialog.openError(CmsMsg.invalidPassword.lead()); - return CANCEL; - } - } else { - cancel(); - CmsMessageDialog.openError(CmsMsg.repeatNewPassword.lead()); - return CANCEL; - } - }; - - pack(); - return previousPassword; - } - - @Override - protected void okPressed() { - Integer returnCode = cmsView.doAs(doIt); - if (returnCode.equals(OK)) { - super.okPressed(); - CmsMessageDialog.openInformation(CmsMsg.passwordChanged.lead()); - } - } - - private static Label addFormLabel(Composite parent, String label) { - Label lbl = new Label(parent, SWT.WRAP); - lbl.setText(label); -// CmsUiUtils.style(lbl, SuiteStyle.simpleLabel); - return lbl; - } - -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/CmsFeedback.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/CmsFeedback.java deleted file mode 100644 index a01c919e9..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/CmsFeedback.java +++ /dev/null @@ -1,100 +0,0 @@ -package org.argeo.cms.swt.dialogs; - -import java.io.PrintWriter; -import java.io.StringWriter; - -import org.argeo.api.cms.CmsLog; -import org.argeo.cms.CmsMsg; -import org.argeo.cms.swt.Selected; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Text; - -/** A dialog feedback based on a {@link LightweightDialog}. */ -public class CmsFeedback extends LightweightDialog { - private final static CmsLog log = CmsLog.getLog(CmsFeedback.class); - - private String message; - private Throwable exception; - - public CmsFeedback(Shell parentShell, String message, Throwable e) { - super(parentShell); - this.message = message; - this.exception = e; - log.error(message, e); - } - - public static CmsFeedback show(String message, Throwable e) { - // rethrow ThreaDeath in order to make sure that RAP will properly clean - // up the UI thread - if (e instanceof ThreadDeath) - throw (ThreadDeath) e; - - try { - CmsFeedback cmsFeedback = new CmsFeedback(null, message, e); - cmsFeedback.setBlockOnOpen(false); - cmsFeedback.open(); - return cmsFeedback; - } catch (Throwable e1) { - log.error("Cannot open error feedback (" + e.getMessage() + "), original error below", e); - return null; - } - } - - public static CmsFeedback show(String message) { - CmsFeedback cmsFeedback = new CmsFeedback(null, message, null); - cmsFeedback.open(); - return cmsFeedback; - } - - /** Tries to find a display */ - // private static Display getDisplay() { - // try { - // Display display = Display.getCurrent(); - // if (display != null) - // return display; - // else - // return Display.getDefault(); - // } catch (Exception e) { - // return Display.getCurrent(); - // } - // } - - protected Control createDialogArea(Composite parent) { - parent.setLayout(new GridLayout(2, false)); - - Label messageLbl = new Label(parent, SWT.WRAP); - if (message != null) - messageLbl.setText(message); - else if (exception != null) - messageLbl.setText(exception.getLocalizedMessage()); - - Button close = new Button(parent, SWT.FLAT); - close.setText(CmsMsg.close.lead()); - close.setLayoutData(new GridData(SWT.END, SWT.TOP, false, false)); - close.addSelectionListener((Selected) (e) -> closeShell(OK)); - - // Composite composite = new Composite(dialogarea, SWT.NONE); - // composite.setLayout(new GridLayout(2, false)); - // composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - - if (exception != null) { - Text stack = new Text(parent, SWT.MULTI | SWT.LEAD | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL); - stack.setEditable(false); - stack.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1)); - StringWriter sw = new StringWriter(); - exception.printStackTrace(new PrintWriter(sw)); - stack.setText(sw.toString()); - } - - // parent.pack(); - return messageLbl; - } - -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/CmsMessageDialog.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/CmsMessageDialog.java deleted file mode 100644 index 66e640595..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/CmsMessageDialog.java +++ /dev/null @@ -1,167 +0,0 @@ -package org.argeo.cms.swt.dialogs; - -import org.argeo.cms.CmsMsg; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.swt.Selected; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.TraverseEvent; -import org.eclipse.swt.events.TraverseListener; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Shell; - -/** Base class for dialogs displaying messages or small forms. */ -public class CmsMessageDialog extends LightweightDialog { - public final static int NONE = 0; - public final static int ERROR = 1; - public final static int INFORMATION = 2; - public final static int QUESTION = 3; - public final static int WARNING = 4; - public final static int CONFIRM = 5; - public final static int QUESTION_WITH_CANCEL = 6; - - private int kind; - private String message; - - public CmsMessageDialog(Shell parentShell, String message, int kind) { - super(parentShell); - this.kind = kind; - this.message = message; - } - - protected Control createDialogArea(Composite parent) { - parent.setLayout(new GridLayout()); - - TraverseListener traverseListener = new TraverseListener() { - private static final long serialVersionUID = -1158892811534971856L; - - public void keyTraversed(TraverseEvent e) { - if (e.detail == SWT.TRAVERSE_RETURN) - okPressed(); - else if (e.detail == SWT.TRAVERSE_ESCAPE) - cancelPressed(); - } - }; - - // message - Composite body = new Composite(parent, SWT.NONE); - body.addTraverseListener(traverseListener); - GridLayout bodyGridLayout = new GridLayout(); - bodyGridLayout.marginHeight = 20; - bodyGridLayout.marginWidth = 20; - body.setLayout(bodyGridLayout); - body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - - if (message != null) { - Label messageLbl = new Label(body, SWT.WRAP); - CmsSwtUtils.markup(messageLbl); - messageLbl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); - messageLbl.setFont(EclipseUiUtils.getBoldFont(parent)); - messageLbl.setText(message); - } - - // buttons - Composite buttons = new Composite(parent, SWT.NONE); - buttons.addTraverseListener(traverseListener); - buttons.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false)); - if (kind == INFORMATION || kind == WARNING || kind == ERROR || kind == ERROR) { - GridLayout layout = new GridLayout(1, true); - layout.marginWidth = 0; - layout.marginHeight = 0; - buttons.setLayout(layout); - - Button close = new Button(buttons, SWT.FLAT); - close.setText(CmsMsg.close.lead()); - close.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); - close.addSelectionListener((Selected) (e) -> closeShell(OK)); - close.setFocus(); - close.addTraverseListener(traverseListener); - - buttons.setTabList(new Control[] { close }); - } else if (kind == CONFIRM || kind == QUESTION || kind == QUESTION_WITH_CANCEL) { - Control input = createInputArea(body); - if (input != null) { - input.addTraverseListener(traverseListener); - body.setTabList(new Control[] { input }); - } - GridLayout layout = new GridLayout(2, true); - layout.marginWidth = 0; - layout.marginHeight = 0; - buttons.setLayout(layout); - - Button cancel = new Button(buttons, SWT.FLAT); - cancel.setText(CmsMsg.cancel.lead()); - cancel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); - cancel.addSelectionListener((Selected) (e) -> cancelPressed()); - cancel.addTraverseListener(traverseListener); - - Button ok = new Button(buttons, SWT.FLAT); - ok.setText(CmsMsg.ok.lead()); - ok.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); - ok.addSelectionListener((Selected) (e) -> okPressed()); - ok.addTraverseListener(traverseListener); - if (input == null) - ok.setFocus(); - else - input.setFocus(); - - buttons.setTabList(new Control[] { ok, cancel }); - } - // pack(); - parent.setTabList(new Control[] { body, buttons }); - return body; - } - - protected Control createInputArea(Composite parent) { - return null; - } - - protected void okPressed() { - closeShell(OK); - } - - protected void cancelPressed() { - closeShell(CANCEL); - } - - protected void cancel() { - closeShell(CANCEL); - } - - protected Point getInitialSize() { - return new Point(400, 200); - } - - public static boolean open(int kind, Shell parent, String message) { - CmsMessageDialog dialog = new CmsMessageDialog(parent, message, kind); - return dialog.open() == 0; - } - - public static boolean openConfirm(String message) { - return open(CONFIRM, Display.getCurrent().getActiveShell(), message); - } - - public static void openInformation(String message) { - open(INFORMATION, Display.getCurrent().getActiveShell(), message); - } - - public static boolean openQuestion(String message) { - return open(QUESTION, Display.getCurrent().getActiveShell(), message); - } - - public static void openWarning(String message) { - open(WARNING, Display.getCurrent().getActiveShell(), message); - } - - public static void openError(String message) { - open(ERROR, Display.getCurrent().getActiveShell(), message); - } - -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/LightweightDialog.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/LightweightDialog.java deleted file mode 100644 index bf6417bea..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/LightweightDialog.java +++ /dev/null @@ -1,255 +0,0 @@ -package org.argeo.cms.swt.dialogs; - -import org.argeo.api.cms.CmsLog; -import org.argeo.eclipse.ui.EclipseUiException; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.FocusEvent; -import org.eclipse.swt.events.FocusListener; -import org.eclipse.swt.events.ShellAdapter; -import org.eclipse.swt.events.ShellEvent; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Shell; - -/** Generic lightweight dialog, not based on JFace. */ -public class LightweightDialog { - private final static CmsLog log = CmsLog.getLog(LightweightDialog.class); - - // must be the same value as org.eclipse.jface.window.Window#OK - public final static int OK = 0; - // must be the same value as org.eclipse.jface.window.Window#CANCEL - public final static int CANCEL = 1; - - private Shell parentShell; - private Shell backgroundShell; - private Shell foregoundShell; - - private Integer returnCode = null; - private boolean block = true; - - private String title; - - /** Tries to find a display */ - private static Display getDisplay() { - try { - Display display = Display.getCurrent(); - if (display != null) - return display; - else - return Display.getDefault(); - } catch (Exception e) { - return Display.getCurrent(); - } - } - - public LightweightDialog(Shell parentShell) { - this.parentShell = parentShell; - } - - public int open() { - if (foregoundShell != null) - throw new EclipseUiException("There is already a shell"); - backgroundShell = new Shell(parentShell, SWT.ON_TOP); - backgroundShell.setFullScreen(true); - // if (parentShell != null) { - // backgroundShell.setBounds(parentShell.getBounds()); - // } else - // backgroundShell.setMaximized(true); - backgroundShell.setAlpha(128); - backgroundShell.setBackground(getDisplay().getSystemColor(SWT.COLOR_BLACK)); - foregoundShell = new Shell(backgroundShell, SWT.NO_TRIM | SWT.ON_TOP); - if (title != null) - setTitle(title); - foregoundShell.setLayout(new GridLayout()); - foregoundShell.setSize(getInitialSize()); - createDialogArea(foregoundShell); - // shell.pack(); - // shell.layout(); - - Rectangle shellBounds = parentShell != null ? parentShell.getBounds() : Display.getCurrent().getBounds();// RAP - Point dialogSize = foregoundShell.getSize(); - int x = shellBounds.x + (shellBounds.width - dialogSize.x) / 2; - int y = shellBounds.y + (shellBounds.height - dialogSize.y) / 2; - foregoundShell.setLocation(x, y); - - foregoundShell.addShellListener(new ShellAdapter() { - private static final long serialVersionUID = -2701270481953688763L; - - @Override - public void shellDeactivated(ShellEvent e) { - if (hasChildShells()) - return; - if (returnCode == null)// not yet closed - closeShell(CANCEL); - } - - @Override - public void shellClosed(ShellEvent e) { - notifyClose(); - } - - }); - - backgroundShell.open(); - foregoundShell.open(); - // after the foreground shell has been opened - backgroundShell.addFocusListener(new FocusListener() { - private static final long serialVersionUID = 3137408447474661070L; - - @Override - public void focusLost(FocusEvent event) { - } - - @Override - public void focusGained(FocusEvent event) { - if (hasChildShells()) - return; - if (returnCode == null)// not yet closed - closeShell(CANCEL); - } - }); - - if (block) { - block(); - } - if (returnCode == null) - returnCode = OK; - return returnCode; - } - - public void block() { - try { - runEventLoop(foregoundShell); - } catch (ThreadDeath t) { - returnCode = CANCEL; - if (log.isTraceEnabled()) - log.error("Thread death, canceling dialog", t); - } catch (Throwable t) { - returnCode = CANCEL; - log.error("Cannot open blocking lightweight dialog", t); - } - } - - private boolean hasChildShells() { - if (foregoundShell == null) - return false; - return foregoundShell.getShells().length != 0; - } - - // public synchronized int openAndWait() { - // open(); - // while (returnCode == null) - // try { - // wait(100); - // } catch (InterruptedException e) { - // // silent - // } - // return returnCode; - // } - - private synchronized void notifyClose() { - if (returnCode == null) - returnCode = CANCEL; - notifyAll(); - } - - protected void closeShell(int returnCode) { - this.returnCode = returnCode; - if (CANCEL == returnCode) - onCancel(); - if (foregoundShell != null && !foregoundShell.isDisposed()) { - foregoundShell.close(); - foregoundShell.dispose(); - foregoundShell = null; - } - - if (backgroundShell != null && !backgroundShell.isDisposed()) { - backgroundShell.close(); - backgroundShell.dispose(); - } - } - - protected Point getInitialSize() { - // if (exception != null) - // return new Point(800, 600); - // else - return new Point(600, 400); - } - - protected Control createDialogArea(Composite parent) { - Composite dialogarea = new Composite(parent, SWT.NONE); - dialogarea.setLayout(new GridLayout()); - dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - return dialogarea; - } - - protected Shell getBackgroundShell() { - return backgroundShell; - } - - protected Shell getForegoundShell() { - return foregoundShell; - } - - public void setBlockOnOpen(boolean shouldBlock) { - block = shouldBlock; - } - - public void pack() { - foregoundShell.pack(); - } - - private void runEventLoop(Shell loopShell) { - Display display; - if (foregoundShell == null) { - display = Display.getCurrent(); - } else { - display = loopShell.getDisplay(); - } - - while (loopShell != null && !loopShell.isDisposed()) { - try { - if (!display.readAndDispatch()) { - display.sleep(); - } - } catch (UnsupportedOperationException e) { - throw e; - } catch (Throwable e) { - handleException(e); - } - } - if (!display.isDisposed()) - display.update(); - } - - protected void handleException(Throwable t) { - if (t instanceof ThreadDeath) { - // Don't catch ThreadDeath as this is a normal occurrence when - // the thread dies - throw (ThreadDeath) t; - } - // Try to keep running. - t.printStackTrace(); - } - - /** @return false, if the dialog should not be closed. */ - protected boolean onCancel() { - return true; - } - - public void setTitle(String title) { - this.title = title; - if (title != null && getForegoundShell() != null) - getForegoundShell().setText(title); - } - - public Integer getReturnCode() { - return returnCode; - } - -} \ No newline at end of file diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/SingleValueDialog.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/SingleValueDialog.java deleted file mode 100644 index 9404b81da..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/SingleValueDialog.java +++ /dev/null @@ -1,82 +0,0 @@ -package org.argeo.cms.swt.dialogs; - -import org.eclipse.jface.window.Window; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Text; - -/** A dialog asking a for a single value. */ -public class SingleValueDialog extends CmsMessageDialog { - private Text valueT; - private String value; - private String defaultValue; - - public SingleValueDialog(Shell parentShell, String message) { - super(parentShell, message, QUESTION); - } - - public SingleValueDialog(Shell parentShell, String message, String defaultValue) { - super(parentShell, message, QUESTION); - this.defaultValue = defaultValue; - } - - @Override - protected Control createInputArea(Composite parent) { - valueT = new Text(parent, SWT.LEAD | SWT.BORDER | SWT.SINGLE); - valueT.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true)); - if (defaultValue != null) - valueT.setText(defaultValue); - return valueT; - } - - @Override - protected void okPressed() { - value = valueT.getText(); - super.okPressed(); - } - - public String getString() { - return value; - } - - public Long getLong() { - return Long.valueOf(getString()); - } - - public Double getDouble() { - return Double.valueOf(getString()); - } - - public static String ask(String message) { - return ask(message, null); - } - - public static String ask(String message, String defaultValue) { - SingleValueDialog svd = new SingleValueDialog(Display.getCurrent().getActiveShell(), message, defaultValue); - if (svd.open() == Window.OK) - return svd.getString(); - else - return null; - } - - public static Long askLong(String message) { - SingleValueDialog svd = new SingleValueDialog(Display.getCurrent().getActiveShell(), message); - if (svd.open() == Window.OK) - return svd.getLong(); - else - return null; - } - - public static Double askDouble(String message) { - SingleValueDialog svd = new SingleValueDialog(Display.getCurrent().getActiveShell(), message); - if (svd.open() == Window.OK) - return svd.getDouble(); - else - return null; - } - -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/package-info.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/package-info.java deleted file mode 100644 index ac76dba81..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** SWT/JFace dialogs. */ -package org.argeo.cms.swt.dialogs; \ No newline at end of file diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/osgi/BundleCmsSwtTheme.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/osgi/BundleCmsSwtTheme.java deleted file mode 100644 index b3fec78ec..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/osgi/BundleCmsSwtTheme.java +++ /dev/null @@ -1,111 +0,0 @@ -package org.argeo.cms.swt.osgi; - -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; -import java.util.Map; - -import org.argeo.api.cms.ux.CmsIcon; -import org.argeo.cms.osgi.BundleCmsTheme; -import org.argeo.cms.swt.CmsSwtTheme; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.graphics.ImageData; -import org.eclipse.swt.widgets.Display; - -/** Centralises some generic {@link CmsSwtTheme} patterns. */ -public class BundleCmsSwtTheme extends BundleCmsTheme implements CmsSwtTheme { - private Map imageCache = new HashMap<>(); - - private Map> iconPaths = new HashMap<>(); - - protected Image getImage(String path) { - if (!imageCache.containsKey(path)) { - try (InputStream in = getResourceAsStream(path)) { - if (in == null) - return null; - ImageData imageData = new ImageData(in); - imageCache.put(path, imageData); - } catch (IOException e) { - throw new IllegalStateException(e); - } - } - ImageData imageData = imageCache.get(path); - Image image = new Image(Display.getCurrent(), imageData); - return image; - } - - /** - * And icon with this file name (without the extension), with a best effort to - * find the appropriate size, or null if not found. - * - * @param name An icon file name without path and extension. - * @param preferredSize the preferred size, if null, - * {@link #getSmallIconSize()} will be tried. - */ - public Image getIcon(String name, Integer preferredSize) { - if (preferredSize == null) - preferredSize = getSmallIconSize(); - Map subCache; - if (!iconPaths.containsKey(name)) - subCache = new HashMap<>(); - else - subCache = iconPaths.get(name); - Image image = null; - if (!subCache.containsKey(preferredSize)) { - Image bestMatchSoFar = null; - paths: for (String p : getImagesPaths()) { - int lastSlash = p.lastIndexOf('/'); - String fileName = p; - String ext = ""; - if (lastSlash >= 0) - fileName = p.substring(lastSlash + 1); - int lastDot = fileName.lastIndexOf('.'); - if (lastDot >= 0) { - ext = fileName.substring(lastDot + 1); - fileName = fileName.substring(0, lastDot); - } - - if ("svg".equals(ext)) - continue paths; - - if (fileName.equals(name)) {// matched - Image img = getImage(p); - int width = img.getBounds().width; - if (width == preferredSize) {// perfect match - subCache.put(preferredSize, p); - image = img; - break paths; - } - if (bestMatchSoFar == null) { - bestMatchSoFar = img; - } else { - if (Math.abs(width - preferredSize) < Math - .abs(bestMatchSoFar.getBounds().width - preferredSize)) - bestMatchSoFar = img; - } - } - } - - if (image == null) - image = bestMatchSoFar; - } else { - image = getImage(subCache.get(preferredSize)); - } - - if (image != null && !iconPaths.containsKey(name)) - iconPaths.put(name, subCache); - - return image; - } - - @Override - public Image getSmallIcon(CmsIcon icon) { - return getIcon(icon.name(), getSmallIconSize()); - } - - @Override - public Image getBigIcon(CmsIcon icon) { - return getIcon(icon.name(), getBigIconSize()); - } - -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/osgi/BundleSvgTheme.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/osgi/BundleSvgTheme.java deleted file mode 100644 index e65f226e2..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/osgi/BundleSvgTheme.java +++ /dev/null @@ -1,105 +0,0 @@ -package org.argeo.cms.swt.osgi; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.lang.System.Logger; -import java.lang.System.Logger.Level; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import org.apache.batik.transcoder.TranscoderException; -import org.apache.batik.transcoder.TranscoderInput; -import org.apache.batik.transcoder.TranscoderOutput; -import org.apache.batik.transcoder.image.ImageTranscoder; -import org.apache.batik.transcoder.image.PNGTranscoder; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.graphics.ImageData; -import org.eclipse.swt.widgets.Display; -import org.osgi.framework.BundleContext; - -/** Theme which can dynamically create icons from SVG data. */ -public class BundleSvgTheme extends BundleCmsSwtTheme { - private final static Logger logger = System.getLogger(BundleSvgTheme.class.getName()); - - private Map> imageCache = Collections.synchronizedMap(new HashMap<>()); - - private Map transcoders = Collections.synchronizedMap(new HashMap<>()); - - @Override - public Image getIcon(String name, Integer preferredSize) { - String path = "icons/types/svg/" + name + ".svg"; - return createImageFromSvg(path, preferredSize); - } - - protected Image createImageFromSvg(String path, Integer preferredSize) { - Image image = null; - if (imageCache.containsKey(path)) { - image = imageCache.get(path).get(preferredSize); - } - if (image != null) - return image; - ImageData imageData = loadFromSvg(path, preferredSize); - image = new Image(Display.getDefault(), imageData); - if (!imageCache.containsKey(path)) - imageCache.put(path, Collections.synchronizedMap(new HashMap<>())); - imageCache.get(path).put(preferredSize, image); - return image; - } - - protected ImageData loadFromSvg(String path, int size) { - ImageTranscoder transcoder = null; - synchronized (this) { - transcoder = transcoders.get(size); - if (transcoder == null) { - transcoder = new PNGTranscoder(); - transcoder.addTranscodingHint(PNGTranscoder.KEY_WIDTH, (float) size); - transcoder.addTranscodingHint(PNGTranscoder.KEY_HEIGHT, (float) size); - transcoders.put(size, transcoder); - } - } - ImageData imageData; - try (InputStream in = getResourceAsStream(path); ByteArrayOutputStream out = new ByteArrayOutputStream();) { - if (in == null) - throw new IllegalArgumentException(path + " not found"); - TranscoderInput input = new TranscoderInput(in); - TranscoderOutput output = new TranscoderOutput(out); - transcoder.transcode(input, output); - try (InputStream imageIn = new ByteArrayInputStream(out.toByteArray())) { - imageData = new ImageData(imageIn); - } - logger.log(Level.DEBUG, () -> "Generated " + size + "x" + size + " PNG icon from " + path); - } catch (IOException | TranscoderException e) { - throw new RuntimeException("Cannot transcode SVG " + path, e); - } - - return imageData; - } - - @Override - public void init(BundleContext bundleContext, Map properties) { - super.init(bundleContext, properties); - - // preload all icons -// paths: for (String p : getImagesPaths()) { -// if (!p.endsWith(".svg")) -// continue paths; -// createImageFromSvg(p, getDefaultIconSize()); -// } - } - - @Override - public void destroy(BundleContext bundleContext, Map properties) { - Display display = Display.getDefault(); - if (display != null) - for (String path : imageCache.keySet()) { - for (Image image : imageCache.get(path).values()) { - display.syncExec(() -> image.dispose()); - } - } - super.destroy(bundleContext, properties); - } - -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/PickUpUserDialog.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/PickUpUserDialog.java deleted file mode 100644 index ed1bfd868..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/PickUpUserDialog.java +++ /dev/null @@ -1,246 +0,0 @@ -package org.argeo.cms.swt.useradmin; - -import java.util.ArrayList; -import java.util.List; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.eclipse.ui.ColumnDefinition; -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.parts.LdifUsersTable; -import org.argeo.util.naming.LdapAttrs; -import org.argeo.util.naming.LdapObjs; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.dialogs.TrayDialog; -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.TableViewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.layout.FillLayout; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Shell; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.service.useradmin.Group; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdmin; - -/** Dialog with a user (or group) list to pick up one */ -public class PickUpUserDialog extends TrayDialog { - private static final long serialVersionUID = -1420106871173920369L; - - // Business objects - private final UserAdmin userAdmin; - private User selectedUser; - - // this page widgets and UI objects - private String title; - private LdifUsersTable userTableViewerCmp; - private TableViewer userViewer; - private List columnDefs = new ArrayList(); - - /** - * A dialog to pick up a group or a user, showing a table with default - * columns - */ - public PickUpUserDialog(Shell parentShell, String title, UserAdmin userAdmin) { - super(parentShell); - this.title = title; - this.userAdmin = userAdmin; - - columnDefs.add(new ColumnDefinition(new UserLP(UserLP.COL_ICON), "", - 24, 24)); - columnDefs.add(new ColumnDefinition( - new UserLP(UserLP.COL_DISPLAY_NAME), "Common Name", 150, 100)); - columnDefs.add(new ColumnDefinition(new UserLP(UserLP.COL_DOMAIN), - "Domain", 100, 120)); - columnDefs.add(new ColumnDefinition(new UserLP(UserLP.COL_DN), - "Distinguished Name", 300, 100)); - } - - /** A dialog to pick up a group or a user */ - public PickUpUserDialog(Shell parentShell, String title, - UserAdmin userAdmin, List columnDefs) { - super(parentShell); - this.title = title; - this.userAdmin = userAdmin; - this.columnDefs = columnDefs; - } - - @Override - protected void okPressed() { - if (getSelected() == null) - MessageDialog.openError(getShell(), "No user chosen", - "Please, choose a user or press Cancel."); - else - super.okPressed(); - } - - protected Control createDialogArea(Composite parent) { - Composite dialogArea = (Composite) super.createDialogArea(parent); - dialogArea.setLayout(new FillLayout()); - - Composite bodyCmp = new Composite(dialogArea, SWT.NO_FOCUS); - bodyCmp.setLayout(new GridLayout()); - - // Create and configure the table - userTableViewerCmp = new MyUserTableViewer(bodyCmp, SWT.MULTI - | SWT.H_SCROLL | SWT.V_SCROLL); - - userTableViewerCmp.setColumnDefinitions(columnDefs); - userTableViewerCmp.populateWithStaticFilters(false, false); - GridData gd = EclipseUiUtils.fillAll(); - gd.minimumHeight = 300; - userTableViewerCmp.setLayoutData(gd); - userTableViewerCmp.refresh(); - - // Controllers - userViewer = userTableViewerCmp.getTableViewer(); - userViewer.addDoubleClickListener(new MyDoubleClickListener()); - userViewer - .addSelectionChangedListener(new MySelectionChangedListener()); - - parent.pack(); - return dialogArea; - } - - public User getSelected() { - if (selectedUser == null) - return null; - else - return selectedUser; - } - - protected void configureShell(Shell shell) { - super.configureShell(shell); - shell.setText(title); - } - - class MyDoubleClickListener implements IDoubleClickListener { - public void doubleClick(DoubleClickEvent evt) { - if (evt.getSelection().isEmpty()) - return; - - Object obj = ((IStructuredSelection) evt.getSelection()) - .getFirstElement(); - if (obj instanceof User) { - selectedUser = (User) obj; - okPressed(); - } - } - } - - class MySelectionChangedListener implements ISelectionChangedListener { - @Override - public void selectionChanged(SelectionChangedEvent event) { - if (event.getSelection().isEmpty()) { - selectedUser = null; - return; - } - Object obj = ((IStructuredSelection) event.getSelection()) - .getFirstElement(); - if (obj instanceof Group) { - selectedUser = (Group) obj; - } - } - } - - private class MyUserTableViewer extends LdifUsersTable { - private static final long serialVersionUID = 8467999509931900367L; - - private final String[] knownProps = { LdapAttrs.uid.name(), - LdapAttrs.cn.name(), LdapAttrs.DN }; - - private Button showSystemRoleBtn; - private Button showUserBtn; - - public MyUserTableViewer(Composite parent, int style) { - super(parent, style); - } - - protected void populateStaticFilters(Composite staticFilterCmp) { - staticFilterCmp.setLayout(new GridLayout()); - showSystemRoleBtn = new Button(staticFilterCmp, SWT.CHECK); - showSystemRoleBtn.setText("Show system roles "); - - showUserBtn = new Button(staticFilterCmp, SWT.CHECK); - showUserBtn.setText("Show users "); - - SelectionListener sl = new SelectionAdapter() { - private static final long serialVersionUID = -7033424592697691676L; - - @Override - public void widgetSelected(SelectionEvent e) { - refresh(); - } - }; - - showSystemRoleBtn.addSelectionListener(sl); - showUserBtn.addSelectionListener(sl); - } - - @Override - protected List listFilteredElements(String filter) { - Role[] roles; - try { - StringBuilder builder = new StringBuilder(); - - StringBuilder filterBuilder = new StringBuilder(); - if (notNull(filter)) - for (String prop : knownProps) { - filterBuilder.append("("); - filterBuilder.append(prop); - filterBuilder.append("=*"); - filterBuilder.append(filter); - filterBuilder.append("*)"); - } - - String typeStr = "(" + LdapAttrs.objectClass.name() + "=" - + LdapObjs.groupOfNames.name() + ")"; - if ((showUserBtn.getSelection())) - typeStr = "(|(" + LdapAttrs.objectClass.name() + "=" - + LdapObjs.inetOrgPerson.name() + ")" + typeStr - + ")"; - - if (!showSystemRoleBtn.getSelection()) - typeStr = "(& " + typeStr + "(!(" + LdapAttrs.DN + "=*" - + CmsConstants.ROLES_BASEDN + ")))"; - - if (filterBuilder.length() > 1) { - builder.append("(&" + typeStr); - builder.append("(|"); - builder.append(filterBuilder.toString()); - builder.append("))"); - } else { - builder.append(typeStr); - } - roles = userAdmin.getRoles(builder.toString()); - } catch (InvalidSyntaxException e) { - throw new EclipseUiException( - "Unable to get roles with filter: " + filter, e); - } - List users = new ArrayList(); - for (Role role : roles) - if (!users.contains(role)) - users.add((User) role); - return users; - } - } - - private boolean notNull(String string) { - if (string == null) - return false; - else - return !"".equals(string.trim()); - } -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/UserLP.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/UserLP.java deleted file mode 100644 index d1c90a43f..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/UserLP.java +++ /dev/null @@ -1,76 +0,0 @@ -package org.argeo.cms.swt.useradmin; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.cms.auth.UserAdminUtils; -import org.eclipse.jface.resource.JFaceResources; -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Font; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.widgets.Display; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; - -/** Centralize label providers for the group table */ -class UserLP extends ColumnLabelProvider { - private static final long serialVersionUID = -4645930210988368571L; - - final static String COL_ICON = "colID.icon"; - final static String COL_DN = "colID.dn"; - final static String COL_DISPLAY_NAME = "colID.displayName"; - final static String COL_DOMAIN = "colID.domain"; - - final String currType; - - // private Font italic; - private Font bold; - - UserLP(String colId) { - this.currType = colId; - } - - @Override - public Font getFont(Object element) { - // Current user as bold - if (UserAdminUtils.isCurrentUser(((User) element))) { - if (bold == null) - bold = JFaceResources.getFontRegistry().defaultFontDescriptor().setStyle(SWT.BOLD) - .createFont(Display.getCurrent()); - return bold; - } - return null; - } - - @Override - public Image getImage(Object element) { - if (COL_ICON.equals(currType)) { - User user = (User) element; - String dn = user.getName(); - if (dn.endsWith(CmsConstants.ROLES_BASEDN)) - return UsersImages.ICON_ROLE; - else if (user.getType() == Role.GROUP) - return UsersImages.ICON_GROUP; - else - return UsersImages.ICON_USER; - } else - return null; - } - - @Override - public String getText(Object element) { - User user = (User) element; - return getText(user); - - } - - public String getText(User user) { - if (COL_DN.equals(currType)) - return user.getName(); - else if (COL_DISPLAY_NAME.equals(currType)) - return UserAdminUtils.getCommonName(user); - else if (COL_DOMAIN.equals(currType)) - return UserAdminUtils.getDomainName(user); - else - return ""; - } -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/UsersImages.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/UsersImages.java deleted file mode 100644 index 21fc5afba..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/UsersImages.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.argeo.cms.swt.useradmin; - -import org.argeo.cms.ui.theme.CmsImages; -import org.eclipse.swt.graphics.Image; - -/** Specific users icons. */ -public class UsersImages { - private final static String PREFIX = "icons/"; - - public final static Image ICON_USER = CmsImages.createImg(PREFIX + "person.png"); - public final static Image ICON_GROUP = CmsImages.createImg(PREFIX + "group.png"); - public final static Image ICON_ROLE = CmsImages.createImg(PREFIX + "role.gif"); - public final static Image ICON_CHANGE_PASSWORD = CmsImages.createImg(PREFIX + "security.gif"); -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/package-info.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/package-info.java deleted file mode 100644 index 3597bfc57..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** SWT/JFace users management components. */ -package org.argeo.cms.swt.useradmin; \ No newline at end of file diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/AbstractSwtPart.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/AbstractSwtPart.java deleted file mode 100644 index c3d11a181..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/AbstractSwtPart.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.argeo.cms.swt.widgets; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ux.widgets.DataPart; -import org.argeo.cms.ux.widgets.DataView; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.widgets.Composite; - -public abstract class AbstractSwtPart extends Composite implements DataView { - private static final long serialVersionUID = -1999179054267812170L; - - protected DataPart dataPart; - - protected final SelectionListener selectionListener; - - public AbstractSwtPart(Composite parent, int style, DataPart dataPart) { - super(parent, style); - setLayout(CmsSwtUtils.noSpaceGridLayout()); - - this.dataPart = dataPart; - - selectionListener = new SelectionListener() { - - private static final long serialVersionUID = 4334785560035009330L; - - @Override - public void widgetSelected(SelectionEvent e) { - if (dataPart.getOnSelected() != null) - dataPart.getOnSelected().accept((TYPE) e.item.getData()); - } - - @Override - public void widgetDefaultSelected(SelectionEvent e) { - if (dataPart.getOnAction() != null) - dataPart.getOnAction().accept((TYPE) e.item.getData()); - } - }; - - dataPart.addView(this); - addDisposeListener((e) -> dataPart.removeView(this)); - } - - public abstract void refresh(); -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/ContextOverlay.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/ContextOverlay.java deleted file mode 100644 index f7b644377..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/ContextOverlay.java +++ /dev/null @@ -1,113 +0,0 @@ -package org.argeo.cms.swt.widgets; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.ShellAdapter; -import org.eclipse.swt.events.ShellEvent; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Shell; - -/** - * Manages a lightweight shell which is related to a {@link Control}, typically - * in order to reproduce a dropdown semantic, but with more flexibility. - */ -public class ContextOverlay extends ScrolledPage { - private static final long serialVersionUID = 6702077429573324009L; - -// private Shell shell; - private Control control; - - private int maxHeight = 400; - - public ContextOverlay(Control control, int style) { - super(createShell(control, style), SWT.NONE); - Shell shell = getShell(); - setLayoutData(CmsSwtUtils.fillAll()); - // TODO make autohide configurable? - //shell.addShellListener(new AutoHideShellListener()); - this.control = control; - control.addDisposeListener((e) -> { - dispose(); - shell.dispose(); - }); - } - - private static Composite createShell(Control control, int style) { - if (control == null) - throw new IllegalArgumentException("Control cannot be null"); - if (control.isDisposed()) - throw new IllegalArgumentException("Control is disposed"); - Shell shell = new Shell(control.getShell(), SWT.NO_TRIM); - shell.setLayout(CmsSwtUtils.noSpaceGridLayout()); - Composite placeholder = new Composite(shell, SWT.BORDER); - placeholder.setLayoutData(CmsSwtUtils.fillAll()); - placeholder.setLayout(CmsSwtUtils.noSpaceGridLayout()); - return placeholder; - } - - public void show() { - Point relativeControlLocation = control.getLocation(); - Point controlLocation = control.toDisplay(relativeControlLocation.x, relativeControlLocation.y); - - int controlWidth = control.getBounds().width; - - Shell shell = getShell(); - - layout(true, true); - shell.pack(); - shell.layout(true, true); - int targetShellWidth = shell.getSize().x < controlWidth ? controlWidth : shell.getSize().x; - if (shell.getSize().y > maxHeight) { - shell.setSize(targetShellWidth, maxHeight); - } else { - shell.setSize(targetShellWidth, shell.getSize().y); - } - - int shellHeight = shell.getSize().y; - int controlHeight = control.getBounds().height; - Point shellLocation = new Point(controlLocation.x, controlLocation.y + controlHeight); - int displayHeight = shell.getDisplay().getBounds().height; - if (shellLocation.y + shellHeight > displayHeight) {// bottom of page - shellLocation = new Point(controlLocation.x, controlLocation.y - shellHeight); - } - shell.setLocation(shellLocation); - - if (getChildren().length != 0) - shell.open(); - if (!control.isDisposed()) - control.setFocus(); - } - - public void hide() { - getShell().setVisible(false); - onHide(); - } - - public boolean isShellVisible() { - if (isDisposed()) - return false; - return getShell().isVisible(); - } - - /** to be overridden */ - protected void onHide() { - // does nothing by default. - } - - private class AutoHideShellListener extends ShellAdapter { - private static final long serialVersionUID = 7743287433907938099L; - - @Override - public void shellDeactivated(ShellEvent e) { - try { - Thread.sleep(1000); - } catch (InterruptedException e1) { - // silent - } - if (!control.isDisposed() && !control.isFocusControl()) - hide(); - } - } -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/EditableText.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/EditableText.java deleted file mode 100644 index 0612e8f9b..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/EditableText.java +++ /dev/null @@ -1,135 +0,0 @@ -package org.argeo.cms.swt.widgets; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Color; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; - -/** Editable text part displaying styled text. */ -public class EditableText extends StyledControl { - private static final long serialVersionUID = -6372283442330912755L; - - private boolean editable = true; - private boolean multiLine = true; - - private Color highlightColor; - private Composite highlight; - - private boolean useTextAsLabel = false; - - public EditableText(Composite parent, int style) { - super(parent, style); - editable = !(SWT.READ_ONLY == (style & SWT.READ_ONLY)); - multiLine = !(SWT.SINGLE == (style & SWT.SINGLE)); - highlightColor = parent.getDisplay().getSystemColor(SWT.COLOR_GRAY); - useTextAsLabel = SWT.FLAT == (style & SWT.FLAT); - } - - @Override - protected Control createControl(Composite box, String style) { - if (isEditing() && getEditable()) { - return createText(box, style, true); - } else { - if (useTextAsLabel) { - return createTextLabel(box, style); - } else { - return createLabel(box, style); - } - } - } - - protected Label createLabel(Composite box, String style) { - Label lbl = new Label(box, getStyle() | SWT.WRAP); - lbl.setLayoutData(CmsSwtUtils.fillWidth()); - if (style != null) - CmsSwtUtils.style(lbl, style); - CmsSwtUtils.markup(lbl); - if (mouseListener != null) - lbl.addMouseListener(mouseListener); - return lbl; - } - - protected Text createTextLabel(Composite box, String style) { - Text lbl = new Text(box, getStyle() | (multiLine ? SWT.MULTI : SWT.SINGLE)); - lbl.setEditable(false); - lbl.setLayoutData(CmsSwtUtils.fillWidth()); - if (style != null) - CmsSwtUtils.style(lbl, style); - CmsSwtUtils.markup(lbl); - if (mouseListener != null) - lbl.addMouseListener(mouseListener); - return lbl; - } - - protected Text createText(Composite box, String style, boolean editable) { - highlight = new Composite(box, SWT.NONE); - highlight.setBackground(highlightColor); - GridData highlightGd = new GridData(SWT.FILL, SWT.FILL, false, false); - highlightGd.widthHint = 5; - highlightGd.heightHint = 3; - highlight.setLayoutData(highlightGd); - - final Text text = new Text(box, getStyle() | (multiLine ? SWT.MULTI : SWT.SINGLE) | SWT.WRAP); - text.setEditable(editable); - GridData textLayoutData = CmsSwtUtils.fillWidth(); - // textLayoutData.heightHint = preferredHeight; - text.setLayoutData(textLayoutData); - if (style != null) - CmsSwtUtils.style(text, style); - text.setFocus(); - return text; - } - - @Override - protected void clear(boolean deep) { - if (highlight != null) - highlight.dispose(); - super.clear(deep); - } - - public void setText(String text) { - Control child = getControl(); - if (child instanceof Label) - ((Label) child).setText(text); - else if (child instanceof Text) - ((Text) child).setText(text); - } - - public Text getAsText() { - return (Text) getControl(); - } - - public Label getAsLabel() { - return (Label) getControl(); - } - - public String getText() { - Control child = getControl(); - - if (child instanceof Label) - return ((Label) child).getText(); - else if (child instanceof Text) - return ((Text) child).getText(); - else - throw new IllegalStateException("Unsupported control " + child.getClass()); - } - - /** @deprecated Use {@link #isEditable()} instead. */ - @Deprecated - public boolean getEditable() { - return isEditable(); - } - - public boolean isEditable() { - return editable; - } - - public void setUseTextAsLabel(boolean useTextAsLabel) { - this.useTextAsLabel = useTextAsLabel; - } - -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/ScrolledPage.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/ScrolledPage.java deleted file mode 100644 index 135f4c147..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/ScrolledPage.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.argeo.cms.swt.widgets; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.ScrolledComposite; -import org.eclipse.swt.events.ControlEvent; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -/** - * A composite that can be scrolled vertically. It wraps a - * {@link ScrolledComposite} (and is being wrapped by it), simplifying its - * configuration. - */ -public class ScrolledPage extends Composite { - private static final long serialVersionUID = 1593536965663574437L; - - private ScrolledComposite scrolledComposite; - - public ScrolledPage(Composite parent, int style) { - this(parent, style, false); - } - - public ScrolledPage(Composite parent, int style, boolean alwaysShowScroll) { - super(createScrolledComposite(parent, alwaysShowScroll), style); - scrolledComposite = (ScrolledComposite) getParent(); - scrolledComposite.setContent(this); - - scrolledComposite.setExpandVertical(true); - scrolledComposite.setExpandHorizontal(true); - scrolledComposite.addControlListener(new ScrollControlListener()); - } - - private static ScrolledComposite createScrolledComposite(Composite parent, boolean alwaysShowScroll) { - ScrolledComposite scrolledComposite = new ScrolledComposite(parent, SWT.V_SCROLL); - scrolledComposite.setAlwaysShowScrollBars(alwaysShowScroll); - return scrolledComposite; - } - - @Override - public void layout(boolean changed, boolean all) { - updateScroll(); - super.layout(changed, all); - } - - public void showControl(Control control) { - scrolledComposite.showControl(control); - } - - protected void updateScroll() { - Rectangle r = scrolledComposite.getClientArea(); - Point preferredSize = computeSize(r.width, SWT.DEFAULT); - scrolledComposite.setMinHeight(preferredSize.y); - } - - // public ScrolledComposite getScrolledComposite() { - // return this.scrolledComposite; - // } - - /** Set it on the wrapping scrolled composite */ - @Override - public void setLayoutData(Object layoutData) { - scrolledComposite.setLayoutData(layoutData); - } - - private class ScrollControlListener extends org.eclipse.swt.events.ControlAdapter { - private static final long serialVersionUID = -3586986238567483316L; - - public void controlResized(ControlEvent e) { - updateScroll(); - } - } -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/StyledControl.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/StyledControl.java deleted file mode 100644 index 82c04a26c..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/StyledControl.java +++ /dev/null @@ -1,151 +0,0 @@ -package org.argeo.cms.swt.widgets; - -import org.argeo.api.cms.ux.CmsStyle; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.swt.SwtEditablePart; -import org.argeo.eclipse.ui.specific.EclipseUiSpecificUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.FocusListener; -import org.eclipse.swt.events.MouseListener; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -/** Editable text part displaying styled text. */ -public abstract class StyledControl extends Composite implements SwtEditablePart { - private static final long serialVersionUID = -6372283442330912755L; - private Control control; - - private Composite container; - private Composite box; - - protected MouseListener mouseListener; - protected FocusListener focusListener; - - private Boolean editing = Boolean.FALSE; - - private Composite ancestorToLayout; - - public StyledControl(Composite parent, int swtStyle) { - super(parent, swtStyle); - setLayout(CmsSwtUtils.noSpaceGridLayout()); - } - - protected abstract Control createControl(Composite box, String style); - - protected Composite createBox() { - Composite box = new Composite(container, SWT.INHERIT_DEFAULT); - setContainerLayoutData(box); - box.setLayout(CmsSwtUtils.noSpaceGridLayout(3)); - return box; - } - - protected Composite createContainer() { - Composite container = new Composite(this, SWT.INHERIT_DEFAULT); - setContainerLayoutData(container); - container.setLayout(CmsSwtUtils.noSpaceGridLayout()); - return container; - } - - @Override - public Control getControl() { - return control; - } - - protected synchronized Boolean isEditing() { - return editing; - } - - @Override - public synchronized void startEditing() { - assert !isEditing(); - editing = true; - // int height = control.getSize().y; - String style = (String) EclipseUiSpecificUtils.getStyleData(control); - clear(false); - refreshControl(style); - - // add the focus listener to the newly created edition control - if (focusListener != null) - control.addFocusListener(focusListener); - } - - @Override - public synchronized void stopEditing() { - assert isEditing(); - editing = false; - String style = (String) EclipseUiSpecificUtils.getStyleData(control); - clear(false); - refreshControl(style); - } - - protected void refreshControl(String style) { - control = createControl(box, style); - setControlLayoutData(control); - if (ancestorToLayout != null) - ancestorToLayout.layout(true, true); - else - getParent().layout(true, true); - } - - public void setStyle(CmsStyle style) { - setStyle(style.style()); - } - - public void setStyle(String style) { - Object currentStyle = null; - if (control != null) - currentStyle = EclipseUiSpecificUtils.getStyleData(control); - if (currentStyle != null && currentStyle.equals(style)) - return; - - clear(true); - refreshControl(style); - - if (style != null) { - CmsSwtUtils.style(box, style + "_box"); - CmsSwtUtils.style(container, style + "_container"); - } - } - - /** To be overridden */ - protected void setControlLayoutData(Control control) { - control.setLayoutData(CmsSwtUtils.fillWidth()); - } - - /** To be overridden */ - protected void setContainerLayoutData(Composite composite) { - composite.setLayoutData(CmsSwtUtils.fillWidth()); - } - - protected void clear(boolean deep) { - if (deep) { - for (Control control : getChildren()) - control.dispose(); - container = createContainer(); - box = createBox(); - } else { - control.dispose(); - } - } - - public void setMouseListener(MouseListener mouseListener) { - if (this.mouseListener != null && control != null) - control.removeMouseListener(this.mouseListener); - this.mouseListener = mouseListener; - if (control != null && this.mouseListener != null) - control.addMouseListener(mouseListener); - } - - public void setFocusListener(FocusListener focusListener) { - if (this.focusListener != null && control != null) - control.removeFocusListener(this.focusListener); - this.focusListener = focusListener; - if (control != null && this.focusListener != null) - control.addFocusListener(focusListener); - } - - public void setAncestorToLayout(Composite ancestorToLayout) { - this.ancestorToLayout = ancestorToLayout; - } - -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtHierarchicalPart.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtHierarchicalPart.java deleted file mode 100644 index 07c9bac2e..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtHierarchicalPart.java +++ /dev/null @@ -1,106 +0,0 @@ -package org.argeo.cms.swt.widgets; - -import java.util.List; - -import org.argeo.api.cms.ux.CmsIcon; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ux.widgets.HierarchicalPart; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Tree; -import org.eclipse.swt.widgets.TreeItem; - -/** {@link HierarchicalPart} implementation based on a {@link Tree}. */ -public class SwtHierarchicalPart extends AbstractSwtPart { - private static final long serialVersionUID = -6247710601465713047L; - - private final Tree tree; - - private HierarchicalPart hierarchicalPart; - - public SwtHierarchicalPart(Composite parent, int style, HierarchicalPart hierarchicalPart) { - super(parent, style, hierarchicalPart); - tree = new Tree(this, SWT.BORDER); - tree.setLayoutData(CmsSwtUtils.fillAll()); - this.hierarchicalPart = hierarchicalPart; - - tree.addSelectionListener(selectionListener); - } - - @Override - public void refresh() { - // TODO optimise - // tree.clearAll(true); - - for (TreeItem rootItem : tree.getItems()) { - rootItem.dispose(); - } - - List rootItems = hierarchicalPart.getChildren(hierarchicalPart.getInput()); - for (T child : rootItems) { - TreeItem childItem = addTreeItem(null, child); -// List grandChildren = hierarchicalPart.getChildren(child); -// for (T grandChild : grandChildren) { -// addTreeItem(childItem, grandChild); -// } - } -// tree.addListener(SWT.SetData, event -> { -// TreeItem item = (TreeItem) event.item; -// TreeItem parentItem = item.getParentItem(); -// if (parentItem == null) { -// refreshRootItem(item); -// } else { -// refreshItem(parentItem, item); -// } -// }); -// tree.setItemCount(getRootItemCount()); - - tree.addListener(SWT.Expand, event -> { - final TreeItem root = (TreeItem) event.item; - TreeItem[] items = root.getItems(); - for (TreeItem item : items) { - if (item.getData() != null) { -// List grandChildren = hierarchicalPart.getChildren((T) item.getData()); -// for (T grandChild : grandChildren) { -// addTreeItem(item, grandChild); -// } - return; - } - item.dispose(); - } - - List children = hierarchicalPart.getChildren((T) root.getData()); - for (T child : children) { - TreeItem childItem = addTreeItem(root, child); -// List grandChildren = hierarchicalPart.getChildren(child); -// for (T grandChild : grandChildren) { -// addTreeItem(childItem, grandChild); -// } - } - }); - - CmsSwtUtils.fill(tree); - - } - - protected TreeItem addTreeItem(TreeItem parent, T data) { - TreeItem item = parent == null ? new TreeItem(tree, SWT.NONE) : new TreeItem(parent, SWT.NONE); - item.setData(data); - String txt = hierarchicalPart.getText(data); - if (txt != null) - item.setText(hierarchicalPart.getText(data)); - CmsIcon icon = hierarchicalPart.getIcon(data); - // TODO optimize - List grandChildren = hierarchicalPart.getChildren(data); - if (grandChildren.size() != 0) - new TreeItem(item, SWT.NONE); - return item; -//if(icon!=null) -// item.setImage(null); - } - - protected Tree getTree() { - return tree; - } - -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtTabularPart.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtTabularPart.java deleted file mode 100644 index 2f10cac75..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtTabularPart.java +++ /dev/null @@ -1,82 +0,0 @@ -package org.argeo.cms.swt.widgets; - -import org.argeo.api.cms.ux.CmsIcon; -import org.argeo.cms.swt.CmsSwtTheme; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ux.widgets.Column; -import org.argeo.cms.ux.widgets.TabularPart; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Table; -import org.eclipse.swt.widgets.TableColumn; -import org.eclipse.swt.widgets.TableItem; - -/** {@link TabularPart} implementation based on a {@link Table}. */ -public class SwtTabularPart extends AbstractSwtPart { - private static final long serialVersionUID = -1114155772446357750L; - private final Table table; - private TabularPart tabularPart; - - private CmsSwtTheme theme; - - public SwtTabularPart(Composite parent, int style, TabularPart tabularPart) { - super(parent, style, tabularPart); - theme = CmsSwtUtils.getCmsTheme(parent); - - table = new Table(this, SWT.VIRTUAL | SWT.BORDER); - table.setLinesVisible(true); - table.setLayoutData(CmsSwtUtils.fillAll()); - - this.tabularPart = tabularPart; - } - - @Override - public void refresh() { - // TODO optimise - table.clearAll(); - table.addListener(SWT.SetData, event -> { - TableItem item = (TableItem) event.item; - refreshItem(item); - }); - table.setItemCount(tabularPart.getItemCount()); - for (int i = 0; i < tabularPart.getColumnCount(); i++) { - TableColumn swtColumn = new TableColumn(table, SWT.NONE); - swtColumn.setWidth(tabularPart.getColumn(i).getWidth()); - } - CmsSwtUtils.fill(table); - - table.addSelectionListener(selectionListener); - - } - - protected Object getDataFromEvent(SelectionEvent e) { - Object data = e.item.getData(); - if (data == null) - data = tabularPart.getData(getTable().indexOf((TableItem) e.item)); - return data; - } - - protected void refreshItem(TableItem item) { - int row = getTable().indexOf(item); - for (int i = 0; i < tabularPart.getColumnCount(); i++) { - Column column = tabularPart.getColumn(i); - T data = tabularPart.getData(row); - item.setData(data); - String text = data != null ? column.getText(data) : ""; - if (text != null) - item.setText(i, text); - CmsIcon icon = column.getIcon(data); - if (icon != null) { - Image image = theme.getSmallIcon(icon); - item.setImage(i, image); - } - } - } - - protected Table getTable() { - return table; - } - -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/ui/theme/CmsImages.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/ui/theme/CmsImages.java deleted file mode 100644 index 1c4d79eee..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/ui/theme/CmsImages.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.argeo.cms.ui.theme; - -import java.net.URL; - -import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.widgets.Display; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; - -public class CmsImages { - private static BundleContext themeBc = FrameworkUtil.getBundle(CmsImages.class).getBundleContext(); - - final public static String ICONS_BASE = "icons/"; - final public static String TYPES_BASE = ICONS_BASE + "types/"; - final public static String ACTIONS_BASE = ICONS_BASE + "actions/"; - - public static Image createIcon(String name) { - return createImg(CmsImages.ICONS_BASE + name); - } - - public static Image createAction(String name) { - return createImg(CmsImages.ACTIONS_BASE + name); - } - - public static Image createType(String name) { - return createImg(CmsImages.TYPES_BASE + name); - } - - public static Image createImg(String name) { - return CmsImages.createDesc(name).createImage(Display.getDefault()); - } - - public static ImageDescriptor createDesc(String name) { - return createDesc(themeBc, name); - } - - public static ImageDescriptor createDesc(BundleContext bc, String name) { - URL url = bc.getBundle().getResource(name); - if (url == null) - return ImageDescriptor.getMissingImageDescriptor(); - return ImageDescriptor.createFromURL(url); - } - - public static Image createImg(BundleContext bc, String name) { - return createDesc(bc, name).createImage(Display.getDefault()); - } - -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/ui/theme/package-info.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/ui/theme/package-info.java deleted file mode 100644 index 7d3a260f3..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/ui/theme/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Argeo CMS core theme images. */ -package org.argeo.cms.ui.theme; \ No newline at end of file diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/AbstractTreeContentProvider.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/AbstractTreeContentProvider.java deleted file mode 100644 index 64ea2dbc9..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/AbstractTreeContentProvider.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.argeo.eclipse.ui; - -import org.argeo.cms.ux.widgets.TreeParent; -import org.eclipse.jface.viewers.ITreeContentProvider; -import org.eclipse.jface.viewers.Viewer; - -/** - * Tree content provider dealing with tree objects and providing reasonable - * defaults. - */ -public abstract class AbstractTreeContentProvider implements - ITreeContentProvider { - private static final long serialVersionUID = 8246126401957763868L; - - /** Does nothing */ - public void dispose() { - } - - /** Does nothing */ - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - } - - public Object[] getChildren(Object element) { - if (element instanceof TreeParent) { - return ((TreeParent) element).getChildren(); - } - return new Object[0]; - } - - public Object getParent(Object element) { - if (element instanceof TreeParent) { - return ((TreeParent) element).getParent(); - } - return null; - } - - public boolean hasChildren(Object element) { - if (element instanceof TreeParent) { - return ((TreeParent) element).hasChildren(); - } - return false; - } -} \ No newline at end of file diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/ColumnDefinition.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/ColumnDefinition.java deleted file mode 100644 index a38552c07..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/ColumnDefinition.java +++ /dev/null @@ -1,68 +0,0 @@ -package org.argeo.eclipse.ui; - -import org.eclipse.jface.viewers.ColumnLabelProvider; - -/** - * Wraps the definition of a column to be used in the various JFace viewers - * (typically tree and table). It enables definition of generic viewers which - * column can be then defined externally. Also used to generate export. - */ -public class ColumnDefinition { - private ColumnLabelProvider labelProvider; - private String label; - private int weight = 0; - private int minWidth = 120; - - public ColumnDefinition(ColumnLabelProvider labelProvider, String label) { - this.labelProvider = labelProvider; - this.label = label; - } - - public ColumnDefinition(ColumnLabelProvider labelProvider, String label, - int weight) { - this.labelProvider = labelProvider; - this.label = label; - this.weight = weight; - this.minWidth = weight; - } - - public ColumnDefinition(ColumnLabelProvider labelProvider, String label, - int weight, int minimumWidth) { - this.labelProvider = labelProvider; - this.label = label; - this.weight = weight; - this.minWidth = minimumWidth; - } - - public ColumnLabelProvider getLabelProvider() { - return labelProvider; - } - - public void setLabelProvider(ColumnLabelProvider labelProvider) { - this.labelProvider = labelProvider; - } - - public String getLabel() { - return label; - } - - public void setLabel(String label) { - this.label = label; - } - - public int getWeight() { - return weight; - } - - public void setWeight(int weight) { - this.weight = weight; - } - - public int getMinWidth() { - return minWidth; - } - - public void setMinWidth(int minWidth) { - this.minWidth = minWidth; - } -} \ No newline at end of file diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/ColumnViewerComparator.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/ColumnViewerComparator.java deleted file mode 100644 index 9430a2083..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/ColumnViewerComparator.java +++ /dev/null @@ -1,81 +0,0 @@ -package org.argeo.eclipse.ui; - -import org.eclipse.jface.viewers.ColumnViewer; -import org.eclipse.jface.viewers.TableViewerColumn; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.jface.viewers.ViewerComparator; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; - -/** Generic column viewer sorter */ -public class ColumnViewerComparator extends ViewerComparator { - private static final long serialVersionUID = -2266218906355859909L; - - public static final int ASC = 1; - - public static final int NONE = 0; - - public static final int DESC = -1; - - private int direction = 0; - - private TableViewerColumn column; - - private ColumnViewer viewer; - - public ColumnViewerComparator(TableViewerColumn column) { - super(null); - this.column = column; - this.viewer = column.getViewer(); - this.column.getColumn().addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = 7586796298965472189L; - - public void widgetSelected(SelectionEvent e) { - if (ColumnViewerComparator.this.viewer.getComparator() != null) { - if (ColumnViewerComparator.this.viewer.getComparator() == ColumnViewerComparator.this) { - int tdirection = ColumnViewerComparator.this.direction; - - if (tdirection == ASC) { - setSortDirection(DESC); - } else if (tdirection == DESC) { - setSortDirection(NONE); - } - } else { - setSortDirection(ASC); - } - } else { - setSortDirection(ASC); - } - } - }); - } - - private void setSortDirection(int direction) { - if (direction == NONE) { - column.getColumn().getParent().setSortColumn(null); - column.getColumn().getParent().setSortDirection(SWT.NONE); - viewer.setComparator(null); - } else { - column.getColumn().getParent().setSortColumn(column.getColumn()); - this.direction = direction; - - if (direction == ASC) { - column.getColumn().getParent().setSortDirection(SWT.DOWN); - } else { - column.getColumn().getParent().setSortDirection(SWT.UP); - } - - if (viewer.getComparator() == this) { - viewer.refresh(); - } else { - viewer.setComparator(this); - } - - } - } - - public int compare(Viewer viewer, Object e1, Object e2) { - return direction * super.compare(viewer, e1, e2); - } -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/EclipseUiException.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/EclipseUiException.java deleted file mode 100644 index 37a36e859..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/EclipseUiException.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.argeo.eclipse.ui; - -/** CMS specific exceptions. */ -public class EclipseUiException extends RuntimeException { - private static final long serialVersionUID = -5341764743356771313L; - - public EclipseUiException(String message) { - super(message); - } - - public EclipseUiException(String message, Throwable e) { - super(message, e); - } - -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/EclipseUiUtils.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/EclipseUiUtils.java deleted file mode 100644 index 95b45fed6..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/EclipseUiUtils.java +++ /dev/null @@ -1,195 +0,0 @@ -package org.argeo.eclipse.ui; - -import org.eclipse.jface.resource.JFaceResources; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.graphics.Font; -import org.eclipse.swt.layout.FormAttachment; -import org.eclipse.swt.layout.FormData; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; - -/** Utilities to simplify UI development. */ -public class EclipseUiUtils { - - /** Dispose all children of a Composite */ - public static void clear(Composite composite) { - for (Control child : composite.getChildren()) - child.dispose(); - } - - /** - * Enables efficient call to the layout method of a composite, refreshing only - * some of the children controls. - */ - public static void layout(Composite parent, Control... toUpdateControls) { - parent.layout(toUpdateControls); - } - - // - // FONTS - // - /** Shortcut to retrieve default italic font from display */ - public static Font getItalicFont(Composite parent) { - return JFaceResources.getFontRegistry().defaultFontDescriptor().setStyle(SWT.ITALIC) - .createFont(parent.getDisplay()); - } - - /** Shortcut to retrieve default bold font from display */ - public static Font getBoldFont(Composite parent) { - return JFaceResources.getFontRegistry().defaultFontDescriptor().setStyle(SWT.BOLD) - .createFont(parent.getDisplay()); - } - - /** Shortcut to retrieve default bold italic font from display */ - public static Font getBoldItalicFont(Composite parent) { - return JFaceResources.getFontRegistry().defaultFontDescriptor().setStyle(SWT.BOLD | SWT.ITALIC) - .createFont(parent.getDisplay()); - } - - // - // Simplify grid layouts management - // - public static GridLayout noSpaceGridLayout() { - return noSpaceGridLayout(new GridLayout()); - } - - public static GridLayout noSpaceGridLayout(int columns) { - return noSpaceGridLayout(new GridLayout(columns, false)); - } - - public static GridLayout noSpaceGridLayout(GridLayout layout) { - layout.horizontalSpacing = 0; - layout.verticalSpacing = 0; - layout.marginWidth = 0; - layout.marginHeight = 0; - return layout; - } - - public static GridData fillWidth() { - return grabWidth(SWT.FILL, SWT.FILL); - } - - public static GridData fillWidth(int colSpan) { - GridData gd = grabWidth(SWT.FILL, SWT.FILL); - gd.horizontalSpan = colSpan; - return gd; - } - - public static GridData fillAll() { - return new GridData(SWT.FILL, SWT.FILL, true, true); - } - - public static GridData fillAll(int colSpan, int rowSpan) { - return new GridData(SWT.FILL, SWT.FILL, true, true, colSpan, rowSpan); - } - - public static GridData grabWidth(int horizontalAlignment, int verticalAlignment) { - return new GridData(horizontalAlignment, horizontalAlignment, true, false); - } - - // - // Simplify Form layout management - // - - /** - * Creates a basic form data that is attached to the 4 corners of the parent - * composite - */ - public static FormData fillFormData() { - FormData formData = new FormData(); - formData.top = new FormAttachment(0, 0); - formData.left = new FormAttachment(0, 0); - formData.right = new FormAttachment(100, 0); - formData.bottom = new FormAttachment(100, 0); - return formData; - } - - /** - * Create a label and a text field for a grid layout, the text field grabbing - * excess horizontal - * - * @param parent - * the parent composite - * @param label - * the label to display - * @param modifyListener - * a {@link ModifyListener} to listen on events on the text, can be - * null - * @return the created text - * - */ - // FIXME why was this deprecated. - // * @ deprecated use { @ link #createGridLT(Composite, String)} instead - // @ Deprecated - public static Text createGridLT(Composite parent, String label, ModifyListener modifyListener) { - Label lbl = new Label(parent, SWT.LEAD); - lbl.setText(label); - lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); - Text txt = new Text(parent, SWT.LEAD | SWT.BORDER); - txt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); - if (modifyListener != null) - txt.addModifyListener(modifyListener); - return txt; - } - - /** - * Create a label and a text field for a grid layout, the text field grabbing - * excess horizontal - */ - public static Text createGridLT(Composite parent, String label) { - return createGridLT(parent, label, null); - } - - /** - * Creates one label and a text field not editable with background colour of the - * parent (like a label but with selectable text) - */ - public static Text createGridLL(Composite parent, String label, String text) { - Text txt = createGridLT(parent, label); - txt.setText(text); - txt.setEditable(false); - txt.setBackground(parent.getBackground()); - return txt; - } - - /** - * Create a label and a text field with password display for a grid layout, the - * text field grabbing excess horizontal - */ - public static Text createGridLP(Composite parent, String label) { - return createGridLP(parent, label, null); - } - - /** - * Create a label and a text field with password display for a grid layout, the - * text field grabbing excess horizontal. The given modify listener will be - * added to the newly created text field if not null. - */ - public static Text createGridLP(Composite parent, String label, ModifyListener modifyListener) { - Label lbl = new Label(parent, SWT.LEAD); - lbl.setText(label); - lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); - Text txt = new Text(parent, SWT.LEAD | SWT.BORDER | SWT.PASSWORD); - txt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); - if (modifyListener != null) - txt.addModifyListener(modifyListener); - return txt; - } - - // MISCELLANEOUS - - /** Simply checks if a string is not null nor empty */ - public static boolean notEmpty(String stringToTest) { - return !(stringToTest == null || "".equals(stringToTest.trim())); - } - - /** Simply checks if a string is null or empty */ - public static boolean isEmpty(String stringToTest) { - return stringToTest == null || "".equals(stringToTest.trim()); - } -} \ No newline at end of file diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/FileProvider.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/FileProvider.java deleted file mode 100644 index e82505df5..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/FileProvider.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.argeo.eclipse.ui; - -import java.io.InputStream; - -/** - * Used for file download : subclasses must implement model specific methods to - * get a byte array representing a file given is ID. - */ -@Deprecated -public interface FileProvider { - - public byte[] getByteArrayFileFromId(String fileId); - - public InputStream getInputStreamFromFileId(String fileId); - -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/GenericTableComparator.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/GenericTableComparator.java deleted file mode 100644 index e1d8b05ea..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/GenericTableComparator.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.argeo.eclipse.ui; - -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.jface.viewers.ViewerComparator; - -public abstract class GenericTableComparator extends ViewerComparator { - private static final long serialVersionUID = -1175894935075325810L; - protected int propertyIndex; - public static final int ASCENDING = 0, DESCENDING = 1; - protected int direction = DESCENDING; - - /** - * Creates an instance of a sorter for TableViewer. - * - * @param defaultColumnIndex - * the default sorter column - */ - - public GenericTableComparator(int defaultColumnIndex, int direction) { - propertyIndex = defaultColumnIndex; - this.direction = direction; - } - - public void setColumn(int column) { - if (column == this.propertyIndex) { - // Same column as last sort; toggle the direction - direction = 1 - direction; - } else { - // New column; do a descending sort - this.propertyIndex = column; - direction = DESCENDING; - } - } - - /** - * Must be Overriden in each view. - */ - public abstract int compare(Viewer viewer, Object e1, Object e2); -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/IListProvider.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/IListProvider.java deleted file mode 100644 index ac7b2d8fb..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/IListProvider.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.argeo.eclipse.ui; - -import java.util.List; - -/** - * Views and editors can implement this interface so that one of the list that - * is displayed in the part (For instance in a Table or a Tree Viewer) can be - * rebuilt externally. Typically to generate csv or calc extract. - */ -public interface IListProvider { - /** - * Returns an array of current and relevant elements - */ - public Object[] getElements(String extractId); - - /** - * Returns the column definition for passed ID - */ - public List getColumnDefinition(String extractId); -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/ErrorFeedback.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/ErrorFeedback.java deleted file mode 100644 index a388e745e..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/ErrorFeedback.java +++ /dev/null @@ -1,106 +0,0 @@ -package org.argeo.eclipse.ui.dialogs; - -import java.io.PrintWriter; -import java.io.StringWriter; - -import org.argeo.api.cms.CmsLog; -import org.eclipse.jface.dialogs.IMessageProvider; -import org.eclipse.jface.dialogs.TitleAreaDialog; -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Text; - -/** - * Generic error dialog to be used in try/catch blocks. - * - * @deprecated Use CMS dialogs instead. - */ -@Deprecated -public class ErrorFeedback extends TitleAreaDialog { - private static final long serialVersionUID = -8918084784628179044L; - - private final static CmsLog log = CmsLog.getLog(ErrorFeedback.class); - - private final String message; - private final Throwable exception; - - public static void show(String message, Throwable e) { - // rethrow ThreaDeath in order to make sure that RAP will properly clean - // up the UI thread - if (e instanceof ThreadDeath) - throw (ThreadDeath) e; - - new ErrorFeedback(newShell(), message, e).open(); - } - - public static void show(String message) { - new ErrorFeedback(newShell(), message, null).open(); - } - - private static Shell newShell() { - return new Shell(getDisplay(), SWT.NO_TRIM); - } - - /** Tries to find a display */ - private static Display getDisplay() { - try { - Display display = Display.getCurrent(); - if (display != null) - return display; - else - return Display.getDefault(); - } catch (Exception e) { - return Display.getCurrent(); - } - } - - public ErrorFeedback(Shell parentShell, String message, Throwable e) { - super(parentShell); - setShellStyle(SWT.NO_TRIM); - this.message = message; - this.exception = e; - log.error(message, e); - } - - protected Point getInitialSize() { - if (exception != null) - return new Point(800, 600); - else - return new Point(400, 300); - } - - @Override - protected Control createDialogArea(Composite parent) { - Composite dialogarea = (Composite) super.createDialogArea(parent); - dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - Composite composite = new Composite(dialogarea, SWT.NONE); - composite.setLayout(new GridLayout(2, false)); - composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - - setMessage(message != null ? message + (exception != null ? ": " + exception.getMessage() : "") - : exception != null ? exception.getMessage() : "Unkown Error", IMessageProvider.ERROR); - - if (exception != null) { - Text stack = new Text(composite, SWT.MULTI | SWT.LEAD | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL); - stack.setEditable(false); - stack.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - StringWriter sw = new StringWriter(); - exception.printStackTrace(new PrintWriter(sw)); - stack.setText(sw.toString()); - } - - parent.pack(); - return composite; - } - - protected void configureShell(Shell shell) { - super.configureShell(shell); - shell.setText("Error"); - } -} \ No newline at end of file diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/FeedbackDialog.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/FeedbackDialog.java deleted file mode 100644 index f2715bc05..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/FeedbackDialog.java +++ /dev/null @@ -1,141 +0,0 @@ -package org.argeo.eclipse.ui.dialogs; - -import java.io.PrintWriter; -import java.io.StringWriter; - -import org.argeo.api.cms.CmsLog; -import org.argeo.eclipse.ui.EclipseUiException; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.ShellAdapter; -import org.eclipse.swt.events.ShellEvent; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Text; - -/** - * Generic lightweight dialog, not based on JFace. - * - * @deprecated Use CMS dialogs instead. - */ -@Deprecated -public class FeedbackDialog extends LightweightDialog { - private final static CmsLog log = CmsLog.getLog(FeedbackDialog.class); - - private String message; - private Throwable exception; - -// private Shell parentShell; - private Shell shell; - - public static void show(String message, Throwable e) { - // rethrow ThreaDeath in order to make sure that RAP will properly clean - // up the UI thread - if (e instanceof ThreadDeath) - throw (ThreadDeath) e; - - new FeedbackDialog(getDisplay().getActiveShell(), message, e).open(); - } - - public static void show(String message) { - new FeedbackDialog(getDisplay().getActiveShell(), message, null).open(); - } - - /** Tries to find a display */ - private static Display getDisplay() { - try { - Display display = Display.getCurrent(); - if (display != null) - return display; - else - return Display.getDefault(); - } catch (Exception e) { - return Display.getCurrent(); - } - } - - public FeedbackDialog(Shell parentShell, String message, Throwable e) { - super(parentShell); - this.message = message; - this.exception = e; - log.error(message, e); - } - - public int open() { - if (shell != null) - throw new EclipseUiException("There is already a shell"); - shell = new Shell(getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP); - shell.setLayout(new GridLayout()); - // shell.setText("Error"); - shell.setSize(getInitialSize()); - createDialogArea(shell); - // shell.pack(); - // shell.layout(); - - Rectangle shellBounds = Display.getCurrent().getBounds();// RAP - Point dialogSize = shell.getSize(); - int x = shellBounds.x + (shellBounds.width - dialogSize.x) / 2; - int y = shellBounds.y + (shellBounds.height - dialogSize.y) / 2; - shell.setLocation(x, y); - - shell.addShellListener(new ShellAdapter() { - private static final long serialVersionUID = -2701270481953688763L; - - @Override - public void shellDeactivated(ShellEvent e) { - closeShell(); - } - }); - - shell.open(); - return OK; - } - - protected void closeShell() { - shell.close(); - shell.dispose(); - shell = null; - } - - protected Point getInitialSize() { - // if (exception != null) - // return new Point(800, 600); - // else - return new Point(400, 300); - } - - protected Control createDialogArea(Composite parent) { - Composite dialogarea = new Composite(parent, SWT.NONE); - dialogarea.setLayout(new GridLayout()); - // Composite dialogarea = (Composite) super.createDialogArea(parent); - dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - - Label messageLbl = new Label(dialogarea, SWT.NONE); - if (message != null) - messageLbl.setText(message); - else if (exception != null) - messageLbl.setText(exception.getLocalizedMessage()); - - Composite composite = new Composite(dialogarea, SWT.NONE); - composite.setLayout(new GridLayout(2, false)); - composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - - if (exception != null) { - Text stack = new Text(composite, SWT.MULTI | SWT.LEAD | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL); - stack.setEditable(false); - stack.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - StringWriter sw = new StringWriter(); - exception.printStackTrace(new PrintWriter(sw)); - stack.setText(sw.toString()); - } - - // parent.pack(); - return composite; - } -} \ No newline at end of file diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/LightweightDialog.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/LightweightDialog.java deleted file mode 100644 index 615e1417a..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/LightweightDialog.java +++ /dev/null @@ -1,256 +0,0 @@ -package org.argeo.eclipse.ui.dialogs; - -import org.argeo.api.cms.CmsLog; -import org.argeo.eclipse.ui.EclipseUiException; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.FocusEvent; -import org.eclipse.swt.events.FocusListener; -import org.eclipse.swt.events.ShellAdapter; -import org.eclipse.swt.events.ShellEvent; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Shell; - -/** Generic lightweight dialog, not based on JFace. */ -@Deprecated -public class LightweightDialog { - private final static CmsLog log = CmsLog.getLog(LightweightDialog.class); - - // must be the same value as org.eclipse.jface.window.Window#OK - public final static int OK = 0; - // must be the same value as org.eclipse.jface.window.Window#CANCEL - public final static int CANCEL = 1; - - private Shell parentShell; - private Shell backgroundShell; - private Shell foregoundShell; - - private Integer returnCode = null; - private boolean block = true; - - private String title; - - /** Tries to find a display */ - private static Display getDisplay() { - try { - Display display = Display.getCurrent(); - if (display != null) - return display; - else - return Display.getDefault(); - } catch (Exception e) { - return Display.getCurrent(); - } - } - - public LightweightDialog(Shell parentShell) { - this.parentShell = parentShell; - } - - public int open() { - if (foregoundShell != null) - throw new EclipseUiException("There is already a shell"); - backgroundShell = new Shell(parentShell, SWT.ON_TOP); - backgroundShell.setFullScreen(true); - // if (parentShell != null) { - // backgroundShell.setBounds(parentShell.getBounds()); - // } else - // backgroundShell.setMaximized(true); - backgroundShell.setAlpha(128); - backgroundShell.setBackground(getDisplay().getSystemColor(SWT.COLOR_BLACK)); - foregoundShell = new Shell(backgroundShell, SWT.NO_TRIM | SWT.ON_TOP); - if (title != null) - setTitle(title); - foregoundShell.setLayout(new GridLayout()); - foregoundShell.setSize(getInitialSize()); - createDialogArea(foregoundShell); - // shell.pack(); - // shell.layout(); - - Rectangle shellBounds = parentShell != null ? parentShell.getBounds() : Display.getCurrent().getBounds();// RAP - Point dialogSize = foregoundShell.getSize(); - int x = shellBounds.x + (shellBounds.width - dialogSize.x) / 2; - int y = shellBounds.y + (shellBounds.height - dialogSize.y) / 2; - foregoundShell.setLocation(x, y); - - foregoundShell.addShellListener(new ShellAdapter() { - private static final long serialVersionUID = -2701270481953688763L; - - @Override - public void shellDeactivated(ShellEvent e) { - if (hasChildShells()) - return; - if (returnCode == null)// not yet closed - closeShell(CANCEL); - } - - @Override - public void shellClosed(ShellEvent e) { - notifyClose(); - } - - }); - - backgroundShell.open(); - foregoundShell.open(); - // after the foreground shell has been opened - backgroundShell.addFocusListener(new FocusListener() { - private static final long serialVersionUID = 3137408447474661070L; - - @Override - public void focusLost(FocusEvent event) { - } - - @Override - public void focusGained(FocusEvent event) { - if (hasChildShells()) - return; - if (returnCode == null)// not yet closed - closeShell(CANCEL); - } - }); - - if (block) { - block(); - } - if (returnCode == null) - returnCode = OK; - return returnCode; - } - - public void block() { - try { - runEventLoop(foregoundShell); - } catch (ThreadDeath t) { - returnCode = CANCEL; - if (log.isTraceEnabled()) - log.error("Thread death, canceling dialog", t); - } catch (Throwable t) { - returnCode = CANCEL; - log.error("Cannot open blocking lightweight dialog", t); - } - } - - private boolean hasChildShells() { - if (foregoundShell == null) - return false; - return foregoundShell.getShells().length != 0; - } - - // public synchronized int openAndWait() { - // open(); - // while (returnCode == null) - // try { - // wait(100); - // } catch (InterruptedException e) { - // // silent - // } - // return returnCode; - // } - - private synchronized void notifyClose() { - if (returnCode == null) - returnCode = CANCEL; - notifyAll(); - } - - protected void closeShell(int returnCode) { - this.returnCode = returnCode; - if (CANCEL == returnCode) - onCancel(); - if (foregoundShell != null && !foregoundShell.isDisposed()) { - foregoundShell.close(); - foregoundShell.dispose(); - foregoundShell = null; - } - - if (backgroundShell != null && !backgroundShell.isDisposed()) { - backgroundShell.close(); - backgroundShell.dispose(); - } - } - - protected Point getInitialSize() { - // if (exception != null) - // return new Point(800, 600); - // else - return new Point(600, 400); - } - - protected Control createDialogArea(Composite parent) { - Composite dialogarea = new Composite(parent, SWT.NONE); - dialogarea.setLayout(new GridLayout()); - dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - return dialogarea; - } - - protected Shell getBackgroundShell() { - return backgroundShell; - } - - protected Shell getForegoundShell() { - return foregoundShell; - } - - public void setBlockOnOpen(boolean shouldBlock) { - block = shouldBlock; - } - - public void pack() { - foregoundShell.pack(); - } - - private void runEventLoop(Shell loopShell) { - Display display; - if (foregoundShell == null) { - display = Display.getCurrent(); - } else { - display = loopShell.getDisplay(); - } - - while (loopShell != null && !loopShell.isDisposed()) { - try { - if (!display.readAndDispatch()) { - display.sleep(); - } - } catch (UnsupportedOperationException e) { - throw e; - } catch (Throwable e) { - handleException(e); - } - } - if (!display.isDisposed()) - display.update(); - } - - protected void handleException(Throwable t) { - if (t instanceof ThreadDeath) { - // Don't catch ThreadDeath as this is a normal occurrence when - // the thread dies - throw (ThreadDeath) t; - } - // Try to keep running. - t.printStackTrace(); - } - - /** @return false, if the dialog should not be closed. */ - protected boolean onCancel() { - return true; - } - - public void setTitle(String title) { - this.title = title; - if (title != null && getForegoundShell() != null) - getForegoundShell().setText(title); - } - - public Integer getReturnCode() { - return returnCode; - } - -} \ No newline at end of file diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/SingleValue.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/SingleValue.java deleted file mode 100644 index 8ce9b44fb..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/SingleValue.java +++ /dev/null @@ -1,130 +0,0 @@ -package org.argeo.eclipse.ui.dialogs; - -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.eclipse.jface.dialogs.IMessageProvider; -import org.eclipse.jface.dialogs.TitleAreaDialog; -import org.eclipse.jface.window.Window; -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Text; - -/** - * Dialog to retrieve a single value. - * - * @deprecated Use CMS dialogs instead. - */ -@Deprecated -public class SingleValue extends TitleAreaDialog { - private static final long serialVersionUID = 2843538207460082349L; - - private Text valueT; - private String value; - private final String title, message, label; - private final Boolean multiline; - - public static String ask(String label, String message) { - SingleValue svd = new SingleValue(label, message); - if (svd.open() == Window.OK) - return svd.getString(); - else - return null; - } - - public static Long askLong(String label, String message) { - SingleValue svd = new SingleValue(label, message); - if (svd.open() == Window.OK) - return svd.getLong(); - else - return null; - } - - public static Double askDouble(String label, String message) { - SingleValue svd = new SingleValue(label, message); - if (svd.open() == Window.OK) - return svd.getDouble(); - else - return null; - } - - public SingleValue(String label, String message) { - this(Display.getDefault().getActiveShell(), label, message, label, false); - } - - public SingleValue(Shell parentShell, String title, String message, String label, Boolean multiline) { - super(parentShell); - this.title = title; - this.message = message; - this.label = label; - this.multiline = multiline; - } - - protected Point getInitialSize() { - if (multiline) - return new Point(450, 350); - - else - return new Point(400, 270); - } - - protected Control createDialogArea(Composite parent) { - Composite dialogarea = (Composite) super.createDialogArea(parent); - dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - Composite composite = new Composite(dialogarea, SWT.NONE); - composite.setLayoutData(EclipseUiUtils.fillAll()); - GridLayout layout = new GridLayout(2, false); - layout.marginWidth = layout.marginHeight = 20; - composite.setLayout(layout); - - valueT = createLT(composite, label); - - setMessage(message, IMessageProvider.NONE); - - parent.pack(); - valueT.setFocus(); - return composite; - } - - @Override - protected void okPressed() { - value = valueT.getText(); - super.okPressed(); - } - - /** Creates label and text. */ - protected Text createLT(Composite parent, String label) { - new Label(parent, SWT.NONE).setText(label); - Text text; - if (multiline) { - text = new Text(parent, SWT.LEAD | SWT.BORDER | SWT.MULTI); - text.setLayoutData(EclipseUiUtils.fillAll()); - } else { - text = new Text(parent, SWT.LEAD | SWT.BORDER | SWT.SINGLE); - text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true)); - } - return text; - } - - protected void configureShell(Shell shell) { - super.configureShell(shell); - shell.setText(title); - } - - public String getString() { - return value; - } - - public Long getLong() { - return Long.valueOf(getString()); - } - - public Double getDouble() { - return Double.valueOf(getString()); - } -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/package-info.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/package-info.java deleted file mode 100644 index d6ab1481e..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Generic SWT/JFace dialogs. */ -package org.argeo.eclipse.ui.dialogs; \ No newline at end of file diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/AdvancedFsBrowser.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/AdvancedFsBrowser.java deleted file mode 100644 index c01b2d751..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/AdvancedFsBrowser.java +++ /dev/null @@ -1,450 +0,0 @@ -package org.argeo.eclipse.ui.fs; - -import java.io.IOException; -import java.nio.file.DirectoryStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.LinkedHashMap; - -import org.argeo.api.cms.CmsLog; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.eclipse.jface.viewers.ISelectionChangedListener; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.SelectionChangedEvent; -import org.eclipse.jface.viewers.StructuredSelection; -import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.SashForm; -import org.eclipse.swt.custom.ScrolledComposite; -import org.eclipse.swt.events.ControlAdapter; -import org.eclipse.swt.events.ControlEvent; -import org.eclipse.swt.events.KeyEvent; -import org.eclipse.swt.events.KeyListener; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Table; -import org.eclipse.swt.widgets.Text; - -/** Simple UI provider that populates a composite parent given a NIO path */ -public class AdvancedFsBrowser { - private final static CmsLog log = CmsLog.getLog(AdvancedFsBrowser.class); - - // Some local constants to experiment. should be cleaned - // private final static int THUMBNAIL_WIDTH = 400; - // private Point imageWidth = new Point(250, 0); - private final static int COLUMN_WIDTH = 160; - - private Path initialPath; - private Path currEdited; - // Filter - private Composite displayBoxCmp; - private Text parentPathTxt; - private Text filterTxt; - // Browser columns - private ScrolledComposite scrolledCmp; - // Keep a cache of the opened directories - private LinkedHashMap browserCols = new LinkedHashMap<>(); - private Composite scrolledCmpBody; - - public Control createUi(Composite parent, Path basePath) { - if (basePath == null) - throw new IllegalArgumentException("Context cannot be null"); - parent.setLayout(new GridLayout()); - - // top filter - Composite filterCmp = new Composite(parent, SWT.NO_FOCUS); - filterCmp.setLayoutData(EclipseUiUtils.fillWidth()); - addFilterPanel(filterCmp); - - // Bottom part a sash with browser on the left - SashForm form = new SashForm(parent, SWT.HORIZONTAL); - // form.setLayout(new FillLayout()); - form.setLayoutData(EclipseUiUtils.fillAll()); - Composite leftCmp = new Composite(form, SWT.NO_FOCUS); - displayBoxCmp = new Composite(form, SWT.NONE); - form.setWeights(new int[] { 3, 1 }); - - createBrowserPart(leftCmp, basePath); - // leftCmp.addControlListener(new ControlAdapter() { - // @Override - // public void controlResized(ControlEvent e) { - // Rectangle r = leftCmp.getClientArea(); - // log.warn("Browser resized: " + r.toString()); - // scrolledCmp.setMinSize(browserCols.size() * (COLUMN_WIDTH + 2), - // SWT.DEFAULT); - // // scrolledCmp.setMinSize(scrolledCmpBody.computeSize(SWT.DEFAULT, - // // r.height)); - // } - // }); - - populateCurrEditedDisplay(displayBoxCmp, basePath); - - // INIT - setEdited(basePath); - initialPath = basePath; - // form.layout(true, true); - return parent; - } - - private void createBrowserPart(Composite parent, Path context) { - parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); - - // scrolled composite - scrolledCmp = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.BORDER | SWT.NO_FOCUS); - scrolledCmp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - scrolledCmp.setExpandVertical(true); - scrolledCmp.setExpandHorizontal(true); - scrolledCmp.setShowFocusedControl(true); - - scrolledCmpBody = new Composite(scrolledCmp, SWT.NO_FOCUS); - scrolledCmp.setContent(scrolledCmpBody); - scrolledCmpBody.addControlListener(new ControlAdapter() { - private static final long serialVersionUID = 183238447102854553L; - - @Override - public void controlResized(ControlEvent e) { - Rectangle r = scrolledCmp.getClientArea(); - scrolledCmp.setMinSize(scrolledCmpBody.computeSize(SWT.DEFAULT, r.height)); - } - }); - initExplorer(scrolledCmpBody, context); - scrolledCmpBody.layout(true, true); - scrolledCmp.layout(); - - } - - private Control initExplorer(Composite parent, Path context) { - parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); - return createBrowserColumn(parent, context); - } - - private Control createBrowserColumn(Composite parent, Path context) { - // TODO style is not correctly managed. - FilterEntitiesVirtualTable table = new FilterEntitiesVirtualTable(parent, SWT.BORDER | SWT.NO_FOCUS, context); - // CmsUtils.style(table, ArgeoOrgStyle.browserColumn.style()); - table.filterList("*"); - table.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, false, true)); - browserCols.put(context, table); - parent.layout(true, true); - return table; - } - - public void addFilterPanel(Composite parent) { - parent.setLayout(EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, false))); - - parentPathTxt = new Text(parent, SWT.NO_FOCUS); - parentPathTxt.setEditable(false); - - filterTxt = new Text(parent, SWT.SEARCH | SWT.ICON_CANCEL); - filterTxt.setMessage("Filter current list"); - filterTxt.setLayoutData(EclipseUiUtils.fillWidth()); - 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(currEdited, 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 FsUiException( - "Unable to determine unique child existence and get it under " + parent + " with filter " + filter, - ioe); - } - } - - private void setEdited(Path path) { - currEdited = path; - EclipseUiUtils.clear(displayBoxCmp); - populateCurrEditedDisplay(displayBoxCmp, currEdited); - refreshFilters(path); - refreshBrowser(path); - } - - private void refreshFilters(Path path) { - parentPathTxt.setText(path.toUri().toString()); - filterTxt.setText(""); - filterTxt.getParent().layout(); - } - - private void refreshBrowser(Path currPath) { - Path currParPath = currPath.getParent(); - Object[][] colMatrix = new Object[browserCols.size()][2]; - - int i = 0, currPathIndex = -1, lastLeftOpenedIndex = -1; - for (Path path : browserCols.keySet()) { - colMatrix[i][0] = path; - colMatrix[i][1] = browserCols.get(path); - if (currPathIndex >= 0 && lastLeftOpenedIndex < 0 && currParPath != null) { - boolean leaveOpened = path.startsWith(currPath); - if (!leaveOpened) - lastLeftOpenedIndex = i; - } - if (currParPath.equals(path)) - currPathIndex = i; - i++; - } - - if (currPathIndex >= 0 && lastLeftOpenedIndex >= 0) { - // dispose and remove useless cols - for (int l = i - 1; l >= lastLeftOpenedIndex; l--) { - ((FilterEntitiesVirtualTable) colMatrix[l][1]).dispose(); - browserCols.remove(colMatrix[l][0]); - } - } - - if (browserCols.containsKey(currPath)) { - FilterEntitiesVirtualTable currCol = browserCols.get(currPath); - if (currCol.isDisposed()) { - // Does it still happen ? - log.warn(currPath + " browser column was disposed and still listed"); - browserCols.remove(currPath); - } - } - - if (!browserCols.containsKey(currPath) && Files.isDirectory(currPath)) - createBrowserColumn(scrolledCmpBody, currPath); - - scrolledCmpBody.setLayout(EclipseUiUtils.noSpaceGridLayout(new GridLayout(browserCols.size(), false))); - scrolledCmpBody.layout(true, true); - // also resize the scrolled composite - scrolledCmp.layout(); - } - - private void modifyFilter(boolean fromOutside) { - if (!fromOutside) - if (currEdited != null) { - String filter = filterTxt.getText() + "*"; - FilterEntitiesVirtualTable table = browserCols.get(currEdited); - if (table != null && !table.isDisposed()) - table.filterList(filter); - } - } - - /** - * Recreates the content of the box that displays information about the current - * selected node. - */ - private void populateCurrEditedDisplay(Composite parent, Path context) { - parent.setLayout(new GridLayout()); - - // if (isImg(context)) { - // EditableImage image = new Img(parent, RIGHT, context, imageWidth); - // image.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false, - // 2, 1)); - // } - - try { - Label contextL = new Label(parent, SWT.NONE); - contextL.setText(context.getFileName().toString()); - contextL.setFont(EclipseUiUtils.getBoldFont(parent)); - addProperty(parent, "Last modified", Files.getLastModifiedTime(context).toString()); - addProperty(parent, "Owner", Files.getOwner(context).getName()); - if (Files.isDirectory(context)) { - addProperty(parent, "Type", "Folder"); - } else { - String mimeType = Files.probeContentType(context); - if (EclipseUiUtils.isEmpty(mimeType)) - mimeType = "Unknown"; - addProperty(parent, "Type", mimeType); - addProperty(parent, "Size", FsUiUtils.humanReadableByteCount(Files.size(context), false)); - } - parent.layout(true, true); - } catch (IOException e) { - throw new FsUiException("Cannot display details for " + context, e); - } - } - - private void addProperty(Composite parent, String propName, String value) { - Label contextL = new Label(parent, SWT.NONE); - contextL.setText(propName + ": " + value); - } - - /** - * Almost canonical implementation of a table that displays the content of a - * directory - */ - private class FilterEntitiesVirtualTable extends Composite { - private static final long serialVersionUID = 2223410043691844875L; - - // Context - private Path context; - private Path currSelected = null; - - // UI Objects - private FsTableViewer viewer; - - @Override - public boolean setFocus() { - if (viewer.getTable().isDisposed()) - return false; - if (currSelected != null) - viewer.setSelection(new StructuredSelection(currSelected), true); - else if (viewer.getSelection().isEmpty()) { - Object first = viewer.getElementAt(0); - if (first != null) - viewer.setSelection(new StructuredSelection(first), true); - } - return viewer.getTable().setFocus(); - } - - /** - * Enable highlighting the correct element in the table when externally browsing - * (typically via the command-line-like Text field) - */ - void setSelected(Path selected) { - // to prevent change selection event to be thrown - currSelected = selected; - viewer.setSelection(new StructuredSelection(currSelected), true); - } - - void filterList(String filter) { - viewer.setInput(context, filter); - } - - public FilterEntitiesVirtualTable(Composite parent, int style, Path context) { - super(parent, SWT.NO_FOCUS); - this.context = context; - createTableViewer(this); - } - - private void createTableViewer(final Composite parent) { - parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); - - // We must limit the size of the table otherwise the full list is - // loaded before the layout happens - // Composite listCmp = new Composite(parent, SWT.NO_FOCUS); - // GridData gd = new GridData(SWT.LEFT, SWT.FILL, false, true); - // gd.widthHint = COLUMN_WIDTH; - // listCmp.setLayoutData(gd); - // listCmp.setLayout(EclipseUiUtils.noSpaceGridLayout()); - // viewer = new TableViewer(listCmp, SWT.VIRTUAL | SWT.MULTI | - // SWT.V_SCROLL); - // Table table = viewer.getTable(); - // table.setLayoutData(EclipseUiUtils.fillAll()); - - viewer = new FsTableViewer(parent, SWT.MULTI); - Table table = viewer.configureDefaultSingleColumnTable(COLUMN_WIDTH); - - viewer.addSelectionChangedListener(new ISelectionChangedListener() { - - @Override - public void selectionChanged(SelectionChangedEvent event) { - IStructuredSelection selection = (IStructuredSelection) viewer.getSelection(); - if (selection.isEmpty()) - return; - Object obj = selection.getFirstElement(); - Path newSelected; - if (obj instanceof Path) - newSelected = (Path) obj; - else if (obj instanceof ParentDir) - newSelected = ((ParentDir) obj).getPath(); - else - return; - if (newSelected.equals(currSelected)) - return; - currSelected = newSelected; - setEdited(newSelected); - - } - }); - - table.addKeyListener(new KeyListener() { - private static final long serialVersionUID = -8083424284436715709L; - - @Override - public void keyReleased(KeyEvent e) { - } - - @Override - public void keyPressed(KeyEvent e) { - IStructuredSelection selection = (IStructuredSelection) viewer.getSelection(); - Path selected = null; - if (!selection.isEmpty()) - selected = ((Path) selection.getFirstElement()); - if (e.keyCode == SWT.ARROW_RIGHT) { - if (!Files.isDirectory(selected)) - return; - if (selected != null) { - setEdited(selected); - browserCols.get(selected).setFocus(); - } - } else if (e.keyCode == SWT.ARROW_LEFT) { - if (context.equals(initialPath)) - return; - Path parent = context.getParent(); - if (parent == null) - return; - - setEdited(parent); - browserCols.get(parent).setFocus(); - } - } - }); - } - } -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FileIconNameLabelProvider.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FileIconNameLabelProvider.java deleted file mode 100644 index d3fc1c903..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FileIconNameLabelProvider.java +++ /dev/null @@ -1,84 +0,0 @@ -package org.argeo.eclipse.ui.fs; - -import java.nio.file.Files; -import java.nio.file.Path; - -import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.swt.graphics.Image; - -/** Basic label provider with icon for NIO file viewers */ -public class FileIconNameLabelProvider extends ColumnLabelProvider { - private static final long serialVersionUID = 8187902187946523148L; - - private Image folderIcon; - private Image fileIcon; - - public FileIconNameLabelProvider() { - // if (!PlatformUI.isWorkbenchRunning()) { - folderIcon = ImageDescriptor.createFromFile(getClass(), "folder.png").createImage(); - fileIcon = ImageDescriptor.createFromFile(getClass(), "file.png").createImage(); - // } - } - - @Override - public void dispose() { - if (folderIcon != null) - folderIcon.dispose(); - if (fileIcon != null) - fileIcon.dispose(); - super.dispose(); - } - - @Override - public String getText(Object element) { - if (element instanceof Path) { - Path curr = ((Path) element); - Path name = curr.getFileName(); - if (name == null) - return "[No name]"; - else - return name.toString(); - } else if (element instanceof ParentDir) { - return ".."; - } - return null; - } - - @Override - public Image getImage(Object element) { - if (element instanceof Path) { - Path curr = ((Path) element); - if (Files.isDirectory(curr)) - // if (folderIcon != null) - return folderIcon; - // else - // return - // PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FOLDER); - // else if (fileIcon != null) - return fileIcon; - // else - // return - // PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FILE); - } else if (element instanceof ParentDir) { - return folderIcon; - } - return null; - } - - @Override - public String getToolTipText(Object element) { - if (element instanceof Path) { - Path curr = ((Path) element); - Path name = curr.getFileName(); - if (name == null) - return "[No name]"; - else - return name.toAbsolutePath().toString(); - } else if (element instanceof ParentDir) { - return ((ParentDir) element).getPath().toAbsolutePath().toString(); - } - return null; - } - -} \ No newline at end of file diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsTableViewer.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsTableViewer.java deleted file mode 100644 index 3b126e90b..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsTableViewer.java +++ /dev/null @@ -1,141 +0,0 @@ -package org.argeo.eclipse.ui.fs; - -import java.nio.file.Path; -import java.util.List; - -import org.argeo.eclipse.ui.ColumnDefinition; -import org.eclipse.jface.viewers.CellLabelProvider; -import org.eclipse.jface.viewers.ILazyContentProvider; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.jface.viewers.TableViewerColumn; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Table; -import org.eclipse.swt.widgets.TableColumn; - -/** - * Canonical implementation of a JFace table viewer to display the content of a - * file folder - */ -public class FsTableViewer extends TableViewer { - private static final long serialVersionUID = -5632407542678477234L; - - private boolean showHiddenItems = false; - private boolean folderFirst = true; - private boolean reverseOrder = false; - private String orderProperty = FsUiConstants.PROPERTY_NAME; - - private Path initialPath = null; - - public FsTableViewer(Composite parent, int style) { - super(parent, style | SWT.VIRTUAL); - } - - public Table configureDefaultSingleColumnTable(int tableWidthHint) { - - return configureDefaultSingleColumnTable(tableWidthHint, new FileIconNameLabelProvider()); - } - - public Table configureDefaultSingleColumnTable(int tableWidthHint, CellLabelProvider labelProvider) { - Table table = this.getTable(); - table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); - table.setLinesVisible(false); - table.setHeaderVisible(false); - // CmsUtils.markup(table); - // CmsUtils.style(table, MaintenanceStyles.BROWSER_COLUMN); - - TableViewerColumn column = new TableViewerColumn(this, SWT.NONE); - TableColumn tcol = column.getColumn(); - tcol.setWidth(tableWidthHint); - column.setLabelProvider(labelProvider); - this.setContentProvider(new MyLazyCP()); - return table; - } - - public Table configureDefaultTable(List columns) { - this.setContentProvider(new MyLazyCP()); - Table table = this.getTable(); - table.setLinesVisible(true); - table.setHeaderVisible(true); - // CmsUtils.markup(table); - // CmsUtils.style(table, MaintenanceStyles.BROWSER_COLUMN); - for (ColumnDefinition colDef : columns) { - TableViewerColumn column = new TableViewerColumn(this, SWT.NONE); - column.setLabelProvider(colDef.getLabelProvider()); - TableColumn tcol = column.getColumn(); - tcol.setResizable(true); - tcol.setText(colDef.getLabel()); - tcol.setWidth(colDef.getMinWidth()); - } - return table; - } - - public void setInput(Path dir, String filter) { - Path[] rows = FsUiUtils.getChildren(dir, filter, showHiddenItems, folderFirst, orderProperty, reverseOrder); - if (rows == null) { - this.setInput(null); - this.setItemCount(0); - return; - } - boolean isRoot; - try { - isRoot = dir.getRoot().equals(dir); - } catch (Exception e) { - // FIXME Workaround for JCR root node access - isRoot = dir.toString().equals("/"); - } - final Object[] res; - if (isRoot) - res = rows; - else if (initialPath != null && initialPath.equals(dir)) - res = rows; - else { - res = new Object[rows.length + 1]; - res[0] = new ParentDir(dir.getParent()); - for (int i = 1; i < res.length; i++) { - res[i] = rows[i - 1]; - } - } - this.setInput(res); - int length = res.length; - this.setItemCount(length); - this.refresh(); - } - - /** Directly displays bookmarks **/ - public void setPathsInput(Path... paths) { - this.setInput((Object[]) paths); - this.setItemCount(paths.length); - this.refresh(); - } - - /** - * A path which is to be considered as root (and thus provide no link to a - * parent directory) - */ - public void setInitialPath(Path initialPath) { - this.initialPath = initialPath; - } - - private class MyLazyCP implements ILazyContentProvider { - private static final long serialVersionUID = 9096550041395433128L; - private Object[] elements; - - public void dispose() { - } - - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - // IMPORTANT: don't forget this: an exception will be thrown if - // a selected object is not part of the results anymore. - viewer.setSelection(null); - this.elements = (Object[]) newInput; - } - - public void updateElement(int index) { - if (index < elements.length) - FsTableViewer.this.replace(elements[index], index); - } - } -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsTreeViewer.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsTreeViewer.java deleted file mode 100644 index f55ead718..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsTreeViewer.java +++ /dev/null @@ -1,144 +0,0 @@ -package org.argeo.eclipse.ui.fs; - -import java.io.IOException; -import java.nio.file.DirectoryIteratorException; -import java.nio.file.DirectoryStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; - -import org.argeo.eclipse.ui.ColumnDefinition; -import org.eclipse.jface.viewers.ITreeContentProvider; -import org.eclipse.jface.viewers.TreeViewer; -import org.eclipse.jface.viewers.TreeViewerColumn; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Tree; -import org.eclipse.swt.widgets.TreeColumn; - -/** - * Canonical implementation of a JFace TreeViewer to display the content of a - * repository - */ -public class FsTreeViewer extends TreeViewer { - private static final long serialVersionUID = -5632407542678477234L; - - private boolean showHiddenItems = false; - private boolean showDirectoryFirst = true; - private String orderingProperty = FsUiConstants.PROPERTY_NAME; - - public FsTreeViewer(Composite parent, int style) { - super(parent, style | SWT.VIRTUAL); - } - - public Tree configureDefaultSingleColumnTable(int tableWidthHint) { - Tree tree = this.getTree(); - tree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); - tree.setLinesVisible(true); - tree.setHeaderVisible(false); -// CmsUtils.markup(tree); - - TreeViewerColumn column = new TreeViewerColumn(this, SWT.NONE); - TreeColumn tcol = column.getColumn(); - tcol.setWidth(tableWidthHint); - column.setLabelProvider(new FileIconNameLabelProvider()); - - this.setContentProvider(new MyCP()); - return tree; - } - - public Tree configureDefaultTable(List columns) { - this.setContentProvider(new MyCP()); - Tree tree = this.getTree(); - tree.setLinesVisible(true); - tree.setHeaderVisible(true); -// CmsUtils.markup(tree); -// CmsUtils.style(tree, MaintenanceStyles.BROWSER_COLUMN); - for (ColumnDefinition colDef : columns) { - TreeViewerColumn column = new TreeViewerColumn(this, SWT.NONE); - column.setLabelProvider(colDef.getLabelProvider()); - TreeColumn tcol = column.getColumn(); - tcol.setResizable(true); - tcol.setText(colDef.getLabel()); - tcol.setWidth(colDef.getMinWidth()); - } - return tree; - } - - public void setInput(Path dir, String filter) { - try (DirectoryStream stream = Files.newDirectoryStream(dir, filter)) { - // TODO make this lazy - List paths = new ArrayList<>(); - for (Path entry : stream) { - paths.add(entry); - } - Object[] rows = paths.toArray(new Object[0]); - this.setInput(rows); - // this.setItemCount(rows.length); - this.refresh(); - } catch (IOException | DirectoryIteratorException e) { - throw new FsUiException("Unable to filter " + dir + " children with filter " + filter, e); - } - } - - /** Directly displays bookmarks **/ - public void setPathsInput(Path... paths) { - this.setInput((Object[]) paths); - // this.setItemCount(paths.length); - this.refresh(); - } - - private class MyCP implements ITreeContentProvider { - private static final long serialVersionUID = 9096550041395433128L; - private Object[] elements; - - public void dispose() { - } - - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - // IMPORTANT: don't forget this: an exception will be thrown if - // a selected object is not part of the results anymore. - viewer.setSelection(null); - this.elements = (Object[]) newInput; - } - - @Override - public Object[] getElements(Object inputElement) { - return elements; - } - - @Override - public Object[] getChildren(Object parentElement) { - Path path = (Path) parentElement; - if (!Files.isDirectory(path)) - return null; - else - return FsUiUtils.getChildren(path, "*", showHiddenItems, showDirectoryFirst, orderingProperty, false); - } - - @Override - public Object getParent(Object element) { - Path path = (Path) element; - return path.getParent(); - } - - @Override - public boolean hasChildren(Object element) { - Path path = (Path) element; - try { - if (!Files.isDirectory(path)) - return false; - else - try (DirectoryStream children = Files.newDirectoryStream(path, "*")) { - return children.iterator().hasNext(); - } - } catch (IOException e) { - throw new FsUiException("Unable to check child existence on " + path, e); - } - } - - } -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiConstants.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiConstants.java deleted file mode 100644 index 2b51e71a2..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiConstants.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.argeo.eclipse.ui.fs; - -/** Centralize constants used by the Nio FS UI parts */ -public interface FsUiConstants { - - // TODO use standard properties - String PROPERTY_NAME = "name"; - String PROPERTY_SIZE = "size"; - String PROPERTY_LAST_MODIFIED = "last-modified"; - String PROPERTY_TYPE = "type"; -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiException.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiException.java deleted file mode 100644 index 422b0e1ad..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiException.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.argeo.eclipse.ui.fs; - -/** Files specific exception */ -public class FsUiException extends RuntimeException { - private static final long serialVersionUID = 1L; - - public FsUiException(String message) { - super(message); - } - - public FsUiException(String message, Throwable e) { - super(message, e); - } -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiUtils.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiUtils.java deleted file mode 100644 index 956d96bb5..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiUtils.java +++ /dev/null @@ -1,132 +0,0 @@ -package org.argeo.eclipse.ui.fs; - -import java.io.IOException; -import java.nio.file.DirectoryIteratorException; -import java.nio.file.DirectoryStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** Centralise additional utilitary methods to manage Java7 NIO files */ -public class FsUiUtils { - - /** - * thanks to - * http://programming.guide/java/formatting-byte-size-to-human-readable-format.html - */ - public static String humanReadableByteCount(long bytes, boolean si) { - int unit = si ? 1000 : 1024; - if (bytes < unit) - return bytes + " B"; - int exp = (int) (Math.log(bytes) / Math.log(unit)); - String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + (si ? "" : "i"); - return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre); - } - - public static Path[] getChildren(Path parent, String filter, boolean showHiddenItems, boolean folderFirst, - String orderProperty, boolean reverseOrder) { - if (!Files.isDirectory(parent)) - return null; - List pairs = new ArrayList<>(); - try (DirectoryStream stream = Files.newDirectoryStream(parent, filter)) { - loop: for (Path entry : stream) { - if (!showHiddenItems) - if (Files.isHidden(entry)) - continue loop; - switch (orderProperty) { - case FsUiConstants.PROPERTY_SIZE: - if (folderFirst) - pairs.add(new LPair(entry, Files.size(entry), Files.isDirectory(entry))); - else - pairs.add(new LPair(entry, Files.size(entry))); - break; - case FsUiConstants.PROPERTY_LAST_MODIFIED: - if (folderFirst) - pairs.add(new LPair(entry, Files.getLastModifiedTime(entry).toMillis(), - Files.isDirectory(entry))); - else - pairs.add(new LPair(entry, Files.getLastModifiedTime(entry).toMillis())); - break; - case FsUiConstants.PROPERTY_NAME: - if (folderFirst) - pairs.add(new SPair(entry, entry.getFileName().toString(), Files.isDirectory(entry))); - else - pairs.add(new SPair(entry, entry.getFileName().toString())); - break; - default: - throw new FsUiException("Unable to prepare sort for property " + orderProperty); - } - } - Pair[] rows = pairs.toArray(new Pair[0]); - Arrays.sort(rows); - Path[] results = new Path[rows.length]; - if (reverseOrder) { - int j = rows.length - 1; - for (int i = 0; i < rows.length; i++) - results[i] = rows[j - i].p; - } else - for (int i = 0; i < rows.length; i++) - results[i] = rows[i].p; - return results; - } catch (IOException | DirectoryIteratorException e) { - throw new FsUiException("Unable to filter " + parent + " children with filter " + filter, e); - } - } - - static abstract class Pair implements Comparable { - Path p; - Boolean i; - }; - - static class LPair extends Pair { - long v; - - public LPair(Path path, long propValue) { - p = path; - v = propValue; - } - - public LPair(Path path, long propValue, boolean isDir) { - p = path; - v = propValue; - i = isDir; - } - - public int compareTo(Object o) { - if (i != null) { - Boolean j = ((LPair) o).i; - if (i.booleanValue() != j.booleanValue()) - return i.booleanValue() ? -1 : 1; - } - long u = ((LPair) o).v; - return v < u ? -1 : v == u ? 0 : 1; - } - }; - - static class SPair extends Pair { - String v; - - public SPair(Path path, String propValue) { - p = path; - v = propValue; - } - - public SPair(Path path, String propValue, boolean isDir) { - p = path; - v = propValue; - i = isDir; - } - - public int compareTo(Object o) { - if (i != null) { - Boolean j = ((SPair) o).i; - if (i.booleanValue() != j.booleanValue()) - return i.booleanValue() ? -1 : 1; - } - String u = ((SPair) o).v; - return v.compareTo(u); - } - }; -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/NioFileLabelProvider.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/NioFileLabelProvider.java deleted file mode 100644 index 2bb65eed0..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/NioFileLabelProvider.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.argeo.eclipse.ui.fs; - -import java.io.IOException; -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.Date; - -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.eclipse.jface.viewers.ColumnLabelProvider; - -/** Expect a {@link Path} as input element */ -public class NioFileLabelProvider extends ColumnLabelProvider { - private final static FileTime EPOCH = FileTime.fromMillis(0); - private static final long serialVersionUID = 2160026425187796930L; - private final String propName; - private DateFormat dateFormat = new SimpleDateFormat("YYYY-MM-dd HH:mm"); - - // TODO use new formatting - // DateTimeFormatter formatter = - // DateTimeFormatter.ofLocalizedDateTime( FormatStyle.SHORT ) - // .withLocale( Locale.UK ) - // .withZone( ZoneId.systemDefault() ); - public NioFileLabelProvider(String propName) { - this.propName = propName; - } - - @Override - public String getText(Object element) { - try { - Path path; - if (element instanceof ParentDir) { -// switch (propName) { -// case FsUiConstants.PROPERTY_SIZE: -// return "-"; -// case FsUiConstants.PROPERTY_LAST_MODIFIED: -// return "-"; -// // return Files.getLastModifiedTime(((ParentDir) element).getPath()).toString(); -// case FsUiConstants.PROPERTY_TYPE: -// return "Folder"; -// } - path = ((ParentDir) element).getPath(); - } else - path = (Path) element; - switch (propName) { - case FsUiConstants.PROPERTY_SIZE: - if (Files.isDirectory(path)) - return "-"; - else - return FsUiUtils.humanReadableByteCount(Files.size(path), false); - case FsUiConstants.PROPERTY_LAST_MODIFIED: - if (Files.isDirectory(path)) - return "-"; - FileTime time = Files.getLastModifiedTime(path); - if (time.equals(EPOCH)) - return "-"; - else - return dateFormat.format(new Date(time.toMillis())); - case FsUiConstants.PROPERTY_TYPE: - if (Files.isDirectory(path)) - return "Folder"; - else { - String mimeType = Files.probeContentType(path); - if (EclipseUiUtils.isEmpty(mimeType)) - return "Unknown"; - else - return mimeType; - } - default: - throw new IllegalArgumentException("Unsupported property " + propName); - } - } catch (IOException ioe) { - throw new FsUiException("Cannot get property " + propName + " on " + element); - } - } -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/ParentDir.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/ParentDir.java deleted file mode 100644 index 6f09c2905..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/ParentDir.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.argeo.eclipse.ui.fs; - -import java.nio.file.Path; - -/** A parent directory (..) reference. */ -public class ParentDir { - Path path; - - public ParentDir(Path path) { - super(); - this.path = path; - } - - public Path getPath() { - return path; - } - - @Override - public int hashCode() { - return path.hashCode(); - } - - @Override - public String toString() { - return "Parent dir " + path; - } - -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/SimpleFsBrowser.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/SimpleFsBrowser.java deleted file mode 100644 index 2e3d6b405..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/SimpleFsBrowser.java +++ /dev/null @@ -1,211 +0,0 @@ -package org.argeo.eclipse.ui.fs; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; - -import org.argeo.api.cms.CmsLog; -import org.argeo.eclipse.ui.ColumnDefinition; -import org.argeo.eclipse.ui.EclipseUiUtils; -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.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Table; - -/** - * Experimental UI upon Java 7 nio files api: SashForm layout with bookmarks on - * the left hand side and a simple table on the right hand side. - */ -public class SimpleFsBrowser extends Composite { - private final static CmsLog log = CmsLog.getLog(SimpleFsBrowser.class); - private static final long serialVersionUID = -40347919096946585L; - - private Path currSelected; - private FsTableViewer bookmarksViewer; - private FsTableViewer directoryDisplayViewer; - - public SimpleFsBrowser(Composite parent, int style) { - super(parent, style); - createContent(this); - // parent.layout(true, true); - } - - public Viewer getViewer() { - return directoryDisplayViewer; - } - - private void createContent(Composite parent) { - parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); - - SashForm form = new SashForm(parent, SWT.HORIZONTAL); - Composite leftCmp = new Composite(form, SWT.NONE); - populateBookmarks(leftCmp); - - Composite rightCmp = new Composite(form, SWT.BORDER); - populateDisplay(rightCmp); - form.setLayoutData(EclipseUiUtils.fillAll()); - form.setWeights(new int[] { 1, 3 }); - } - - public void setInput(Path... paths) { - bookmarksViewer.setPathsInput(paths); - bookmarksViewer.getTable().getParent().layout(true, true); - } - - private void populateBookmarks(final Composite parent) { - // GridLayout layout = EclipseUiUtils.noSpaceGridLayout(); - // layout.verticalSpacing = 5; - parent.setLayout(new GridLayout()); - - ISelectionChangedListener selList = new MySelectionChangedListener(); - - appendTitle(parent, "My bookmarks"); - bookmarksViewer = new FsTableViewer(parent, SWT.MULTI | SWT.NO_SCROLL); - Table table = bookmarksViewer.configureDefaultSingleColumnTable(500); - GridData gd = EclipseUiUtils.fillWidth(); - gd.horizontalIndent = 10; - table.setLayoutData(gd); - bookmarksViewer.addSelectionChangedListener(selList); - - appendTitle(parent, "Jcr + File"); - - FsTableViewer jcrFilesViewers = new FsTableViewer(parent, SWT.MULTI | SWT.NO_SCROLL); - table = jcrFilesViewers.configureDefaultSingleColumnTable(500); - gd = EclipseUiUtils.fillWidth(); - gd.horizontalIndent = 10; - table.setLayoutData(gd); - jcrFilesViewers.addSelectionChangedListener(selList); - - // FileSystemProvider fsProvider = new JackrabbitMemoryFsProvider(); - // try { - // Path testPath = fsProvider.getPath(new URI("jcr+memory:/")); - // jcrFilesViewers.setPathsInput(testPath); - // } catch (URISyntaxException e) { - // // TODO Auto-generated catch block - // e.printStackTrace(); - // } - } - - private Label appendTitle(Composite parent, String value) { - Label titleLbl = new Label(parent, SWT.NONE); - titleLbl.setText(value); - titleLbl.setFont(EclipseUiUtils.getBoldFont(parent)); - GridData gd = EclipseUiUtils.fillWidth(); - gd.horizontalIndent = 5; - gd.verticalIndent = 5; - titleLbl.setLayoutData(gd); - return titleLbl; - } - - private class MySelectionChangedListener implements ISelectionChangedListener { - @Override - public void selectionChanged(SelectionChangedEvent event) { - IStructuredSelection selection = (IStructuredSelection) bookmarksViewer.getSelection(); - if (selection.isEmpty()) - return; - else { - Path newSelected = (Path) selection.getFirstElement(); - if (newSelected.equals(currSelected)) - return; - currSelected = newSelected; - directoryDisplayViewer.setInput(currSelected, "*"); - } - } - } - - private void populateDisplay(final Composite parent) { - parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); - directoryDisplayViewer = new FsTableViewer(parent, SWT.MULTI); - List colDefs = new ArrayList<>(); - colDefs.add(new ColumnDefinition(new FileIconNameLabelProvider(), "Name", 200)); - colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_SIZE), "Size", 100)); - colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_TYPE), "Type", 250)); - colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_LAST_MODIFIED), - "Last modified", 200)); - Table table = directoryDisplayViewer.configureDefaultTable(colDefs); - table.setLayoutData(EclipseUiUtils.fillAll()); - - table.addKeyListener(new KeyListener() { - private static final long serialVersionUID = -8083424284436715709L; - - @Override - public void keyReleased(KeyEvent e) { - } - - @Override - public void keyPressed(KeyEvent e) { - log.debug("Key event received: " + e.keyCode); - IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection(); - Path selected = null; - if (!selection.isEmpty()) - selected = ((Path) selection.getFirstElement()); - if (e.keyCode == SWT.CR) { - if (!Files.isDirectory(selected)) - return; - if (selected != null) { - currSelected = selected; - directoryDisplayViewer.setInput(currSelected, "*"); - } - } else if (e.keyCode == SWT.BS) { - currSelected = currSelected.getParent(); - directoryDisplayViewer.setInput(currSelected, "*"); - directoryDisplayViewer.getTable().setFocus(); - } - } - }); - -// directoryDisplayViewer.addDoubleClickListener(new IDoubleClickListener() { -// @Override -// public void doubleClick(DoubleClickEvent event) { -// IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection(); -// Path selected = null; -// if (!selection.isEmpty()) { -// Object obj = selection.getFirstElement(); -// if (obj instanceof Path) -// selected = (Path) obj; -// else if (obj instanceof ParentDir) -// selected = ((ParentDir) obj).getPath(); -// } -// if (selected != null) { -// if (!Files.isDirectory(selected)) -// return; -// currSelected = selected; -// directoryDisplayViewer.setInput(currSelected, "*"); -// } -// } -// }); - - directoryDisplayViewer.addDoubleClickListener(new IDoubleClickListener() { - @Override - public void doubleClick(DoubleClickEvent event) { - IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection(); - Path selected = null; - if (!selection.isEmpty()) { - Object obj = selection.getFirstElement(); - if (obj instanceof Path) - selected = (Path) obj; - else if (obj instanceof ParentDir) - selected = ((ParentDir) obj).getPath(); - } - if (selected != null) { - if (!Files.isDirectory(selected)) - return; - currSelected = selected; - directoryDisplayViewer.setInput(currSelected, "*"); - } - } - }); - } -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/SimpleFsTreeBrowser.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/SimpleFsTreeBrowser.java deleted file mode 100644 index 401e5cb5e..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/SimpleFsTreeBrowser.java +++ /dev/null @@ -1,128 +0,0 @@ -package org.argeo.eclipse.ui.fs; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; - -import org.argeo.api.cms.CmsLog; -import org.argeo.eclipse.ui.ColumnDefinition; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.eclipse.jface.viewers.ISelectionChangedListener; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.SelectionChangedEvent; -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.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Table; -import org.eclipse.swt.widgets.Tree; - -/** A simple Java 7 nio files browser with a tree */ -public class SimpleFsTreeBrowser extends Composite { - private final static CmsLog log = CmsLog.getLog(SimpleFsTreeBrowser.class); - private static final long serialVersionUID = -40347919096946585L; - - private Path currSelected; - private FsTreeViewer treeViewer; - private FsTableViewer directoryDisplayViewer; - - public SimpleFsTreeBrowser(Composite parent, int style) { - super(parent, style); - createContent(this); - // parent.layout(true, true); - } - - private void createContent(Composite parent) { - parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); - SashForm form = new SashForm(parent, SWT.HORIZONTAL); - Composite child1 = new Composite(form, SWT.NONE); - populateTree(child1); - Composite child2 = new Composite(form, SWT.BORDER); - populateDisplay(child2); - form.setLayoutData(EclipseUiUtils.fillAll()); - form.setWeights(new int[] { 1, 3 }); - } - - public void setInput(Path... paths) { - treeViewer.setPathsInput(paths); - treeViewer.getControl().getParent().layout(true, true); - } - - private void populateTree(final Composite parent) { - // GridLayout layout = EclipseUiUtils.noSpaceGridLayout(); - // layout.verticalSpacing = 5; - parent.setLayout(new GridLayout()); - - ISelectionChangedListener selList = new MySelectionChangedListener(); - - treeViewer = new FsTreeViewer(parent, SWT.MULTI); - Tree tree = treeViewer.configureDefaultSingleColumnTable(500); - GridData gd = EclipseUiUtils.fillAll(); - // gd.horizontalIndent = 10; - tree.setLayoutData(gd); - treeViewer.addSelectionChangedListener(selList); - } - - private class MySelectionChangedListener implements ISelectionChangedListener { - @Override - public void selectionChanged(SelectionChangedEvent event) { - IStructuredSelection selection = (IStructuredSelection) treeViewer.getSelection(); - if (selection.isEmpty()) - return; - else { - Path newSelected = (Path) selection.getFirstElement(); - if (newSelected.equals(currSelected)) - return; - currSelected = newSelected; - if (Files.isDirectory(currSelected)) - directoryDisplayViewer.setInput(currSelected, "*"); - } - } - } - - private void populateDisplay(final Composite parent) { - parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); - directoryDisplayViewer = new FsTableViewer(parent, SWT.MULTI); - List colDefs = new ArrayList<>(); - colDefs.add(new ColumnDefinition(new FileIconNameLabelProvider(), "Name", 200, 200)); - colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_SIZE), "Size", 100, 100)); - colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_TYPE), "Type", 300, 300)); - colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_LAST_MODIFIED), - "Last modified", 100, 100)); - Table table = directoryDisplayViewer.configureDefaultTable(colDefs); - table.setLayoutData(EclipseUiUtils.fillAll()); - - table.addKeyListener(new KeyListener() { - private static final long serialVersionUID = -8083424284436715709L; - - @Override - public void keyReleased(KeyEvent e) { - } - - @Override - public void keyPressed(KeyEvent e) { - log.debug("Key event received: " + e.keyCode); - IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection(); - Path selected = null; - if (!selection.isEmpty()) - selected = ((Path) selection.getFirstElement()); - if (e.keyCode == SWT.CR) { - if (!Files.isDirectory(selected)) - return; - if (selected != null) { - currSelected = selected; - directoryDisplayViewer.setInput(currSelected, "*"); - } - } else if (e.keyCode == SWT.BS) { - currSelected = currSelected.getParent(); - directoryDisplayViewer.setInput(currSelected, "*"); - directoryDisplayViewer.getTable().setFocus(); - } - } - }); - } -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/file.png b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/file.png deleted file mode 100644 index ce2f2a8f4..000000000 Binary files a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/file.png and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/folder.png b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/folder.png deleted file mode 100644 index c31f37e07..000000000 Binary files a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/folder.png and /dev/null differ diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/package-info.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/package-info.java deleted file mode 100644 index d7f83c3e1..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Generic SWT/JFace file system utilities. */ -package org.argeo.eclipse.ui.fs; \ No newline at end of file diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/package-info.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/package-info.java deleted file mode 100644 index 0d245db07..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Generic SWT/JFace utilities. */ -package org.argeo.eclipse.ui; \ No newline at end of file diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/parts/LdifUsersTable.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/parts/LdifUsersTable.java deleted file mode 100644 index 57139056c..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/parts/LdifUsersTable.java +++ /dev/null @@ -1,402 +0,0 @@ -package org.argeo.eclipse.ui.parts; - -import java.util.ArrayList; -import java.util.List; - -import org.argeo.eclipse.ui.ColumnDefinition; -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.util.ViewerUtils; -import org.eclipse.jface.layout.TableColumnLayout; -import org.eclipse.jface.viewers.CheckboxTableViewer; -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.jface.viewers.ColumnWeightData; -import org.eclipse.jface.viewers.IStructuredContentProvider; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.jface.viewers.TableViewerColumn; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Link; -import org.eclipse.swt.widgets.Table; -import org.eclipse.swt.widgets.TableColumn; -import org.eclipse.swt.widgets.Text; -import org.osgi.service.useradmin.User; - -/** - * Generic composite that display a filter and a table viewer to display users - * (can also be groups) - * - * Warning: this class does not extends TableViewer. Use the - * getTableViewer method to access it. - * - */ -public abstract class LdifUsersTable extends Composite { - private static final long serialVersionUID = -7385959046279360420L; - - // Context - // private UserAdmin userAdmin; - - // Configuration - private List columnDefs = new ArrayList(); - private boolean hasFilter; - private boolean preventTableLayout = false; - private boolean hasSelectionColumn; - private int tableStyle; - - // Local UI Objects - private TableViewer usersViewer; - private Text filterTxt; - - /* EXPOSED METHODS */ - - /** - * @param parent - * @param style - */ - public LdifUsersTable(Composite parent, int style) { - super(parent, SWT.NO_FOCUS); - this.tableStyle = style; - } - - // TODO workaround the bug of the table layout in the Form - public LdifUsersTable(Composite parent, int style, boolean preventTableLayout) { - super(parent, SWT.NO_FOCUS); - this.tableStyle = style; - this.preventTableLayout = preventTableLayout; - } - - /** This must be called before the call to populate method */ - public void setColumnDefinitions(List columnDefinitions) { - this.columnDefs = columnDefinitions; - } - - /** - * - * @param addFilter - * choose to add a field to filter results or not - * @param addSelection - * choose to add a column to select some of the displayed results or - * not - */ - public void populate(boolean addFilter, boolean addSelection) { - // initialization - Composite parent = this; - hasFilter = addFilter; - hasSelectionColumn = addSelection; - - // Main Layout - GridLayout layout = EclipseUiUtils.noSpaceGridLayout(); - layout.verticalSpacing = 5; - this.setLayout(layout); - if (hasFilter) - createFilterPart(parent); - - Composite tableComp = new Composite(parent, SWT.NO_FOCUS); - tableComp.setLayoutData(EclipseUiUtils.fillAll()); - usersViewer = createTableViewer(tableComp); - usersViewer.setContentProvider(new UsersContentProvider()); - } - - /** - * - * @param showMore - * display static filters on creation - * @param addSelection - * choose to add a column to select some of the displayed results or - * not - */ - public void populateWithStaticFilters(boolean showMore, boolean addSelection) { - // initialization - Composite parent = this; - hasFilter = true; - hasSelectionColumn = addSelection; - - // Main Layout - GridLayout layout = EclipseUiUtils.noSpaceGridLayout(); - layout.verticalSpacing = 5; - this.setLayout(layout); - createStaticFilterPart(parent, showMore); - - Composite tableComp = new Composite(parent, SWT.NO_FOCUS); - tableComp.setLayoutData(EclipseUiUtils.fillAll()); - usersViewer = createTableViewer(tableComp); - usersViewer.setContentProvider(new UsersContentProvider()); - } - - /** Enable access to the selected users or groups */ - public List getSelectedUsers() { - if (hasSelectionColumn) { - Object[] elements = ((CheckboxTableViewer) usersViewer).getCheckedElements(); - - List result = new ArrayList(); - for (Object obj : elements) { - result.add((User) obj); - } - return result; - } else - throw new EclipseUiException( - "Unvalid request: no selection column " + "has been created for the current table"); - } - - /** Returns the User table viewer, typically to add doubleclick listener */ - public TableViewer getTableViewer() { - return usersViewer; - } - - /** - * Force the refresh of the underlying table using the current filter string if - * relevant - */ - public void refresh() { - String filter = hasFilter ? filterTxt.getText().trim() : null; - if ("".equals(filter)) - filter = null; - refreshFilteredList(filter); - } - - /** Effective repository request: caller must implement this method */ - abstract protected List listFilteredElements(String filter); - - // protected List listFilteredElements(String filter) { - // List users = new ArrayList(); - // try { - // Role[] roles = userAdmin.getRoles(filter); - // // Display all users and groups - // for (Role role : roles) - // users.add((User) role); - // } catch (InvalidSyntaxException e) { - // throw new EclipseUiException("Unable to get roles with filter: " - // + filter, e); - // } - // return users; - // } - - /* GENERIC COMPOSITE METHODS */ - @Override - public boolean setFocus() { - if (hasFilter) - return filterTxt.setFocus(); - else - return usersViewer.getTable().setFocus(); - } - - @Override - public void dispose() { - super.dispose(); - } - - /* LOCAL CLASSES AND METHODS */ - // Will be usefull to rather use a virtual table viewer - private void refreshFilteredList(String filter) { - List users = listFilteredElements(filter); - usersViewer.setInput(users.toArray()); - } - - private class UsersContentProvider implements IStructuredContentProvider { - private static final long serialVersionUID = 1L; - - public Object[] getElements(Object inputElement) { - return (Object[]) inputElement; - } - - public void dispose() { - } - - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - } - } - - /* MANAGE FILTER */ - private void createFilterPart(Composite parent) { - // Text Area for the filter - filterTxt = new Text(parent, SWT.BORDER | SWT.SEARCH | SWT.ICON_SEARCH | SWT.ICON_CANCEL); - filterTxt.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false)); - filterTxt.addModifyListener(new ModifyListener() { - private static final long serialVersionUID = 1L; - - public void modifyText(ModifyEvent event) { - refreshFilteredList(filterTxt.getText()); - } - }); - } - - private void createStaticFilterPart(Composite parent, boolean showMore) { - Composite filterComp = new Composite(parent, SWT.NO_FOCUS); - filterComp.setLayout(new GridLayout(2, false)); - filterComp.setLayoutData(EclipseUiUtils.fillWidth()); - // generic search - filterTxt = new Text(filterComp, SWT.BORDER | SWT.SEARCH | SWT.ICON_SEARCH | SWT.ICON_CANCEL); - filterTxt.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false)); - // filterTxt.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | - // GridData.HORIZONTAL_ALIGN_FILL)); - filterTxt.addModifyListener(new ModifyListener() { - private static final long serialVersionUID = 1L; - - public void modifyText(ModifyEvent event) { - refreshFilteredList(filterTxt.getText()); - } - }); - - // add static filter abilities - Link moreLk = new Link(filterComp, SWT.NONE); - Composite staticFilterCmp = new Composite(filterComp, SWT.NO_FOCUS); - staticFilterCmp.setLayoutData(EclipseUiUtils.fillWidth(2)); - populateStaticFilters(staticFilterCmp); - - MoreLinkListener listener = new MoreLinkListener(moreLk, staticFilterCmp, showMore); - // initialise the layout - listener.refresh(); - moreLk.addSelectionListener(listener); - } - - /** Overwrite to add static filters */ - protected void populateStaticFilters(Composite staticFilterCmp) { - } - - // private void addMoreSL(final Link more) { - // more.addSelectionListener( } - - private class MoreLinkListener extends SelectionAdapter { - private static final long serialVersionUID = -524987616510893463L; - private boolean isShown; - private final Composite staticFilterCmp; - private final Link moreLk; - - public MoreLinkListener(Link moreLk, Composite staticFilterCmp, boolean isShown) { - this.moreLk = moreLk; - this.staticFilterCmp = staticFilterCmp; - this.isShown = isShown; - } - - @Override - public void widgetSelected(SelectionEvent e) { - isShown = !isShown; - refresh(); - } - - public void refresh() { - GridData gd = (GridData) staticFilterCmp.getLayoutData(); - if (isShown) { - moreLk.setText(" Less... "); - gd.heightHint = SWT.DEFAULT; - } else { - moreLk.setText(" More... "); - gd.heightHint = 0; - } - forceLayout(); - } - } - - private void forceLayout() { - LdifUsersTable.this.getParent().layout(true, true); - } - - private TableViewer createTableViewer(final Composite parent) { - - int style = tableStyle | SWT.H_SCROLL | SWT.V_SCROLL; - if (hasSelectionColumn) - style = style | SWT.CHECK; - Table table = new Table(parent, style); - TableColumnLayout layout = new TableColumnLayout(); - - // TODO the table layout does not works with the scrolled form - - if (preventTableLayout) { - parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); - table.setLayoutData(EclipseUiUtils.fillAll()); - } else - parent.setLayout(layout); - - TableViewer viewer; - if (hasSelectionColumn) - viewer = new CheckboxTableViewer(table); - else - viewer = new TableViewer(table); - table.setLinesVisible(true); - table.setHeaderVisible(true); - - TableViewerColumn column; - // int offset = 0; - if (hasSelectionColumn) { - // offset = 1; - column = ViewerUtils.createTableViewerColumn(viewer, "", SWT.NONE, 25); - column.setLabelProvider(new ColumnLabelProvider() { - private static final long serialVersionUID = 1L; - - @Override - public String getText(Object element) { - return null; - } - }); - layout.setColumnData(column.getColumn(), new ColumnWeightData(25, 25, false)); - - SelectionAdapter selectionAdapter = new SelectionAdapter() { - private static final long serialVersionUID = 1L; - - boolean allSelected = false; - - @Override - public void widgetSelected(SelectionEvent e) { - allSelected = !allSelected; - ((CheckboxTableViewer) usersViewer).setAllChecked(allSelected); - } - }; - column.getColumn().addSelectionListener(selectionAdapter); - } - - // NodeViewerComparator comparator = new NodeViewerComparator(); - // TODO enable the sort by click on the header - // int i = offset; - for (ColumnDefinition colDef : columnDefs) - createTableColumn(viewer, layout, colDef); - - // column = ViewerUtils.createTableViewerColumn(viewer, - // colDef.getHeaderLabel(), SWT.NONE, colDef.getColumnSize()); - // column.setLabelProvider(new CLProvider(colDef.getPropertyName())); - // column.getColumn().addSelectionListener( - // JcrUiUtils.getNodeSelectionAdapter(i, - // colDef.getPropertyType(), colDef.getPropertyName(), - // comparator, viewer)); - // i++; - // } - - // IMPORTANT: initialize comparator before setting it - // JcrColumnDefinition firstCol = colDefs.get(0); - // comparator.setColumn(firstCol.getPropertyType(), - // firstCol.getPropertyName()); - // viewer.setComparator(comparator); - - return viewer; - } - - /** Default creation of a column for a user table */ - private TableViewerColumn createTableColumn(TableViewer tableViewer, TableColumnLayout layout, - ColumnDefinition columnDef) { - - boolean resizable = true; - TableViewerColumn tvc = new TableViewerColumn(tableViewer, SWT.NONE); - TableColumn column = tvc.getColumn(); - - column.setText(columnDef.getLabel()); - column.setWidth(columnDef.getMinWidth()); - column.setResizable(resizable); - - ColumnLabelProvider lp = columnDef.getLabelProvider(); - // add a reference to the display to enable font management - // if (lp instanceof UserAdminAbstractLP) - // ((UserAdminAbstractLP) lp).setDisplay(tableViewer.getTable() - // .getDisplay()); - tvc.setLabelProvider(lp); - - layout.setColumnData(column, new ColumnWeightData(columnDef.getWeight(), columnDef.getMinWidth(), resizable)); - - return tvc; - } -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/parts/package-info.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/parts/package-info.java deleted file mode 100644 index 9e93b1106..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/parts/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Generic SWT/JFace composites. */ -package org.argeo.eclipse.ui.parts; \ No newline at end of file diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/util/ViewerUtils.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/util/ViewerUtils.java deleted file mode 100644 index 8f4df1799..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/util/ViewerUtils.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.argeo.eclipse.ui.util; - -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.jface.viewers.TableViewerColumn; -import org.eclipse.jface.viewers.TreeViewer; -import org.eclipse.jface.viewers.TreeViewerColumn; -import org.eclipse.swt.widgets.Table; -import org.eclipse.swt.widgets.TableColumn; -import org.eclipse.swt.widgets.TreeColumn; - -/** - * Centralise useful methods to manage JFace Table, Tree and TreeColumn viewers. - */ -public class ViewerUtils { - - /** - * Creates a basic column for the given table. For the time being, we do not - * support movable columns. - */ - public static TableColumn createColumn(Table parent, String name, int style, int width) { - TableColumn result = new TableColumn(parent, style); - result.setText(name); - result.setWidth(width); - result.setResizable(true); - return result; - } - - /** - * Creates a TableViewerColumn for the given viewer. For the time being, we do - * not support movable columns. - */ - public static TableViewerColumn createTableViewerColumn(TableViewer parent, String name, int style, int width) { - TableViewerColumn tvc = new TableViewerColumn(parent, style); - TableColumn column = tvc.getColumn(); - column.setText(name); - column.setWidth(width); - column.setResizable(true); - return tvc; - } - - // public static TableViewerColumn createTableViewerColumn(TableViewer parent, - // Localized name, int style, int width) { - // return createTableViewerColumn(parent, name.lead(), style, width); - // } - - /** - * Creates a TreeViewerColumn for the given viewer. For the time being, we do - * not support movable columns. - */ - public static TreeViewerColumn createTreeViewerColumn(TreeViewer parent, String name, int style, int width) { - TreeViewerColumn tvc = new TreeViewerColumn(parent, style); - TreeColumn column = tvc.getColumn(); - column.setText(name); - column.setWidth(width); - column.setResizable(true); - return tvc; - } -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/util/package-info.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/util/package-info.java deleted file mode 100644 index 798d17482..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/util/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Generic SWT/JFace JCR helpers. */ -package org.argeo.eclipse.ui.util; \ No newline at end of file diff --git a/org.argeo.api.cli/.classpath b/org.argeo.api.cli/.classpath new file mode 100644 index 000000000..81fe078c2 --- /dev/null +++ b/org.argeo.api.cli/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/org.argeo.api.cli/.project b/org.argeo.api.cli/.project new file mode 100644 index 000000000..8f5cd419b --- /dev/null +++ b/org.argeo.api.cli/.project @@ -0,0 +1,28 @@ + + + org.argeo.api.cli + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/org.argeo.api.cli/bnd.bnd b/org.argeo.api.cli/bnd.bnd new file mode 100644 index 000000000..e69de29bb diff --git a/org.argeo.api.cli/build.properties b/org.argeo.api.cli/build.properties new file mode 100644 index 000000000..34d2e4d2d --- /dev/null +++ b/org.argeo.api.cli/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/org.argeo.api.cli/src/org/argeo/api/cli/CommandArgsException.java b/org.argeo.api.cli/src/org/argeo/api/cli/CommandArgsException.java new file mode 100644 index 000000000..b2a12a603 --- /dev/null +++ b/org.argeo.api.cli/src/org/argeo/api/cli/CommandArgsException.java @@ -0,0 +1,32 @@ +package org.argeo.api.cli; + +public class CommandArgsException extends IllegalArgumentException { + private static final long serialVersionUID = -7271050747105253935L; + private String commandName; + private volatile CommandsCli commandsCli; + + public CommandArgsException(Exception cause) { + super(cause.getMessage(), cause); + } + + public CommandArgsException(String message) { + super(message); + } + + public String getCommandName() { + return commandName; + } + + public void setCommandName(String commandName) { + this.commandName = commandName; + } + + public CommandsCli getCommandsCli() { + return commandsCli; + } + + public void setCommandsCli(CommandsCli commandsCli) { + this.commandsCli = commandsCli; + } + +} diff --git a/org.argeo.api.cli/src/org/argeo/api/cli/CommandRuntimeException.java b/org.argeo.api.cli/src/org/argeo/api/cli/CommandRuntimeException.java new file mode 100644 index 000000000..52c033433 --- /dev/null +++ b/org.argeo.api.cli/src/org/argeo/api/cli/CommandRuntimeException.java @@ -0,0 +1,35 @@ +package org.argeo.api.cli; + +import java.util.List; + +/** {@link RuntimeException} referring during a command run. */ +public class CommandRuntimeException extends RuntimeException { + private static final long serialVersionUID = 5595999301269377128L; + + private final DescribedCommand command; + private final List arguments; + + public CommandRuntimeException(Throwable e, DescribedCommand command, List arguments) { + this(null, e, command, arguments); + } + + public CommandRuntimeException(String message, DescribedCommand command, List arguments) { + this(message, null, command, arguments); + } + + public CommandRuntimeException(String message, Throwable e, DescribedCommand command, List arguments) { + super(message == null ? "(" + command.getClass().getName() + " " + arguments.toString() + ")" + : message + " (" + command.getClass().getName() + " " + arguments.toString() + ")", e); + this.command = command; + this.arguments = arguments; + } + + public DescribedCommand getCommand() { + return command; + } + + public List getArguments() { + return arguments; + } + +} diff --git a/org.argeo.api.cli/src/org/argeo/api/cli/CommandsCli.java b/org.argeo.api.cli/src/org/argeo/api/cli/CommandsCli.java new file mode 100644 index 000000000..b82308a2b --- /dev/null +++ b/org.argeo.api.cli/src/org/argeo/api/cli/CommandsCli.java @@ -0,0 +1,136 @@ +package org.argeo.api.cli; + +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.function.Function; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.MissingOptionException; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; + +/** Base class for a CLI managing sub commands. */ +public abstract class CommandsCli implements DescribedCommand { + public final static String HELP = "help"; + + private final String commandName; + private Map, ?>> commands = new TreeMap<>(); + + protected final Options options = new Options(); + + public CommandsCli(String commandName) { + this.commandName = commandName; + } + + @Override + public Object apply(List args) { + String cmd = null; + List newArgs = new ArrayList<>(); + try { + CommandLineParser clParser = new DefaultParser(); + CommandLine commonCl = clParser.parse(getOptions(), args.toArray(new String[args.size()]), true); + List leftOvers = commonCl.getArgList(); + for (String arg : leftOvers) { + if (!arg.startsWith("-") && cmd == null) { + cmd = arg; + } else { + newArgs.add(arg); + } + } + } catch (ParseException e) { + CommandArgsException cae = new CommandArgsException(e); + throw cae; + } + + Function, ?> function = cmd != null ? getCommand(cmd) : getDefaultCommand(); + if (function == null) + throw new IllegalArgumentException("Uknown command " + cmd); + try { + Object value = function.apply(newArgs); + return value != null ? value.toString() : null; + } catch (CommandArgsException e) { + if (e.getCommandName() == null) { + e.setCommandName(cmd); + e.setCommandsCli(this); + } + throw e; + } catch (IllegalArgumentException e) { + CommandArgsException cae = new CommandArgsException(e); + cae.setCommandName(cmd); + throw cae; + } + } + + @Override + public Options getOptions() { + return options; + } + + protected void addCommand(String cmd, Function, ?> function) { + commands.put(cmd, function); + + } + + @Override + public String getUsage() { + return "[command]"; + } + + protected void addCommandsCli(CommandsCli commandsCli) { + addCommand(commandsCli.getCommandName(), commandsCli); + commandsCli.addCommand(HELP, new HelpCommand(this, commandsCli)); + } + + public String getCommandName() { + return commandName; + } + + public Set getSubCommands() { + return commands.keySet(); + } + + public Function, ?> getCommand(String command) { + return commands.get(command); + } + + public HelpCommand getHelpCommand() { + return (HelpCommand) getCommand(HELP); + } + + public Function, String> getDefaultCommand() { + return getHelpCommand(); + } + + /** In order to implement quickly a main method. */ + public static void mainImpl(CommandsCli cli, String[] args) { + try { + cli.addCommand(CommandsCli.HELP, new HelpCommand(null, cli)); + Object output = cli.apply(Arrays.asList(args)); + System.out.println(output); + System.exit(0); + } catch (CommandArgsException e) { + System.err.println("Wrong arguments " + Arrays.toString(args) + ": " + e.getMessage()); + Throwable cause = e.getCause(); + if (!(cause instanceof MissingOptionException)) + e.printStackTrace(); + if (e.getCommandName() != null) { + StringWriter out = new StringWriter(); + HelpCommand.printHelp(e.getCommandsCli(), e.getCommandName(), out); + System.err.println(out.toString()); + } else { + e.printStackTrace(); + } + System.exit(1); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } +} diff --git a/org.argeo.api.cli/src/org/argeo/api/cli/DescribedCommand.java b/org.argeo.api.cli/src/org/argeo/api/cli/DescribedCommand.java new file mode 100644 index 000000000..7a9d5d901 --- /dev/null +++ b/org.argeo.api.cli/src/org/argeo/api/cli/DescribedCommand.java @@ -0,0 +1,55 @@ +package org.argeo.api.cli; + +import java.io.StringWriter; +import java.util.Arrays; +import java.util.List; +import java.util.function.Function; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; + +/** A command that can be described. */ +public interface DescribedCommand extends Function, T> { + default Options getOptions() { + return new Options(); + } + + String getDescription(); + + default String getUsage() { + return null; + } + + default String getExamples() { + return null; + } + + default CommandLine toCommandLine(List args) { + try { + DefaultParser parser = new DefaultParser(); + return parser.parse(getOptions(), args.toArray(new String[args.size()])); + } catch (ParseException e) { + throw new CommandArgsException(e); + } + } + + /** In order to implement quickly a main method. */ + public static void mainImpl(DescribedCommand command, String[] args) { + try { + Object output = command.apply(Arrays.asList(args)); + System.out.println(output); + System.exit(0); + } catch (IllegalArgumentException e) { + StringWriter out = new StringWriter(); + HelpCommand.printHelp(command, out); + System.err.println(out.toString()); + System.exit(1); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } + +} diff --git a/org.argeo.api.cli/src/org/argeo/api/cli/HelpCommand.java b/org.argeo.api.cli/src/org/argeo/api/cli/HelpCommand.java new file mode 100644 index 000000000..d5285a60c --- /dev/null +++ b/org.argeo.api.cli/src/org/argeo/api/cli/HelpCommand.java @@ -0,0 +1,143 @@ +package org.argeo.api.cli; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.List; +import java.util.function.Function; + +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Options; + +/** A special command that can describe {@link DescribedCommand}. */ +public class HelpCommand implements DescribedCommand { + private CommandsCli commandsCli; + private CommandsCli parentCommandsCli; + + // Help formatting + private static int helpWidth = 80; + private static int helpLeftPad = 4; + private static int helpDescPad = 20; + + public HelpCommand(CommandsCli parentCommandsCli, CommandsCli commandsCli) { + super(); + this.parentCommandsCli = parentCommandsCli; + this.commandsCli = commandsCli; + } + + @Override + public String apply(List args) { + StringWriter out = new StringWriter(); + + if (args.size() == 0) {// overview + printHelp(commandsCli, out); + } else { + String cmd = args.get(0); + Function, ?> function = commandsCli.getCommand(cmd); + if (function == null) + return "Command " + cmd + " not found."; + Options options; + String examples; + DescribedCommand command = null; + if (function instanceof DescribedCommand) { + command = (DescribedCommand) function; + options = command.getOptions(); + examples = command.getExamples(); + } else { + options = new Options(); + examples = null; + } + String description = getShortDescription(function); + String commandCall = getCommandUsage(cmd, command); + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp(new PrintWriter(out), helpWidth, commandCall, description, options, helpLeftPad, + helpDescPad, examples, false); + } + return out.toString(); + } + + private static String getShortDescription(Function, ?> function) { + if (function instanceof DescribedCommand) { + return ((DescribedCommand) function).getDescription(); + } else { + return function.toString(); + } + } + + public String getCommandUsage(String cmd, DescribedCommand command) { + String commandCall = getCommandCall(commandsCli) + " " + cmd; + assert command != null; + if (command != null && command.getUsage() != null) { + commandCall = commandCall + " " + command.getUsage(); + } + return commandCall; + } + + @Override + public String getDescription() { + return "Shows this help or describes a command"; + } + + @Override + public String getUsage() { + return "[command]"; + } + + public CommandsCli getParentCommandsCli() { + return parentCommandsCli; + } + + protected String getCommandCall(CommandsCli commandsCli) { + HelpCommand hc = commandsCli.getHelpCommand(); + if (hc.getParentCommandsCli() != null) { + return getCommandCall(hc.getParentCommandsCli()) + " " + commandsCli.getCommandName(); + } else { + return commandsCli.getCommandName(); + } + } + + public static void printHelp(DescribedCommand command, StringWriter out) { + String usage = "java " + command.getClass().getName() + + (command.getUsage() != null ? " " + command.getUsage() : ""); + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp(new PrintWriter(out), helpWidth, usage, command.getDescription(), command.getOptions(), + helpLeftPad, helpDescPad, command.getExamples(), false); + + } + + public static void printHelp(CommandsCli commandsCli, String commandName, StringWriter out) { + DescribedCommand command = (DescribedCommand) commandsCli.getCommand(commandName); + String usage = commandsCli.getHelpCommand().getCommandUsage(commandName, command); + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp(new PrintWriter(out), helpWidth, usage, command.getDescription(), command.getOptions(), + helpLeftPad, helpDescPad, command.getExamples(), false); + + } + + public static void printHelp(CommandsCli commandsCli, StringWriter out) { + out.append(commandsCli.getDescription()).append('\n'); + String leftPad = spaces(helpLeftPad); + for (String cmd : commandsCli.getSubCommands()) { + Function, ?> function = commandsCli.getCommand(cmd); + assert function != null; + out.append(leftPad); + out.append(cmd); + // TODO deal with long commands + out.append(spaces(helpDescPad - cmd.length())); + out.append(getShortDescription(function)); + out.append('\n'); + } + } + + private static String spaces(int count) { + // Java 11 + // return " ".repeat(count); + if (count <= 0) + return ""; + else { + StringBuilder sb = new StringBuilder(count); + for (int i = 0; i < count; i++) + sb.append(' '); + return sb.toString(); + } + } +} diff --git a/org.argeo.api.cli/src/org/argeo/api/cli/package-info.java b/org.argeo.api.cli/src/org/argeo/api/cli/package-info.java new file mode 100644 index 000000000..114fd02d4 --- /dev/null +++ b/org.argeo.api.cli/src/org/argeo/api/cli/package-info.java @@ -0,0 +1,2 @@ +/** Command line API. */ +package org.argeo.api.cli; \ No newline at end of file diff --git a/org.argeo.cms.ssh/src/org/argeo/cms/ssh/cli/SshCli.java b/org.argeo.cms.ssh/src/org/argeo/cms/ssh/cli/SshCli.java new file mode 100644 index 000000000..12b4d5e1d --- /dev/null +++ b/org.argeo.cms.ssh/src/org/argeo/cms/ssh/cli/SshCli.java @@ -0,0 +1,17 @@ +package org.argeo.cms.ssh.cli; + +import org.argeo.api.cli.CommandsCli; + +public class SshCli extends CommandsCli { + public SshCli(String commandName) { + super(commandName); + addCommand("shell", new SshShell()); + } + + @Override + public String getDescription() { + return "SSH utilities."; + } + + +} diff --git a/org.argeo.cms.ssh/src/org/argeo/cms/ssh/cli/SshShell.java b/org.argeo.cms.ssh/src/org/argeo/cms/ssh/cli/SshShell.java new file mode 100644 index 000000000..78903a779 --- /dev/null +++ b/org.argeo.cms.ssh/src/org/argeo/cms/ssh/cli/SshShell.java @@ -0,0 +1,115 @@ +package org.argeo.cms.ssh.cli; + +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.net.URI; +import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.util.List; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.apache.sshd.agent.SshAgent; +import org.apache.sshd.agent.SshAgentFactory; +import org.apache.sshd.agent.local.LocalAgentFactory; +import org.apache.sshd.agent.unix.UnixAgentFactory; +import org.apache.sshd.client.config.keys.ClientIdentityLoader; +import org.apache.sshd.common.NamedResource; +import org.apache.sshd.common.config.keys.FilePasswordProvider; +import org.argeo.api.cli.DescribedCommand; +import org.argeo.cms.ssh.AbstractSsh; +import org.argeo.cms.ssh.Ssh; + +public class SshShell implements DescribedCommand { + private Option portOption; + + @Override + public Options getOptions() { + Options options = new Options(); + portOption = Option.builder().option("p").longOpt("port").hasArg().desc("port to connect to").build(); + options.addOption(portOption); + return options; + } + + @Override + public String apply(List args) { + CommandLine cl = toCommandLine(args); + String portStr = cl.getOptionValue(portOption); + if (portStr == null) + portStr = "22"; + + String host = cl.getArgList().get(0); + + String uriStr = "ssh://" + host + ":" + portStr + "/"; + // System.out.println(uriStr); + URI uri = URI.create(uriStr); + + Ssh ssh = null; + try { + ssh = new Ssh(uri); + boolean osAgent; + SshAgent sshAgent; + try { + String sshAuthSockentEnv = System.getenv(SshAgent.SSH_AUTHSOCKET_ENV_NAME); + if (sshAuthSockentEnv != null) { + ssh.getSshClient().getProperties().put(SshAgent.SSH_AUTHSOCKET_ENV_NAME, sshAuthSockentEnv); + SshAgentFactory agentFactory = new UnixAgentFactory(); + ssh.getSshClient().setAgentFactory(agentFactory); + sshAgent = agentFactory.createClient(null, ssh.getSshClient()); + osAgent = true; + } else { + osAgent = false; + } + } catch (Exception e) { + e.printStackTrace(); + osAgent = false; + } + + if (!osAgent) { + SshAgentFactory agentFactory = new LocalAgentFactory(); + ssh.getSshClient().setAgentFactory(agentFactory); + sshAgent = agentFactory.createClient(null, ssh.getSshClient()); + String keyPath = System.getProperty("user.home") + "/.ssh/id_rsa"; + + char[] keyPassword = AbstractSsh.readPassword(); + NamedResource namedResource = new NamedResource() { + + @Override + public String getName() { + return keyPath; + } + }; + KeyPair keyPair = ClientIdentityLoader.DEFAULT + .loadClientIdentities(null, namedResource, FilePasswordProvider.of(new String(keyPassword))) + .iterator().next(); + sshAgent.addIdentity(keyPair, "NO COMMENT"); + } + +// char[] keyPassword = AbstractSsh.readPassword(); +// SshKeyPair keyPair = SshKeyPair.loadDefault(keyPassword); +// Arrays.fill(keyPassword, '*'); +// ssh.setSshKeyPair(keyPair); +// ssh.authenticate(); + ssh.verifyAuth(); + + long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime(); + System.out.println("Ssh available in " + jvmUptime + " ms."); + + AbstractSsh.openShell(ssh); + } catch (IOException | GeneralSecurityException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } finally { + if (ssh != null) + ssh.closeSession(); + } + return null; + } + + @Override + public String getDescription() { + return "Launch a static CMS."; + } + + } \ No newline at end of file diff --git a/org.argeo.cms/src/org/argeo/cms/acr/fs/FileSync.java b/org.argeo.cms/src/org/argeo/cms/acr/fs/FileSync.java index 230a831a9..397caea34 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/fs/FileSync.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/fs/FileSync.java @@ -9,8 +9,8 @@ import java.util.List; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; -import org.argeo.cms.cli.CommandArgsException; -import org.argeo.cms.cli.DescribedCommand; +import org.argeo.api.cli.CommandArgsException; +import org.argeo.api.cli.DescribedCommand; public class FileSync implements DescribedCommand> { final static Option deleteOption = Option.builder().longOpt("delete").desc("delete from target").build(); diff --git a/org.argeo.cms/src/org/argeo/cms/acr/fs/FsCommands.java b/org.argeo.cms/src/org/argeo/cms/acr/fs/FsCommands.java index 2aa240633..088c1c352 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/fs/FsCommands.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/fs/FsCommands.java @@ -1,6 +1,6 @@ package org.argeo.cms.acr.fs; -import org.argeo.cms.cli.CommandsCli; +import org.argeo.api.cli.CommandsCli; /** File utilities. */ public class FsCommands extends CommandsCli { diff --git a/org.argeo.cms/src/org/argeo/cms/auth/AnonymousLoginModule.java b/org.argeo.cms/src/org/argeo/cms/auth/AnonymousLoginModule.java index de3a30270..82873ad01 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/AnonymousLoginModule.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/AnonymousLoginModule.java @@ -9,8 +9,7 @@ import javax.security.auth.login.LoginException; import javax.security.auth.spi.LoginModule; import org.argeo.api.cms.CmsLog; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; +import org.argeo.cms.internal.runtime.CmsContextImpl; import org.osgi.service.useradmin.Authorization; import org.osgi.service.useradmin.UserAdmin; @@ -22,7 +21,7 @@ public class AnonymousLoginModule implements LoginModule { private Map sharedState = null; // private state - private BundleContext bc; +// private BundleContext bc; @SuppressWarnings("unchecked") @Override @@ -30,12 +29,12 @@ public class AnonymousLoginModule implements LoginModule { Map options) { this.subject = subject; this.sharedState = (Map) sharedState; - try { - bc = FrameworkUtil.getBundle(AnonymousLoginModule.class).getBundleContext(); - assert bc != null; - } catch (Exception e) { - throw new IllegalStateException("Cannot initialize login module", e); - } +// try { +// bc = FrameworkUtil.getBundle(AnonymousLoginModule.class).getBundleContext(); +// assert bc != null; +// } catch (Exception e) { +// throw new IllegalStateException("Cannot initialize login module", e); +// } } @Override @@ -45,7 +44,7 @@ public class AnonymousLoginModule implements LoginModule { @Override public boolean commit() throws LoginException { - UserAdmin userAdmin = bc.getService(bc.getServiceReference(UserAdmin.class)); + UserAdmin userAdmin = CmsContextImpl.getCmsContext().getUserAdmin(); Authorization authorization = userAdmin.getAuthorization(null); RemoteAuthRequest request = (RemoteAuthRequest) sharedState.get(CmsAuthUtils.SHARED_STATE_HTTP_REQUEST); Locale locale = Locale.getDefault(); diff --git a/org.argeo.cms/src/org/argeo/cms/cli/CommandArgsException.java b/org.argeo.cms/src/org/argeo/cms/cli/CommandArgsException.java deleted file mode 100644 index 1f6d56bb1..000000000 --- a/org.argeo.cms/src/org/argeo/cms/cli/CommandArgsException.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.argeo.cms.cli; - -public class CommandArgsException extends IllegalArgumentException { - private static final long serialVersionUID = -7271050747105253935L; - private String commandName; - private volatile CommandsCli commandsCli; - - public CommandArgsException(Exception cause) { - super(cause.getMessage(), cause); - } - - public CommandArgsException(String message) { - super(message); - } - - public String getCommandName() { - return commandName; - } - - public void setCommandName(String commandName) { - this.commandName = commandName; - } - - public CommandsCli getCommandsCli() { - return commandsCli; - } - - public void setCommandsCli(CommandsCli commandsCli) { - this.commandsCli = commandsCli; - } - -} diff --git a/org.argeo.cms/src/org/argeo/cms/cli/CommandRuntimeException.java b/org.argeo.cms/src/org/argeo/cms/cli/CommandRuntimeException.java deleted file mode 100644 index ef27c1fc8..000000000 --- a/org.argeo.cms/src/org/argeo/cms/cli/CommandRuntimeException.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.argeo.cms.cli; - -import java.util.List; - -/** {@link RuntimeException} referring during a command run. */ -public class CommandRuntimeException extends RuntimeException { - private static final long serialVersionUID = 5595999301269377128L; - - private final DescribedCommand command; - private final List arguments; - - public CommandRuntimeException(Throwable e, DescribedCommand command, List arguments) { - this(null, e, command, arguments); - } - - public CommandRuntimeException(String message, DescribedCommand command, List arguments) { - this(message, null, command, arguments); - } - - public CommandRuntimeException(String message, Throwable e, DescribedCommand command, List arguments) { - super(message == null ? "(" + command.getClass().getName() + " " + arguments.toString() + ")" - : message + " (" + command.getClass().getName() + " " + arguments.toString() + ")", e); - this.command = command; - this.arguments = arguments; - } - - public DescribedCommand getCommand() { - return command; - } - - public List getArguments() { - return arguments; - } - -} diff --git a/org.argeo.cms/src/org/argeo/cms/cli/CommandsCli.java b/org.argeo.cms/src/org/argeo/cms/cli/CommandsCli.java deleted file mode 100644 index b9d262dee..000000000 --- a/org.argeo.cms/src/org/argeo/cms/cli/CommandsCli.java +++ /dev/null @@ -1,136 +0,0 @@ -package org.argeo.cms.cli; - -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.function.Function; - -import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.CommandLineParser; -import org.apache.commons.cli.DefaultParser; -import org.apache.commons.cli.MissingOptionException; -import org.apache.commons.cli.Options; -import org.apache.commons.cli.ParseException; - -/** Base class for a CLI managing sub commands. */ -public abstract class CommandsCli implements DescribedCommand { - public final static String HELP = "help"; - - private final String commandName; - private Map, ?>> commands = new TreeMap<>(); - - protected final Options options = new Options(); - - public CommandsCli(String commandName) { - this.commandName = commandName; - } - - @Override - public Object apply(List args) { - String cmd = null; - List newArgs = new ArrayList<>(); - try { - CommandLineParser clParser = new DefaultParser(); - CommandLine commonCl = clParser.parse(getOptions(), args.toArray(new String[args.size()]), true); - List leftOvers = commonCl.getArgList(); - for (String arg : leftOvers) { - if (!arg.startsWith("-") && cmd == null) { - cmd = arg; - } else { - newArgs.add(arg); - } - } - } catch (ParseException e) { - CommandArgsException cae = new CommandArgsException(e); - throw cae; - } - - Function, ?> function = cmd != null ? getCommand(cmd) : getDefaultCommand(); - if (function == null) - throw new IllegalArgumentException("Uknown command " + cmd); - try { - Object value = function.apply(newArgs); - return value != null ? value.toString() : null; - } catch (CommandArgsException e) { - if (e.getCommandName() == null) { - e.setCommandName(cmd); - e.setCommandsCli(this); - } - throw e; - } catch (IllegalArgumentException e) { - CommandArgsException cae = new CommandArgsException(e); - cae.setCommandName(cmd); - throw cae; - } - } - - @Override - public Options getOptions() { - return options; - } - - protected void addCommand(String cmd, Function, ?> function) { - commands.put(cmd, function); - - } - - @Override - public String getUsage() { - return "[command]"; - } - - protected void addCommandsCli(CommandsCli commandsCli) { - addCommand(commandsCli.getCommandName(), commandsCli); - commandsCli.addCommand(HELP, new HelpCommand(this, commandsCli)); - } - - public String getCommandName() { - return commandName; - } - - public Set getSubCommands() { - return commands.keySet(); - } - - public Function, ?> getCommand(String command) { - return commands.get(command); - } - - public HelpCommand getHelpCommand() { - return (HelpCommand) getCommand(HELP); - } - - public Function, String> getDefaultCommand() { - return getHelpCommand(); - } - - /** In order to implement quickly a main method. */ - public static void mainImpl(CommandsCli cli, String[] args) { - try { - cli.addCommand(CommandsCli.HELP, new HelpCommand(null, cli)); - Object output = cli.apply(Arrays.asList(args)); - System.out.println(output); - System.exit(0); - } catch (CommandArgsException e) { - System.err.println("Wrong arguments " + Arrays.toString(args) + ": " + e.getMessage()); - Throwable cause = e.getCause(); - if (!(cause instanceof MissingOptionException)) - e.printStackTrace(); - if (e.getCommandName() != null) { - StringWriter out = new StringWriter(); - HelpCommand.printHelp(e.getCommandsCli(), e.getCommandName(), out); - System.err.println(out.toString()); - } else { - e.printStackTrace(); - } - System.exit(1); - } catch (Exception e) { - e.printStackTrace(); - System.exit(1); - } - } -} diff --git a/org.argeo.cms/src/org/argeo/cms/cli/DescribedCommand.java b/org.argeo.cms/src/org/argeo/cms/cli/DescribedCommand.java deleted file mode 100644 index cdfe1300d..000000000 --- a/org.argeo.cms/src/org/argeo/cms/cli/DescribedCommand.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.argeo.cms.cli; - -import java.io.StringWriter; -import java.util.Arrays; -import java.util.List; -import java.util.function.Function; - -import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.DefaultParser; -import org.apache.commons.cli.Options; -import org.apache.commons.cli.ParseException; - -/** A command that can be described. */ -public interface DescribedCommand extends Function, T> { - default Options getOptions() { - return new Options(); - } - - String getDescription(); - - default String getUsage() { - return null; - } - - default String getExamples() { - return null; - } - - default CommandLine toCommandLine(List args) { - try { - DefaultParser parser = new DefaultParser(); - return parser.parse(getOptions(), args.toArray(new String[args.size()])); - } catch (ParseException e) { - throw new CommandArgsException(e); - } - } - - /** In order to implement quickly a main method. */ - public static void mainImpl(DescribedCommand command, String[] args) { - try { - Object output = command.apply(Arrays.asList(args)); - System.out.println(output); - System.exit(0); - } catch (IllegalArgumentException e) { - StringWriter out = new StringWriter(); - HelpCommand.printHelp(command, out); - System.err.println(out.toString()); - System.exit(1); - } catch (Exception e) { - e.printStackTrace(); - System.exit(1); - } - } - -} diff --git a/org.argeo.cms/src/org/argeo/cms/cli/HelpCommand.java b/org.argeo.cms/src/org/argeo/cms/cli/HelpCommand.java deleted file mode 100644 index ab899f437..000000000 --- a/org.argeo.cms/src/org/argeo/cms/cli/HelpCommand.java +++ /dev/null @@ -1,143 +0,0 @@ -package org.argeo.cms.cli; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.List; -import java.util.function.Function; - -import org.apache.commons.cli.HelpFormatter; -import org.apache.commons.cli.Options; - -/** A special command that can describe {@link DescribedCommand}. */ -public class HelpCommand implements DescribedCommand { - private CommandsCli commandsCli; - private CommandsCli parentCommandsCli; - - // Help formatting - private static int helpWidth = 80; - private static int helpLeftPad = 4; - private static int helpDescPad = 20; - - public HelpCommand(CommandsCli parentCommandsCli, CommandsCli commandsCli) { - super(); - this.parentCommandsCli = parentCommandsCli; - this.commandsCli = commandsCli; - } - - @Override - public String apply(List args) { - StringWriter out = new StringWriter(); - - if (args.size() == 0) {// overview - printHelp(commandsCli, out); - } else { - String cmd = args.get(0); - Function, ?> function = commandsCli.getCommand(cmd); - if (function == null) - return "Command " + cmd + " not found."; - Options options; - String examples; - DescribedCommand command = null; - if (function instanceof DescribedCommand) { - command = (DescribedCommand) function; - options = command.getOptions(); - examples = command.getExamples(); - } else { - options = new Options(); - examples = null; - } - String description = getShortDescription(function); - String commandCall = getCommandUsage(cmd, command); - HelpFormatter formatter = new HelpFormatter(); - formatter.printHelp(new PrintWriter(out), helpWidth, commandCall, description, options, helpLeftPad, - helpDescPad, examples, false); - } - return out.toString(); - } - - private static String getShortDescription(Function, ?> function) { - if (function instanceof DescribedCommand) { - return ((DescribedCommand) function).getDescription(); - } else { - return function.toString(); - } - } - - public String getCommandUsage(String cmd, DescribedCommand command) { - String commandCall = getCommandCall(commandsCli) + " " + cmd; - assert command != null; - if (command != null && command.getUsage() != null) { - commandCall = commandCall + " " + command.getUsage(); - } - return commandCall; - } - - @Override - public String getDescription() { - return "Shows this help or describes a command"; - } - - @Override - public String getUsage() { - return "[command]"; - } - - public CommandsCli getParentCommandsCli() { - return parentCommandsCli; - } - - protected String getCommandCall(CommandsCli commandsCli) { - HelpCommand hc = commandsCli.getHelpCommand(); - if (hc.getParentCommandsCli() != null) { - return getCommandCall(hc.getParentCommandsCli()) + " " + commandsCli.getCommandName(); - } else { - return commandsCli.getCommandName(); - } - } - - public static void printHelp(DescribedCommand command, StringWriter out) { - String usage = "java " + command.getClass().getName() - + (command.getUsage() != null ? " " + command.getUsage() : ""); - HelpFormatter formatter = new HelpFormatter(); - formatter.printHelp(new PrintWriter(out), helpWidth, usage, command.getDescription(), command.getOptions(), - helpLeftPad, helpDescPad, command.getExamples(), false); - - } - - public static void printHelp(CommandsCli commandsCli, String commandName, StringWriter out) { - DescribedCommand command = (DescribedCommand) commandsCli.getCommand(commandName); - String usage = commandsCli.getHelpCommand().getCommandUsage(commandName, command); - HelpFormatter formatter = new HelpFormatter(); - formatter.printHelp(new PrintWriter(out), helpWidth, usage, command.getDescription(), command.getOptions(), - helpLeftPad, helpDescPad, command.getExamples(), false); - - } - - public static void printHelp(CommandsCli commandsCli, StringWriter out) { - out.append(commandsCli.getDescription()).append('\n'); - String leftPad = spaces(helpLeftPad); - for (String cmd : commandsCli.getSubCommands()) { - Function, ?> function = commandsCli.getCommand(cmd); - assert function != null; - out.append(leftPad); - out.append(cmd); - // TODO deal with long commands - out.append(spaces(helpDescPad - cmd.length())); - out.append(getShortDescription(function)); - out.append('\n'); - } - } - - private static String spaces(int count) { - // Java 11 - // return " ".repeat(count); - if (count <= 0) - return ""; - else { - StringBuilder sb = new StringBuilder(count); - for (int i = 0; i < count; i++) - sb.append(' '); - return sb.toString(); - } - } -} diff --git a/org.argeo.cms/src/org/argeo/cms/cli/package-info.java b/org.argeo.cms/src/org/argeo/cms/cli/package-info.java deleted file mode 100644 index 59feed1d1..000000000 --- a/org.argeo.cms/src/org/argeo/cms/cli/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Command line API. */ -package org.argeo.cms.cli; \ No newline at end of file diff --git a/org.argeo.cms/src/org/argeo/cms/runtime/StaticCms.java b/org.argeo.cms/src/org/argeo/cms/runtime/StaticCms.java index f133d3049..d1a19bd63 100644 --- a/org.argeo.cms/src/org/argeo/cms/runtime/StaticCms.java +++ b/org.argeo.cms/src/org/argeo/cms/runtime/StaticCms.java @@ -19,6 +19,7 @@ import org.argeo.cms.internal.runtime.CmsStateImpl; import org.argeo.cms.internal.runtime.CmsUserAdmin; import org.argeo.cms.internal.runtime.DeployedContentRepository; import org.argeo.util.register.Component; +import org.argeo.util.register.ComponentRegister; import org.argeo.util.register.SimpleRegister; import org.argeo.util.transaction.SimpleTransactionManager; import org.argeo.util.transaction.WorkControl; @@ -26,7 +27,7 @@ import org.argeo.util.transaction.WorkTransaction; import org.osgi.service.useradmin.UserAdmin; /** - * A CMS assembly which is programatically defined, as an alternative to OSGi + * A CMS assembly which is programmatically defined, as an alternative to OSGi * deployment. Useful for testing or AOT compilation. */ public class StaticCms { @@ -139,14 +140,18 @@ public class StaticCms { postActivation(register); } - protected void addComponents(SimpleRegister register) { + protected void addComponents(ComponentRegister register) { } - protected void postActivation(SimpleRegister register) { + protected void postActivation(ComponentRegister register) { } + public ComponentRegister getComponentRegister() { + return register; + } + public void stop() { if (register.isActive()) { register.deactivate(); diff --git a/org.argeo.util/src/org/argeo/util/register/ComponentRegister.java b/org.argeo.util/src/org/argeo/util/register/ComponentRegister.java index d5f9ea421..d78b6badb 100644 --- a/org.argeo.util/src/org/argeo/util/register/ComponentRegister.java +++ b/org.argeo.util/src/org/argeo/util/register/ComponentRegister.java @@ -10,6 +10,20 @@ public interface ComponentRegister { SortedSet> find(Class clss, Predicate> filter); + default Component.PublishedType getSingleton(Class type) { + SortedSet> found = find(type, null); + if (found.size() == 0) + throw new IllegalStateException("No component found for " + type); + return found.first().getType(type); + } + + default T getObject(Class clss) { + SortedSet> found = find(clss, null); + if (found.size() == 0) + return null; + return found.first().get(); + } + Component get(Object instance); // default PublishedType getType(Class clss) { diff --git a/rap/org.argeo.cms.e4.rap/.classpath b/rap/org.argeo.cms.e4.rap/.classpath deleted file mode 100644 index e801ebfb4..000000000 --- a/rap/org.argeo.cms.e4.rap/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/rap/org.argeo.cms.e4.rap/.project b/rap/org.argeo.cms.e4.rap/.project deleted file mode 100644 index 40c9e013f..000000000 --- a/rap/org.argeo.cms.e4.rap/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.argeo.cms.e4.rap - - - - - - 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/rap/org.argeo.cms.e4.rap/META-INF/.gitignore b/rap/org.argeo.cms.e4.rap/META-INF/.gitignore deleted file mode 100644 index 4854a41b9..000000000 --- a/rap/org.argeo.cms.e4.rap/META-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/MANIFEST.MF diff --git a/rap/org.argeo.cms.e4.rap/OSGI-INF/cms-admin-rap.xml b/rap/org.argeo.cms.e4.rap/OSGI-INF/cms-admin-rap.xml deleted file mode 100644 index 1f688baa6..000000000 --- a/rap/org.argeo.cms.e4.rap/OSGI-INF/cms-admin-rap.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/rap/org.argeo.cms.e4.rap/bnd.bnd b/rap/org.argeo.cms.e4.rap/bnd.bnd deleted file mode 100644 index 5bbe4bc4b..000000000 --- a/rap/org.argeo.cms.e4.rap/bnd.bnd +++ /dev/null @@ -1,9 +0,0 @@ -Bundle-ActivationPolicy: lazy -Service-Component: OSGI-INF/cms-admin-rap.xml - -Import-Package: org.eclipse.swt,\ -org.eclipse.swt.graphics,\ -org.eclipse.e4.ui.workbench,\ -org.eclipse.rap.rwt.client,\ -org.eclipse.nebula.widgets.richtext;resolution:=optional,\ -* diff --git a/rap/org.argeo.cms.e4.rap/build.properties b/rap/org.argeo.cms.e4.rap/build.properties deleted file mode 100644 index c58ea2178..000000000 --- a/rap/org.argeo.cms.e4.rap/build.properties +++ /dev/null @@ -1,5 +0,0 @@ -source.. = src/ -output.. = bin/ -bin.includes = META-INF/,\ - .,\ - OSGI-INF/ diff --git a/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/AbstractRapE4App.java b/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/AbstractRapE4App.java deleted file mode 100644 index 5fe22ae33..000000000 --- a/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/AbstractRapE4App.java +++ /dev/null @@ -1,139 +0,0 @@ -package org.argeo.cms.e4.rap; - -import java.util.HashMap; -import java.util.Map; - -import org.argeo.cms.swt.dialogs.CmsFeedback; -import org.eclipse.rap.e4.E4ApplicationConfig; -import org.eclipse.rap.rwt.application.Application; -import org.eclipse.rap.rwt.application.Application.OperationMode; -import org.eclipse.rap.rwt.application.ApplicationConfiguration; -import org.eclipse.rap.rwt.application.ExceptionHandler; -import org.eclipse.rap.rwt.client.WebClient; -import org.osgi.framework.BundleContext; - -/** Base class for CMS RAP applications. */ -public abstract class AbstractRapE4App implements ApplicationConfiguration { - private String e4Xmi; - private String path; - private String lifeCycleUri = "bundleclass://org.argeo.cms.e4.rap/org.argeo.cms.e4.rap.CmsLoginLifecycle"; - - private Map baseProperties = new HashMap(); - - private BundleContext bundleContext; - public final static String CONTEXT_NAME_PROPERTY = "contextName"; - private String contextName; - - /** - * To be overridden in order to add multiple entry points, directly or using - * {@link #addE4EntryPoint(Application, String, String, Map)}. - */ - protected void addEntryPoints(Application application) { - } - - public void configure(Application application) { - application.setExceptionHandler(new ExceptionHandler() { - - @Override - public void handleException(Throwable throwable) { - CmsFeedback.show("Unexpected RWT exception", throwable); - } - }); - - if (e4Xmi != null) {// backward compatibility - addE4EntryPoint(application, path, e4Xmi, getBaseProperties()); - } else { - addEntryPoints(application); - } - } - - protected Map getBaseProperties() { - return baseProperties; - } - -// protected void addEntryPoint(Application application, E4ApplicationConfig config, Map properties) { -// CmsE4EntryPointFactory entryPointFactory = new CmsE4EntryPointFactory(config); -// application.addEntryPoint(path, entryPointFactory, properties); -// application.setOperationMode(OperationMode.SWT_COMPATIBILITY); -// } - - protected void addE4EntryPoint(Application application, String path, String e4Xmi, Map properties) { - E4ApplicationConfig config = createE4ApplicationConfig(e4Xmi); - CmsE4EntryPointFactory entryPointFactory = new CmsE4EntryPointFactory(config); - application.addEntryPoint(path, entryPointFactory, properties); - application.setOperationMode(OperationMode.SWT_COMPATIBILITY); - } - - /** - * To be overridden for further configuration. - * - * @see E4ApplicationConfig - */ - protected E4ApplicationConfig createE4ApplicationConfig(String e4Xmi) { - return new E4ApplicationConfig(e4Xmi, lifeCycleUri, null, null, false, true, true); - } - - @Deprecated - public void setPageTitle(String pageTitle) { - if (pageTitle != null) - baseProperties.put(WebClient.PAGE_TITLE, pageTitle); - } - - /** Returns a new map used to customise and entry point. */ - public Map customise(String pageTitle) { - Map custom = new HashMap<>(getBaseProperties()); - if (pageTitle != null) - custom.put(WebClient.PAGE_TITLE, pageTitle); - return custom; - } - - @Deprecated - public void setE4Xmi(String e4Xmi) { - this.e4Xmi = e4Xmi; - } - - @Deprecated - public void setPath(String path) { - this.path = path; - } - - public void setLifeCycleUri(String lifeCycleUri) { - this.lifeCycleUri = lifeCycleUri; - } - - protected BundleContext getBundleContext() { - return bundleContext; - } - - protected void setBundleContext(BundleContext bundleContext) { - this.bundleContext = bundleContext; - } - - public String getContextName() { - return contextName; - } - - public void setContextName(String contextName) { - this.contextName = contextName; - } - - public void init(BundleContext bundleContext, Map properties) { - this.bundleContext = bundleContext; - for (String key : properties.keySet()) { - Object value = properties.get(key); - if (value != null) - baseProperties.put(key, value.toString()); - } - - if (properties.containsKey(CONTEXT_NAME_PROPERTY)) { - assert properties.get(CONTEXT_NAME_PROPERTY) != null; - contextName = properties.get(CONTEXT_NAME_PROPERTY).toString(); - } else { - contextName = ""; - } - } - - public void destroy(Map properties) { - - } -} diff --git a/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4AdminApp.java b/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4AdminApp.java deleted file mode 100644 index 66be1e8e9..000000000 --- a/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4AdminApp.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.argeo.cms.e4.rap; - -import org.eclipse.rap.rwt.application.Application; - -/** - * Access to canonical views of the core CMS concepts, useful for devleopers and - * operators. - */ -public class CmsE4AdminApp extends AbstractRapE4App { - @Override - protected void addEntryPoints(Application application) { - addE4EntryPoint(application, "/devops", "org.argeo.cms.e4/e4xmi/cms-devops.e4xmi", - customise("Argeo CMS DevOps")); - addE4EntryPoint(application, "/ego", "org.argeo.cms.e4/e4xmi/cms-ego.e4xmi", customise("Argeo CMS Ego")); - } - -} diff --git a/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4EntryPointFactory.java b/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4EntryPointFactory.java deleted file mode 100644 index a5a32348e..000000000 --- a/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4EntryPointFactory.java +++ /dev/null @@ -1,76 +0,0 @@ -package org.argeo.cms.e4.rap; - -import java.security.PrivilegedAction; - -import javax.security.auth.Subject; - -import org.eclipse.rap.e4.E4ApplicationConfig; -import org.eclipse.rap.e4.E4EntryPointFactory; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.application.EntryPoint; -import org.eclipse.rap.rwt.client.service.JavaScriptExecutor; - -public class CmsE4EntryPointFactory extends E4EntryPointFactory { - public final static String DEFAULT_LIFECYCLE_URI = "bundleclass://org.argeo.cms.e4.rap/org.argeo.cms.e4.rap.CmsLoginLifecycle"; - - public CmsE4EntryPointFactory(E4ApplicationConfig config) { - super(config); - } - - public CmsE4EntryPointFactory(String e4Xmi, String lifeCycleUri) { - super(defaultConfig(e4Xmi, lifeCycleUri)); - } - - public CmsE4EntryPointFactory(String e4Xmi) { - this(e4Xmi, DEFAULT_LIFECYCLE_URI); - } - - public static E4ApplicationConfig defaultConfig(String e4Xmi, String lifeCycleUri) { - E4ApplicationConfig config = new E4ApplicationConfig(e4Xmi, lifeCycleUri, null, null, false, true, true); - return config; - } - - @Override - public EntryPoint create() { - EntryPoint ep = createEntryPoint(); - EntryPoint authEp = new EntryPoint() { - - @Override - public int createUI() { - Subject subject = new Subject(); - return Subject.doAs(subject, new PrivilegedAction() { - - @Override - public Integer run() { - // SPNEGO - // HttpServletRequest request = RWT.getRequest(); - // String authorization = request.getHeader(HEADER_AUTHORIZATION); - // if (authorization == null || !authorization.startsWith("Negotiate")) { - // HttpServletResponse response = RWT.getResponse(); - // response.setStatus(401); - // response.setHeader(HEADER_WWW_AUTHENTICATE, "Negotiate"); - // response.setDateHeader("Date", System.currentTimeMillis()); - // response.setDateHeader("Expires", System.currentTimeMillis() + (24 * 60 * 60 - // * 1000)); - // response.setHeader("Accept-Ranges", "bytes"); - // response.setHeader("Connection", "Keep-Alive"); - // response.setHeader("Keep-Alive", "timeout=5, max=97"); - // // response.setContentType("text/html; charset=UTF-8"); - // } - - JavaScriptExecutor jsExecutor = RWT.getClient().getService(JavaScriptExecutor.class); - Integer exitCode = ep.createUI(); - jsExecutor.execute("location.reload()"); - return exitCode; - } - - }); - } - }; - return authEp; - } - - protected EntryPoint createEntryPoint() { - return super.create(); - } -} diff --git a/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsLoginLifecycle.java b/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsLoginLifecycle.java deleted file mode 100644 index 471cdeca5..000000000 --- a/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsLoginLifecycle.java +++ /dev/null @@ -1,183 +0,0 @@ -package org.argeo.cms.e4.rap; - -import java.security.AccessController; -import java.util.UUID; - -import javax.security.auth.Subject; -import javax.security.auth.login.LoginContext; -import javax.security.auth.login.LoginException; - -import org.argeo.api.cms.CmsAuth; -import org.argeo.api.cms.CmsLog; -import org.argeo.api.cms.ux.CmsImageManager; -import org.argeo.api.cms.ux.CmsView; -import org.argeo.api.cms.ux.UxContext; -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.swt.SimpleSwtUxContext; -import org.argeo.cms.swt.auth.CmsLoginShell; -import org.argeo.cms.swt.dialogs.CmsFeedback; -import org.argeo.cms.ui.util.SimpleImageManager; -import org.eclipse.e4.core.services.events.IEventBroker; -import org.eclipse.e4.ui.workbench.UIEvents; -import org.eclipse.e4.ui.workbench.lifecycle.PostContextCreate; -import org.eclipse.e4.ui.workbench.lifecycle.PreSave; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.client.service.BrowserNavigation; -import org.eclipse.rap.rwt.client.service.BrowserNavigationEvent; -import org.eclipse.rap.rwt.client.service.BrowserNavigationListener; -import org.eclipse.swt.widgets.Display; -import org.osgi.service.event.Event; -import org.osgi.service.event.EventHandler; - -@SuppressWarnings("restriction") -public class CmsLoginLifecycle implements CmsView { - private final static CmsLog log = CmsLog.getLog(CmsLoginLifecycle.class); - - private UxContext uxContext; - private CmsImageManager imageManager; - - private LoginContext loginContext; - private BrowserNavigation browserNavigation; - - private String state = null; - private String uid; - - @PostContextCreate - boolean login(final IEventBroker eventBroker) { - uid = UUID.randomUUID().toString(); - browserNavigation = RWT.getClient().getService(BrowserNavigation.class); - if (browserNavigation != null) - browserNavigation.addBrowserNavigationListener(new BrowserNavigationListener() { - private static final long serialVersionUID = -3668136623771902865L; - - @Override - public void navigated(BrowserNavigationEvent event) { - state = event.getState(); - if (uxContext != null)// is logged in - stateChanged(); - } - }); - - Subject subject = Subject.getSubject(AccessController.getContext()); - Display display = Display.getCurrent(); -// UiContext.setData(CmsView.KEY, this); - // FIXME get CMS context - CmsLoginShell loginShell = new CmsLoginShell(this, null); - CmsSwtUtils.registerCmsView(loginShell.getShell(), this); - loginShell.setSubject(subject); - try { - // try pre-auth - loginContext = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER, subject, loginShell); - loginContext.login(); - } catch (LoginException e) { - loginShell.createUi(); - loginShell.open(); - - while (!loginShell.getShell().isDisposed()) { - if (!display.readAndDispatch()) - display.sleep(); - } - } - if (CurrentUser.getUsername(getSubject()) == null) - return false; - uxContext = new SimpleSwtUxContext(); - imageManager = new SimpleImageManager(); - - eventBroker.subscribe(UIEvents.UILifeCycle.APP_STARTUP_COMPLETE, new EventHandler() { - @Override - public void handleEvent(Event event) { - startupComplete(); - eventBroker.unsubscribe(this); - } - }); - - // lcs.changeApplicationLocale(Locale.FRENCH); - return true; - } - - @PreSave - void destroy() { - // logout(); - } - - @Override - public UxContext getUxContext() { - return uxContext; - } - - @Override - public void navigateTo(String state) { - browserNavigation.pushState(state, state); - } - - @Override - public void authChange(LoginContext loginContext) { - if (loginContext == null) - throw new IllegalArgumentException("Login context cannot be null"); - // logout previous login context - // if (this.loginContext != null) - // try { - // this.loginContext.logout(); - // } catch (LoginException e1) { - // System.err.println("Could not log out: " + e1); - // } - this.loginContext = loginContext; - } - - @Override - public void logout() { - if (loginContext == null) - throw new IllegalStateException("Login context should not be null"); - try { - CurrentUser.logoutCmsSession(loginContext.getSubject()); - loginContext.logout(); - } catch (LoginException e) { - throw new IllegalStateException("Cannot log out", e); - } - } - - @Override - public void exception(Throwable e) { - String msg = "Unexpected exception in Eclipse 4 RAP"; - log.error(msg, e); - CmsFeedback.show(msg, e); - } - - @Override - public CmsImageManager getImageManager() { - return imageManager; - } - - protected Subject getSubject() { - return loginContext.getSubject(); - } - - @Override - public boolean isAnonymous() { - return CurrentUser.isAnonymous(getSubject()); - } - - @Override - public String getUid() { - return uid; - } - - // CALLBACKS - protected void startupComplete() { - } - - protected void stateChanged() { - - } - - // GETTERS - protected BrowserNavigation getBrowserNavigation() { - return browserNavigation; - } - - protected String getState() { - return state; - } - -} diff --git a/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/SimpleRapE4App.java b/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/SimpleRapE4App.java deleted file mode 100644 index 1bca333c9..000000000 --- a/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/SimpleRapE4App.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.argeo.cms.e4.rap; - -import java.util.Enumeration; - -import org.apache.commons.io.FilenameUtils; -import org.argeo.api.cms.CmsLog; -import org.eclipse.rap.rwt.application.Application; -import org.osgi.framework.Bundle; - -/** Simple RAP app which loads all e4xmi files. */ -public class SimpleRapE4App extends AbstractRapE4App { - private final static CmsLog log = CmsLog.getLog(SimpleRapE4App.class); - - private String baseE4xmi = "/e4xmi"; - - @Override - protected void addEntryPoints(Application application) { - Bundle bundle = getBundleContext().getBundle(); - Enumeration paths = bundle.getEntryPaths(baseE4xmi); - while (paths.hasMoreElements()) { - String p = paths.nextElement(); - if (p.endsWith(".e4xmi")) { - String e4xmiPath = bundle.getSymbolicName() + '/' + p; - String name = '/' + FilenameUtils.removeExtension(FilenameUtils.getName(p)); - addE4EntryPoint(application, name, e4xmiPath, getBaseProperties()); - if (log.isDebugEnabled()) - log.debug("Registered " + e4xmiPath + " as " + getContextName() + name); - } - } - } - -} diff --git a/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/package-info.java b/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/package-info.java deleted file mode 100644 index 1122f1922..000000000 --- a/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Eclipse 4 RAP specific extensions. */ -package org.argeo.cms.e4.rap; \ No newline at end of file diff --git a/rap/org.argeo.cms.ui.rap/.classpath b/rap/org.argeo.cms.ui.rap/.classpath deleted file mode 100644 index e801ebfb4..000000000 --- a/rap/org.argeo.cms.ui.rap/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/rap/org.argeo.cms.ui.rap/.project b/rap/org.argeo.cms.ui.rap/.project deleted file mode 100644 index 630160402..000000000 --- a/rap/org.argeo.cms.ui.rap/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.argeo.cms.ui.rap - - - - - - 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/rap/org.argeo.cms.ui.rap/META-INF/.gitignore b/rap/org.argeo.cms.ui.rap/META-INF/.gitignore deleted file mode 100644 index 4854a41b9..000000000 --- a/rap/org.argeo.cms.ui.rap/META-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/MANIFEST.MF diff --git a/rap/org.argeo.cms.ui.rap/OSGI-INF/cmsWebAppFactory.xml b/rap/org.argeo.cms.ui.rap/OSGI-INF/cmsWebAppFactory.xml deleted file mode 100644 index aa7e0adca..000000000 --- a/rap/org.argeo.cms.ui.rap/OSGI-INF/cmsWebAppFactory.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/rap/org.argeo.cms.ui.rap/bnd.bnd b/rap/org.argeo.cms.ui.rap/bnd.bnd deleted file mode 100644 index 1d7c4b08b..000000000 --- a/rap/org.argeo.cms.ui.rap/bnd.bnd +++ /dev/null @@ -1,18 +0,0 @@ -Import-Package:\ -org.argeo.api.acr,\ -org.eclipse.swt,\ -org.argeo.eclipse.ui,\ -javax.jcr.nodetype,\ -javax.jcr.security,\ -org.eclipse.swt.graphics,\ -org.eclipse.jetty.util.component;version="[9.4,12)";resolution:=optional,\ -org.eclipse.jetty.http;version="[9.4,12)";resolution:=optional,\ -org.eclipse.jetty.io;version="[9.4,12)";resolution:=optional,\ -org.eclipse.jetty.security;version="[9.4,12)";resolution:=optional,\ -org.eclipse.jetty.server.handler;version="[9.4,12)";resolution:=optional,\ -org.eclipse.jetty.*;version="[9.4,12)";resolution:=optional,\ -javax.servlet.*;version="[3,5)",\ -* - -Service-Component: OSGI-INF/cmsWebAppFactory.xml - diff --git a/rap/org.argeo.cms.ui.rap/build.properties b/rap/org.argeo.cms.ui.rap/build.properties deleted file mode 100644 index 2416e522a..000000000 --- a/rap/org.argeo.cms.ui.rap/build.properties +++ /dev/null @@ -1,7 +0,0 @@ -output.. = bin/ -bin.includes = META-INF/,\ - .,\ - OSGI-INF/cmsWebAppFactory.xml -source.. = src/ -additional.bundles = org.argeo.ext.slf4j,\ - org.slf4j.api diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/AppUi.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/AppUi.java deleted file mode 100644 index 01ebb237e..000000000 --- a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/AppUi.java +++ /dev/null @@ -1,268 +0,0 @@ -package org.argeo.cms.ui.script; - -import java.util.HashMap; -import java.util.Map; - -import javax.jcr.Node; -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.script.Invocable; -import javax.script.ScriptException; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.swt.Selected; -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.cms.ui.util.CmsPane; -import org.argeo.cms.web.SimpleErgonomics; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.application.Application; -import org.eclipse.rap.rwt.application.EntryPoint; -import org.eclipse.rap.rwt.application.EntryPointFactory; -import org.eclipse.rap.rwt.client.WebClient; -import org.eclipse.rap.rwt.client.service.JavaScriptExecutor; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionEvent; -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.osgi.framework.BundleContext; - -public class AppUi implements CmsUiProvider, Branding { - private final CmsScriptApp app; - - private CmsUiProvider ui; - private String createUi; - private Object impl; - private String script; - // private Branding branding = new Branding(); - - private EntryPointFactory factory; - - // Branding - private String themeId; - private String additionalHeaders; - private String bodyHtml; - private String pageTitle; - private String pageOverflow; - private String favicon; - - public AppUi(CmsScriptApp app) { - this.app = app; - } - - public AppUi(CmsScriptApp app, String scriptPath) { - this.app = app; - this.ui = new ScriptUi((BundleContext) app.getScriptEngine().get(CmsScriptRwtApplication.BC), - app.getScriptEngine(), scriptPath); - } - - public AppUi(CmsScriptApp app, CmsUiProvider uiProvider) { - this.app = app; - this.ui = uiProvider; - } - - public AppUi(CmsScriptApp app, EntryPointFactory factory) { - this.app = app; - this.factory = factory; - } - - public void apply(Repository repository, Application application, Branding appBranding, String path) { - Map factoryProperties = new HashMap<>(); - if (appBranding != null) - appBranding.applyBranding(factoryProperties); - applyBranding(factoryProperties); - if (factory != null) { - application.addEntryPoint("/" + path, factory, factoryProperties); - } else { - EntryPointFactory entryPointFactory = new EntryPointFactory() { - @Override - public EntryPoint create() { - SimpleErgonomics ergonomics = new SimpleErgonomics(repository, CmsConstants.SYS_WORKSPACE, - "/home/root/argeo:keyring", AppUi.this, factoryProperties); -// CmsUiProvider header = app.getHeader(); -// if (header != null) -// ergonomics.setHeader(header); - app.applySides(ergonomics); - Integer headerHeight = app.getHeaderHeight(); - if (headerHeight != null) - ergonomics.setHeaderHeight(headerHeight); - return ergonomics; - } - }; - application.addEntryPoint("/" + path, entryPointFactory, factoryProperties); - } - } - - public void setUi(CmsUiProvider uiProvider) { - this.ui = uiProvider; - } - - public void applyBranding(Map properties) { - if (themeId != null) - properties.put(WebClient.THEME_ID, themeId); - if (additionalHeaders != null) - properties.put(WebClient.HEAD_HTML, additionalHeaders); - if (bodyHtml != null) - properties.put(WebClient.BODY_HTML, bodyHtml); - if (pageTitle != null) - properties.put(WebClient.PAGE_TITLE, pageTitle); - if (pageOverflow != null) - properties.put(WebClient.PAGE_OVERFLOW, pageOverflow); - if (favicon != null) - properties.put(WebClient.FAVICON, favicon); - } - - // public Branding getBranding() { - // return branding; - // } - - @Override - public Control createUi(Composite parent, Node context) throws RepositoryException { - CmsPane cmsPane = new CmsPane(parent, SWT.NONE); - - if (false) { - // QA - CmsSwtUtils.style(cmsPane.getQaArea(), "qa"); - Button reload = new Button(cmsPane.getQaArea(), SWT.FLAT); - CmsSwtUtils.style(reload, "qa"); - reload.setText("Reload"); - reload.addSelectionListener(new Selected() { - private static final long serialVersionUID = 1L; - - @Override - public void widgetSelected(SelectionEvent e) { - new Thread() { - @Override - public void run() { - app.reload(); - } - }.start(); - RWT.getClient().getService(JavaScriptExecutor.class) - .execute("setTimeout('location.reload()',1000)"); - } - }); - - // Support - CmsSwtUtils.style(cmsPane.getSupportArea(), "support"); - Label msg = new Label(cmsPane.getSupportArea(), SWT.NONE); - CmsSwtUtils.style(msg, "support"); - msg.setText("UNSUPPORTED DEVELOPMENT VERSION"); - } - - if (ui != null) { - ui.createUi(cmsPane.getMainArea(), context); - } - if (createUi != null) { - Invocable invocable = (Invocable) app.getScriptEngine(); - try { - invocable.invokeFunction(createUi, cmsPane.getMainArea(), context); - - } catch (NoSuchMethodException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (ScriptException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - if (impl != null) { - Invocable invocable = (Invocable) app.getScriptEngine(); - try { - invocable.invokeMethod(impl, "createUi", cmsPane.getMainArea(), context); - - } catch (NoSuchMethodException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (ScriptException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - // Invocable invocable = (Invocable) app.getScriptEngine(); - // try { - // invocable.invokeMethod(AppUi.this, "initUi", parent, context); - // - // } catch (NoSuchMethodException e) { - // // TODO Auto-generated catch block - // e.printStackTrace(); - // } catch (ScriptException e) { - // // TODO Auto-generated catch block - // e.printStackTrace(); - // } - - return null; - } - - public void setCreateUi(String createUi) { - this.createUi = createUi; - } - - public void setImpl(Object impl) { - this.impl = impl; - } - - public Object getImpl() { - return impl; - } - - public String getScript() { - return script; - } - - public void setScript(String script) { - this.script = script; - } - - // Branding - public String getThemeId() { - return themeId; - } - - public void setThemeId(String themeId) { - this.themeId = themeId; - } - - public String getAdditionalHeaders() { - return additionalHeaders; - } - - public void setAdditionalHeaders(String additionalHeaders) { - this.additionalHeaders = additionalHeaders; - } - - public String getBodyHtml() { - return bodyHtml; - } - - public void setBodyHtml(String bodyHtml) { - this.bodyHtml = bodyHtml; - } - - public String getPageTitle() { - return pageTitle; - } - - public void setPageTitle(String pageTitle) { - this.pageTitle = pageTitle; - } - - public String getPageOverflow() { - return pageOverflow; - } - - public void setPageOverflow(String pageOverflow) { - this.pageOverflow = pageOverflow; - } - - public String getFavicon() { - return favicon; - } - - public void setFavicon(String favicon) { - this.favicon = favicon; - } - -} diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/Branding.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/Branding.java deleted file mode 100644 index f72338ef7..000000000 --- a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/Branding.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.argeo.cms.ui.script; - -import java.util.Map; - -public interface Branding { - public void applyBranding(Map properties); - - public String getThemeId(); - - public String getAdditionalHeaders(); - - public String getBodyHtml(); - - public String getPageTitle(); - - public String getPageOverflow(); - - public String getFavicon(); - -} diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/CmsScriptApp.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/CmsScriptApp.java deleted file mode 100644 index 6b3a670d7..000000000 --- a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/CmsScriptApp.java +++ /dev/null @@ -1,421 +0,0 @@ -package org.argeo.cms.ui.script; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.List; -import java.util.Map; - -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.PropertyIterator; -import javax.jcr.PropertyType; -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.script.ScriptEngine; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.argeo.api.cms.CmsLog; -import org.argeo.api.cms.ux.CmsTheme; -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.ui.CmsUiConstants; -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.cms.ui.util.CmsUiUtils; -import org.argeo.cms.web.BundleResourceLoader; -import org.argeo.cms.web.SimpleErgonomics; -import org.argeo.cms.web.WebThemeUtils; -import org.eclipse.rap.rwt.application.Application; -import org.eclipse.rap.rwt.application.Application.OperationMode; -import org.eclipse.rap.rwt.application.ApplicationConfiguration; -import org.eclipse.rap.rwt.application.ExceptionHandler; -import org.eclipse.rap.rwt.client.WebClient; -import org.eclipse.rap.rwt.service.ResourceLoader; -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceRegistration; -import org.osgi.service.http.HttpContext; -import org.osgi.service.http.HttpService; -import org.osgi.service.http.NamespaceException; - -public class CmsScriptApp implements Branding { - public final static String CONTEXT_NAME = "contextName"; - - ServiceRegistration appConfigReg; - - private ScriptEngine scriptEngine; - - private final static CmsLog log = CmsLog.getLog(CmsScriptApp.class); - - private String webPath; - private String repo = "(cn=node)"; - - // private Branding branding = new Branding(); - private CmsTheme theme; - - private List resources = new ArrayList<>(); - - private Map ui = new HashMap<>(); - - private CmsUiProvider header; - private Integer headerHeight = null; - private CmsUiProvider lead; - private CmsUiProvider end; - private CmsUiProvider footer; - - // Branding - private String themeId; - private String additionalHeaders; - private String bodyHtml; - private String pageTitle; - private String pageOverflow; - private String favicon; - - public CmsScriptApp(ScriptEngine scriptEngine) { - super(); - this.scriptEngine = scriptEngine; - } - - public void apply(BundleContext bundleContext, Repository repository, Application application) { - BundleResourceLoader bundleRL = new BundleResourceLoader(bundleContext.getBundle()); - - application.setOperationMode(OperationMode.SWT_COMPATIBILITY); - // application.setOperationMode(OperationMode.JEE_COMPATIBILITY); - - application.setExceptionHandler(new CmsExceptionHandler()); - - // loading animated gif - application.addResource(CmsUiConstants.LOADING_IMAGE, createResourceLoader(CmsUiConstants.LOADING_IMAGE)); - // empty image - application.addResource(CmsUiConstants.NO_IMAGE, createResourceLoader(CmsUiConstants.NO_IMAGE)); - - for (String resource : resources) { - application.addResource(resource, bundleRL); - if (log.isTraceEnabled()) - log.trace("Resource " + resource); - } - - if (theme != null) { - WebThemeUtils.apply(application, theme); - String themeHeaders = theme.getHtmlHeaders(); - if (themeHeaders != null) { - if (additionalHeaders == null) - additionalHeaders = themeHeaders; - else - additionalHeaders = themeHeaders + "\n" + additionalHeaders; - } - themeId = theme.getThemeId(); - } - - // client JavaScript - Bundle appBundle = bundleRL.getBundle(); - BundleContext bc = appBundle.getBundleContext(); - HttpService httpService = bc.getService(bc.getServiceReference(HttpService.class)); - HttpContext httpContext = new BundleHttpContext(bc); - Enumeration themeResources = appBundle.findEntries("/js/", "*", true); - if (themeResources != null) - bundleResources: while (themeResources.hasMoreElements()) { - try { - String name = themeResources.nextElement().getPath(); - if (name.endsWith("/")) - continue bundleResources; - String alias = "/" + getWebPath() + name; - - httpService.registerResources(alias, name, httpContext); - if (log.isDebugEnabled()) - log.debug("Mapped " + name + " to alias " + alias); - - } catch (NamespaceException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - // App UIs - for (String appUiName : ui.keySet()) { - AppUi appUi = ui.get(appUiName); - appUi.apply(repository, application, this, appUiName); - - } - - } - - public void applySides(SimpleErgonomics simpleErgonomics) { - simpleErgonomics.setHeader(header); - simpleErgonomics.setLead(lead); - simpleErgonomics.setEnd(end); - simpleErgonomics.setFooter(footer); - } - - public void register(BundleContext bundleContext, ApplicationConfiguration appConfig) { - Hashtable props = new Hashtable<>(); - props.put(CONTEXT_NAME, webPath); - appConfigReg = bundleContext.registerService(ApplicationConfiguration.class, appConfig, props); - } - - public void reload() { - BundleContext bundleContext = appConfigReg.getReference().getBundle().getBundleContext(); - ApplicationConfiguration appConfig = bundleContext.getService(appConfigReg.getReference()); - appConfigReg.unregister(); - register(bundleContext, appConfig); - - // BundleContext bundleContext = (BundleContext) - // getScriptEngine().get("bundleContext"); - // try { - // Bundle bundle = bundleContext.getBundle(); - // bundle.stop(); - // bundle.start(); - // } catch (BundleException e) { - // // TODO Auto-generated catch block - // e.printStackTrace(); - // } - } - - private static ResourceLoader createResourceLoader(final String resourceName) { - return new ResourceLoader() { - public InputStream getResourceAsStream(String resourceName) throws IOException { - return getClass().getClassLoader().getResourceAsStream(resourceName); - } - }; - } - - public List getResources() { - return resources; - } - - public AppUi newUi(String name) { - if (ui.containsKey(name)) - throw new IllegalArgumentException("There is already an UI named " + name); - AppUi appUi = new AppUi(this); - // appUi.setApp(this); - ui.put(name, appUi); - return appUi; - } - - public void addUi(String name, AppUi appUi) { - if (ui.containsKey(name)) - throw new IllegalArgumentException("There is already an UI named " + name); - // appUi.setApp(this); - ui.put(name, appUi); - } - - public void applyBranding(Map properties) { - if (themeId != null) - properties.put(WebClient.THEME_ID, themeId); - if (additionalHeaders != null) - properties.put(WebClient.HEAD_HTML, additionalHeaders); - if (bodyHtml != null) - properties.put(WebClient.BODY_HTML, bodyHtml); - if (pageTitle != null) - properties.put(WebClient.PAGE_TITLE, pageTitle); - if (pageOverflow != null) - properties.put(WebClient.PAGE_OVERFLOW, pageOverflow); - if (favicon != null) - properties.put(WebClient.FAVICON, favicon); - } - - class CmsExceptionHandler implements ExceptionHandler { - - @Override - public void handleException(Throwable throwable) { - // TODO be smarter - CmsUiUtils.getCmsView().exception(throwable); - } - - } - - // public Branding getBranding() { - // return branding; - // } - - ScriptEngine getScriptEngine() { - return scriptEngine; - } - - public static String toJson(Node node) { - try { - StringBuilder sb = new StringBuilder(); - sb.append('{'); - PropertyIterator pit = node.getProperties(); - int count = 0; - while (pit.hasNext()) { - Property p = pit.nextProperty(); - int type = p.getType(); - if (type == PropertyType.REFERENCE || type == PropertyType.WEAKREFERENCE || type == PropertyType.PATH) { - Node ref = p.getNode(); - if (count != 0) - sb.append(','); - // TODO limit depth? - sb.append(toJson(ref)); - count++; - } else if (!p.isMultiple()) { - if (count != 0) - sb.append(','); - sb.append('\"').append(p.getName()).append("\":\"").append(p.getString()).append('\"'); - count++; - } - } - sb.append('}'); - return sb.toString(); - } catch (RepositoryException e) { - throw new CmsException("Cannot convert " + node + " to JSON", e); - } - } - - public void fromJson(Node node, String json) { - // TODO - } - - public CmsTheme getTheme() { - return theme; - } - - public void setTheme(CmsTheme theme) { - this.theme = theme; - } - - public String getWebPath() { - return webPath; - } - - public void setWebPath(String context) { - this.webPath = context; - } - - public String getRepo() { - return repo; - } - - public void setRepo(String repo) { - this.repo = repo; - } - - public Map getUi() { - return ui; - } - - public void setUi(Map ui) { - this.ui = ui; - } - - // Branding - public String getThemeId() { - return themeId; - } - - public void setThemeId(String themeId) { - this.themeId = themeId; - } - - public String getAdditionalHeaders() { - return additionalHeaders; - } - - public void setAdditionalHeaders(String additionalHeaders) { - this.additionalHeaders = additionalHeaders; - } - - public String getBodyHtml() { - return bodyHtml; - } - - public void setBodyHtml(String bodyHtml) { - this.bodyHtml = bodyHtml; - } - - public String getPageTitle() { - return pageTitle; - } - - public void setPageTitle(String pageTitle) { - this.pageTitle = pageTitle; - } - - public String getPageOverflow() { - return pageOverflow; - } - - public void setPageOverflow(String pageOverflow) { - this.pageOverflow = pageOverflow; - } - - public String getFavicon() { - return favicon; - } - - public void setFavicon(String favicon) { - this.favicon = favicon; - } - - public CmsUiProvider getHeader() { - return header; - } - - public void setHeader(CmsUiProvider header) { - this.header = header; - } - - public Integer getHeaderHeight() { - return headerHeight; - } - - public void setHeaderHeight(Integer headerHeight) { - this.headerHeight = headerHeight; - } - - public CmsUiProvider getLead() { - return lead; - } - - public void setLead(CmsUiProvider lead) { - this.lead = lead; - } - - public CmsUiProvider getEnd() { - return end; - } - - public void setEnd(CmsUiProvider end) { - this.end = end; - } - - public CmsUiProvider getFooter() { - return footer; - } - - public void setFooter(CmsUiProvider footer) { - this.footer = footer; - } - - static class BundleHttpContext implements HttpContext { - private BundleContext bundleContext; - - public BundleHttpContext(BundleContext bundleContext) { - super(); - this.bundleContext = bundleContext; - } - - @Override - public boolean handleSecurity(HttpServletRequest request, HttpServletResponse response) throws IOException { - // TODO Auto-generated method stub - return true; - } - - @Override - public URL getResource(String name) { - - return bundleContext.getBundle().getEntry(name); - } - - @Override - public String getMimeType(String name) { - return null; - } - - } - -} diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/CmsScriptRwtApplication.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/CmsScriptRwtApplication.java deleted file mode 100644 index 9879fb019..000000000 --- a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/CmsScriptRwtApplication.java +++ /dev/null @@ -1,120 +0,0 @@ -package org.argeo.cms.ui.script; - -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; -import java.net.URL; - -import javax.jcr.Repository; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; -import javax.script.ScriptException; - -import org.argeo.api.cms.CmsLog; -import org.argeo.cms.swt.CmsException; -import org.eclipse.rap.rwt.application.Application; -import org.eclipse.rap.rwt.application.ApplicationConfiguration; -import org.osgi.framework.BundleContext; -import org.osgi.framework.BundleException; -import org.osgi.framework.wiring.BundleWiring; - -public class CmsScriptRwtApplication implements ApplicationConfiguration { - public final static String APP = "APP"; - public final static String BC = "BC"; - - private final CmsLog log = CmsLog.getLog(CmsScriptRwtApplication.class); - - BundleContext bundleContext; - Repository repository; - - ScriptEngine engine; - - public void init(BundleContext bundleContext) { - this.bundleContext = bundleContext; - ClassLoader bundleCl = bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader(); - ClassLoader originalCcl = Thread.currentThread().getContextClassLoader(); - try { -// Thread.currentThread().setContextClassLoader(bundleCl);// GraalVM needs it to be before creating manager -// ScriptEngineManager scriptEngineManager = new ScriptEngineManager(bundleCl); -// engine = scriptEngineManager.getEngineByName("JavaScript"); -// if (engine == null) {// Nashorn -// Thread.currentThread().setContextClassLoader(originalCcl); -// scriptEngineManager = new ScriptEngineManager(); -// Thread.currentThread().setContextClassLoader(bundleCl); -// engine = scriptEngineManager.getEngineByName("JavaScript"); -// } - engine = loadScriptEngine(originalCcl, bundleCl); - - // Load script - URL appUrl = bundleContext.getBundle().getEntry("cms/app.js"); - // System.out.println("Loading " + appUrl); - // System.out.println("Loading " + appUrl.getHost()); - // System.out.println("Loading " + appUrl.getPath()); - - CmsScriptApp app = new CmsScriptApp(engine); - engine.put(APP, app); - engine.put(BC, bundleContext); - try (Reader reader = new InputStreamReader(appUrl.openStream())) { - engine.eval(reader); - } catch (IOException | ScriptException e) { - throw new CmsException("Cannot execute " + appUrl, e); - } - - if (log.isDebugEnabled()) - log.debug("CMS script app initialized from " + appUrl); - - } catch (Exception e) { - e.printStackTrace(); - } finally { - Thread.currentThread().setContextClassLoader(originalCcl); - } - } - - public void destroy(BundleContext bundleContext) { - engine = null; - } - - @Override - public void configure(Application application) { - load(application); - } - - void load(Application application) { - CmsScriptApp app = getApp(); - app.apply(bundleContext, repository, application); - if (log.isDebugEnabled()) - log.debug("CMS script app loaded to " + app.getWebPath()); - } - - CmsScriptApp getApp() { - if (engine == null) - throw new IllegalStateException("CMS script app is not initialized"); - return (CmsScriptApp) engine.get(APP); - } - - void update() { - - try { - bundleContext.getBundle().update(); - } catch (BundleException e) { - e.printStackTrace(); - } - } - - public void setRepository(Repository repository) { - this.repository = repository; - } - - private static ScriptEngine loadScriptEngine(ClassLoader originalCcl, ClassLoader bundleCl) { - Thread.currentThread().setContextClassLoader(bundleCl);// GraalVM needs it to be before creating manager - ScriptEngineManager scriptEngineManager = new ScriptEngineManager(bundleCl); - ScriptEngine engine = scriptEngineManager.getEngineByName("JavaScript"); - if (engine == null) {// Nashorn - Thread.currentThread().setContextClassLoader(originalCcl); - scriptEngineManager = new ScriptEngineManager(); - Thread.currentThread().setContextClassLoader(bundleCl); - engine = scriptEngineManager.getEngineByName("JavaScript"); - } - return engine; - } -} diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/ScriptAppActivator.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/ScriptAppActivator.java deleted file mode 100644 index a55095371..000000000 --- a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/ScriptAppActivator.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.argeo.cms.ui.script; - -import javax.jcr.Repository; - -import org.argeo.api.cms.CmsLog; -import org.osgi.framework.BundleActivator; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.ServiceReference; -import org.osgi.util.tracker.ServiceTracker; - -public class ScriptAppActivator implements BundleActivator { - private final static CmsLog log = CmsLog.getLog(ScriptAppActivator.class); - - @Override - public void start(BundleContext context) throws Exception { - try { - CmsScriptRwtApplication appConfig = new CmsScriptRwtApplication(); - appConfig.init(context); - CmsScriptApp app = appConfig.getApp(); - ServiceTracker repoSt = new ServiceTracker(context, - FrameworkUtil.createFilter("(&" + app.getRepo() + "(objectClass=javax.jcr.Repository))"), null) { - - @Override - public Repository addingService(ServiceReference reference) { - Repository repository = super.addingService(reference); - appConfig.setRepository(repository); - CmsScriptApp app = appConfig.getApp(); - app.register(context, appConfig); - return repository; - } - - }; - repoSt.open(); - } catch (Exception e) { - log.error("Cannot initialise script bundle " + context.getBundle().getSymbolicName(), e); - throw e; - } - } - - @Override - public void stop(BundleContext context) throws Exception { - } - -} diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/ScriptUi.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/ScriptUi.java deleted file mode 100644 index 0c870e1e8..000000000 --- a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/ScriptUi.java +++ /dev/null @@ -1,115 +0,0 @@ -package org.argeo.cms.ui.script; - -import java.net.URL; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.script.Invocable; -import javax.script.ScriptEngine; -import javax.script.ScriptException; - -import org.argeo.api.cms.CmsLog; -import org.argeo.cms.ui.CmsUiProvider; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.osgi.framework.BundleContext; - -class ScriptUi implements CmsUiProvider { - private final static CmsLog log = CmsLog.getLog(ScriptUi.class); - - private boolean development = true; - private ScriptEngine scriptEngine; - - private URL appUrl; - // private BundleContext bundleContext; - // private String path; - - // private Bindings bindings; - // private String script; - - public ScriptUi(BundleContext bundleContext,ScriptEngine scriptEngine, String path) { - this.scriptEngine = scriptEngine; -//// ScriptEngineManager scriptEngineManager = new ScriptEngineManager(); -// ClassLoader bundleCl = bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader(); -// ClassLoader originalCcl = Thread.currentThread().getContextClassLoader(); -// try { -//// Thread.currentThread().setContextClassLoader(bundleCl); -//// scriptEngine = scriptEngineManager.getEngineByName("JavaScript"); -//// scriptEngine.put(CmsScriptRwtApplication.BC, bundleContext); -// scriptEngine = CmsScriptRwtApplication.loadScriptEngine(originalCcl, bundleCl); -// -// } catch (Exception e) { -// e.printStackTrace(); -// } finally { -// Thread.currentThread().setContextClassLoader(originalCcl); -// } - this.appUrl = bundleContext.getBundle().getEntry(path); - load(); - } - - private void load() { -// try (Reader reader = new InputStreamReader(appUrl.openStream())) { -// scriptEngine.eval(reader); -// } catch (IOException | ScriptException e) { -// log.warn("Cannot execute " + appUrl, e); -// } - - try { - scriptEngine.eval("load('" + appUrl + "')"); - } catch (ScriptException e) { - log.warn("Cannot execute " + appUrl, e); - } - - } - - // public ScriptUiProvider(ScriptEngine scriptEngine, String script) throws - // ScriptException { - // super(); - // this.scriptEngine = scriptEngine; - // this.script = script; - // bindings = scriptEngine.createBindings(); - // scriptEngine.eval(script, bindings); - // } - - @Override - public Control createUi(Composite parent, Node context) throws RepositoryException { - long begin = System.currentTimeMillis(); - // if (bindings == null) { - // bindings = scriptEngine.createBindings(); - // try { - // scriptEngine.eval(script, bindings); - // } catch (ScriptException e) { - // log.warn("Cannot evaluate script", e); - // } - // } - // Bindings bindings = scriptEngine.createBindings(); - // bindings.put("parent", parent); - // bindings.put("context", context); - // URL appUrl = bundleContext.getBundle().getEntry(path); - // try (Reader reader = new InputStreamReader(appUrl.openStream())) { - // scriptEngine.eval(reader,bindings); - // } catch (IOException | ScriptException e) { - // log.warn("Cannot execute " + appUrl, e); - // } - - if (development) - load(); - - Invocable invocable = (Invocable) scriptEngine; - try { - invocable.invokeFunction("createUi", parent, context); - } catch (NoSuchMethodException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (ScriptException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - long duration = System.currentTimeMillis() - begin; - if (log.isTraceEnabled()) - log.trace(appUrl + " UI in " + duration + " ms"); - return null; - } - -} diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/cms.js b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/cms.js deleted file mode 100644 index be9618dcb..000000000 --- a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/cms.js +++ /dev/null @@ -1,90 +0,0 @@ -// CMS -var ScrolledPage = Java.type('org.argeo.cms.ui.widgets.ScrolledPage'); - -var CmsScriptApp = Java.type('org.argeo.cms.ui.script.CmsScriptApp'); -var AppUi = Java.type('org.argeo.cms.ui.script.AppUi'); -var Theme = Java.type('org.argeo.cms.ui.script.Theme'); -var ScriptUi = Java.type('org.argeo.cms.ui.script.ScriptUi'); -var CmsUtils = Java.type('org.argeo.cms.ui.util.CmsUiUtils'); -var SimpleCmsHeader = Java.type('org.argeo.cms.ui.util.SimpleCmsHeader'); -var CmsLink = Java.type('org.argeo.cms.ui.util.CmsLink'); -var MenuLink = Java.type('org.argeo.cms.ui.util.MenuLink'); -var UserMenuLink = Java.type('org.argeo.cms.ui.util.UserMenuLink'); - -// SWT -var SWT = Java.type('org.eclipse.swt.SWT'); -var Composite = Java.type('org.eclipse.swt.widgets.Composite'); -var Label = Java.type('org.eclipse.swt.widgets.Label'); -var Button = Java.type('org.eclipse.swt.widgets.Button'); -var Text = Java.type('org.eclipse.swt.widgets.Text'); -var Browser = Java.type('org.eclipse.swt.browser.Browser'); - -var FillLayout = Java.type('org.eclipse.swt.layout.FillLayout'); -var GridLayout = Java.type('org.eclipse.swt.layout.GridLayout'); -var RowLayout = Java.type('org.eclipse.swt.layout.RowLayout'); -var FormLayout = Java.type('org.eclipse.swt.layout.FormLayout'); -var GridData = Java.type('org.eclipse.swt.layout.GridData'); - -function loadNode(node) { - var json = CmsScriptApp.toJson(node) - var fromJson = JSON.parse(json) - return fromJson -} - -function newArea(parent, style, layout) { - var control = new Composite(parent, SWT.NONE) - control.setLayout(layout) - CmsUtils.style(control, style) - return control -} - -function newLabel(parent, style, text) { - var control = new Label(parent, SWT.WRAP) - control.setText(text) - CmsUtils.style(control, style) - CmsUtils.markup(control) - return control -} - -function newButton(parent, style, text) { - var control = new Button(parent, SWT.FLAT) - control.setText(text) - CmsUtils.style(control, style) - CmsUtils.markup(control) - return control -} - -function newFormLabel(parent, style, text) { - return newLabel(parent, style, '' + text + '') -} - -function newText(parent, style, msg) { - var control = new Text(parent, SWT.NONE) - control.setMessage(msg) - CmsUtils.style(control, style) - return control -} - -function newScrolledPage(parent) { - var scrolled = new ScrolledPage(parent, SWT.NONE) - scrolled.setLayoutData(CmsUtils.fillAll()) - scrolled.setLayout(CmsUtils.noSpaceGridLayout()) - var page = new Composite(scrolled, SWT.NONE) - page.setLayout(CmsUtils.noSpaceGridLayout()) - page.setBackgroundMode(SWT.INHERIT_NONE) - return page -} - -function gridData(control) { - var gridData = new GridData() - control.setLayoutData(gridData) - return gridData -} - -function gridData(control, hAlign, vAlign) { - var gridData = new GridData(hAlign, vAlign, false, false) - control.setLayoutData(gridData) - return gridData -} - -// print(__FILE__, __LINE__, __DIR__) diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/package-info.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/package-info.java deleted file mode 100644 index 7440596ab..000000000 --- a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Argeo CMS user interface scripting. */ -package org.argeo.cms.ui.script; \ No newline at end of file diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/AbstractCmsEntryPoint.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/AbstractCmsEntryPoint.java deleted file mode 100644 index f3269f2c4..000000000 --- a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/AbstractCmsEntryPoint.java +++ /dev/null @@ -1,398 +0,0 @@ -package org.argeo.cms.web; - -import static org.argeo.util.directory.ldap.SharedSecret.X_SHARED_SECRET; - -import java.io.IOException; -import java.security.PrivilegedAction; -import java.util.HashMap; -import java.util.Map; - -import javax.jcr.Node; -import javax.jcr.PathNotFoundException; -import javax.jcr.Property; -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.nodetype.NodeType; -import javax.security.auth.Subject; -import javax.security.auth.callback.Callback; -import javax.security.auth.callback.UnsupportedCallbackException; -import javax.security.auth.login.LoginContext; -import javax.security.auth.login.LoginException; -import javax.servlet.http.HttpServletRequest; - -import org.argeo.api.cms.CmsLog; -import org.argeo.api.cms.ux.CmsView; -import org.argeo.api.cms.CmsAuth; -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.auth.RemoteAuthCallback; -import org.argeo.cms.auth.RemoteAuthCallbackHandler; -import org.argeo.cms.servlet.ServletHttpRequest; -import org.argeo.cms.servlet.ServletHttpResponse; -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.swt.CmsStyles; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.eclipse.ui.specific.UiContext; -import org.argeo.jcr.JcrUtils; -import org.argeo.util.directory.ldap.AuthPassword; -import org.argeo.util.directory.ldap.SharedSecret; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.application.AbstractEntryPoint; -import org.eclipse.rap.rwt.client.WebClient; -import org.eclipse.rap.rwt.client.service.BrowserNavigation; -import org.eclipse.rap.rwt.client.service.BrowserNavigationEvent; -import org.eclipse.rap.rwt.client.service.BrowserNavigationListener; -import org.eclipse.rap.rwt.client.service.JavaScriptExecutor; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Shell; - -/** Manages history and navigation */ -@Deprecated -public abstract class AbstractCmsEntryPoint extends AbstractEntryPoint implements CmsView { - private static final long serialVersionUID = 906558779562569784L; - - private final CmsLog log = CmsLog.getLog(AbstractCmsEntryPoint.class); - - // private final Subject subject; - private LoginContext loginContext; - - private final Repository repository; - private final String workspace; - private final String defaultPath; - private final Map factoryProperties; - - // Current state - private Session session; - private Node node; - private String nodePath;// useful when changing auth - private String state; - private Throwable exception; - - // Client services - private final JavaScriptExecutor jsExecutor; - private final BrowserNavigation browserNavigation; - - public AbstractCmsEntryPoint(Repository repository, String workspace, String defaultPath, - Map factoryProperties) { - this.repository = repository; - this.workspace = workspace; - this.defaultPath = defaultPath; - this.factoryProperties = new HashMap(factoryProperties); - // subject = new Subject(); - - // Initial login - LoginContext lc; - try { - lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER, - new RemoteAuthCallbackHandler(new ServletHttpRequest(UiContext.getHttpRequest()), - new ServletHttpResponse(UiContext.getHttpResponse()))); - lc.login(); - } catch (LoginException e) { - try { - lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_ANONYMOUS); - lc.login(); - } catch (LoginException e1) { - throw new CmsException("Cannot log in as anonymous", e1); - } - } - authChange(lc); - - jsExecutor = RWT.getClient().getService(JavaScriptExecutor.class); - browserNavigation = RWT.getClient().getService(BrowserNavigation.class); - if (browserNavigation != null) - browserNavigation.addBrowserNavigationListener(new CmsNavigationListener()); - } - - @Override - protected Shell createShell(Display display) { - Shell shell = super.createShell(display); - shell.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_SHELL); - display.disposeExec(new Runnable() { - - @Override - public void run() { - if (log.isTraceEnabled()) - log.trace("Logging out " + session); - JcrUtils.logoutQuietly(session); - } - }); - return shell; - } - - @Override - protected final void createContents(final Composite parent) { - // UiContext.setData(CmsView.KEY, this); - CmsSwtUtils.registerCmsView(parent.getShell(), this); - Subject.doAs(getSubject(), new PrivilegedAction() { - @Override - public Void run() { - try { - initUi(parent); - } catch (Exception e) { - throw new CmsException("Cannot create entrypoint contents", e); - } - return null; - } - }); - } - - /** Create UI */ - protected abstract void initUi(Composite parent); - - /** Recreate UI after navigation or auth change */ - protected abstract void refresh(); - - /** - * The node to return when no node was found (for authenticated users and - * anonymous) - */ -// private Node getDefaultNode(Session session) throws RepositoryException { -// if (!session.hasPermission(defaultPath, "read")) { -// String userId = session.getUserID(); -// if (userId.equals(NodeConstants.ROLE_ANONYMOUS)) -// // TODO throw a special exception -// throw new CmsException("Login required"); -// else -// throw new CmsException("Unauthorized"); -// } -// return session.getNode(defaultPath); -// } - - protected String getBaseTitle() { - return factoryProperties.get(WebClient.PAGE_TITLE); - } - - public void navigateTo(String state) { - exception = null; - String title = setState(state); - doRefresh(); - if (browserNavigation != null) - browserNavigation.pushState(state, title); - } - - // @Override - // public synchronized Subject getSubject() { - // return subject; - // } - - // @Override - // public LoginContext getLoginContext() { - // return loginContext; - // } - protected Subject getSubject() { - return loginContext.getSubject(); - } - - @Override - public boolean isAnonymous() { - return CurrentUser.isAnonymous(getSubject()); - } - - @Override - public synchronized void logout() { - if (loginContext == null) - throw new CmsException("Login context should not be null"); - try { - CurrentUser.logoutCmsSession(loginContext.getSubject()); - loginContext.logout(); - LoginContext anonymousLc = new LoginContext(CmsAuth.LOGIN_CONTEXT_ANONYMOUS); - anonymousLc.login(); - authChange(anonymousLc); - } catch (LoginException e) { - log.error("Cannot logout", e); - } - } - - @Override - public synchronized void authChange(LoginContext lc) { - if (lc == null) - throw new CmsException("Login context cannot be null"); - // logout previous login context - if (this.loginContext != null) - try { - this.loginContext.logout(); - } catch (LoginException e1) { - log.warn("Could not log out: " + e1); - } - this.loginContext = lc; - Subject.doAs(getSubject(), new PrivilegedAction() { - - @Override - public Void run() { - try { - JcrUtils.logoutQuietly(session); - session = repository.login(workspace); - if (nodePath != null) - try { - node = session.getNode(nodePath); - } catch (PathNotFoundException e) { - navigateTo("~"); - } - - // refresh UI - doRefresh(); - } catch (RepositoryException e) { - throw new CmsException("Cannot perform auth change", e); - } - return null; - } - - }); - } - - @Override - public void exception(final Throwable e) { - AbstractCmsEntryPoint.this.exception = e; - log.error("Unexpected exception in CMS", e); - doRefresh(); - } - - protected synchronized void doRefresh() { - Subject.doAs(getSubject(), new PrivilegedAction() { - @Override - public Void run() { - refresh(); - return null; - } - }); - } - - /** Sets the state of the entry point and retrieve the related JCR node. */ - protected synchronized String setState(String newState) { - String previousState = this.state; - - String newNodePath = null; - String prefix = null; - this.state = newState; - if (newState.equals("~")) - this.state = ""; - - try { - int firstSlash = state.indexOf('/'); - if (firstSlash == 0) { - newNodePath = state; - prefix = ""; - } else if (firstSlash > 0) { - prefix = state.substring(0, firstSlash); - newNodePath = state.substring(firstSlash); - } else { - newNodePath = defaultPath; - prefix = state; - - } - - // auth - int colonIndex = prefix.indexOf('$'); - if (colonIndex > 0) { - SharedSecret token = new SharedSecret(new AuthPassword(X_SHARED_SECRET + '$' + prefix)) { - - @Override - public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { - super.handle(callbacks); - // handle HTTP context - for (Callback callback : callbacks) { - if (callback instanceof RemoteAuthCallback) { - ((RemoteAuthCallback) callback) - .setRequest(new ServletHttpRequest(UiContext.getHttpRequest())); - ((RemoteAuthCallback) callback) - .setResponse(new ServletHttpResponse(UiContext.getHttpResponse())); - } - } - } - }; - LoginContext lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER, token); - lc.login(); - authChange(lc);// sets the node as well - // } else { - // // TODO check consistency - // } - } else { - Node newNode = null; - if (session.nodeExists(newNodePath)) - newNode = session.getNode(newNodePath); - else { -// throw new CmsException("Data " + newNodePath + " does not exist"); - newNode = null; - } - setNode(newNode); - } - String title = publishMetaData(getNode()); - - if (log.isTraceEnabled()) - log.trace("node=" + newNodePath + ", state=" + state + " (prefix=" + prefix + ")"); - - return title; - } catch (Exception e) { - log.error("Cannot set state '" + state + "'", e); - if (state.equals("") || newState.equals("~") || newState.equals(previousState)) - return "Unrecoverable exception : " + e.getClass().getSimpleName(); - if (previousState.equals("")) - previousState = "~"; - navigateTo(previousState); - throw new CmsException("Unexpected issue when accessing #" + newState, e); - } - } - - private String publishMetaData(Node node) throws RepositoryException { - // Title - String title; - if (node != null && node.isNodeType(NodeType.MIX_TITLE) && node.hasProperty(Property.JCR_TITLE)) - title = node.getProperty(Property.JCR_TITLE).getString() + " - " + getBaseTitle(); - else - title = getBaseTitle(); - - HttpServletRequest request = UiContext.getHttpRequest(); - if (request == null) - return null; - - StringBuilder js = new StringBuilder(); - if (title == null) - title = ""; - title = title.replace("'", "\\'");// sanitize - js.append("document.title = '" + title + "';"); - jsExecutor.execute(js.toString()); - return title; - } - - // Simply remove some illegal character - // private String clean(String stringToClean) { - // return stringToClean.replaceAll("'", "").replaceAll("\\n", "") - // .replaceAll("\\t", ""); - // } - - protected synchronized Node getNode() { - return node; - } - - private synchronized void setNode(Node node) throws RepositoryException { - this.node = node; - this.nodePath = node == null ? null : node.getPath(); - } - - protected String getState() { - return state; - } - - protected Throwable getException() { - return exception; - } - - protected void resetException() { - exception = null; - } - - protected Session getSession() { - return session; - } - - private class CmsNavigationListener implements BrowserNavigationListener { - private static final long serialVersionUID = -3591018803430389270L; - - @Override - public void navigated(BrowserNavigationEvent event) { - setState(event.getState()); - doRefresh(); - } - } -} \ No newline at end of file diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/BundleResourceLoader.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/BundleResourceLoader.java deleted file mode 100644 index ca93e625e..000000000 --- a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/BundleResourceLoader.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.argeo.cms.web; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; - -import org.eclipse.rap.rwt.service.ResourceLoader; -import org.osgi.framework.Bundle; - -/** {@link ResourceLoader} implementation wrapping an {@link Bundle}. */ -public class BundleResourceLoader implements ResourceLoader { - private final Bundle bundle; - - public BundleResourceLoader(Bundle bundle) { - this.bundle = bundle; - } - - @Override - public InputStream getResourceAsStream(String resourceName) throws IOException { - URL res = bundle.getEntry(resourceName); - if (res == null) { - res = bundle.getResource(resourceName); - if (res == null) - throw new IllegalArgumentException( - "Resource " + resourceName + " not found in bundle " + bundle.getSymbolicName()); - } - return res.openStream(); - } - - public Bundle getBundle() { - return bundle; - } - -} diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsThemeResourceLoader.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsThemeResourceLoader.java deleted file mode 100644 index 102a4e103..000000000 --- a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsThemeResourceLoader.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.argeo.cms.web; - -import java.io.IOException; -import java.io.InputStream; - -import org.argeo.api.cms.ux.CmsTheme; -import org.eclipse.rap.rwt.service.ResourceLoader; - -/** A RAP {@link ResourceLoader} based on a {@link CmsTheme}. */ -public class CmsThemeResourceLoader implements ResourceLoader { - private final CmsTheme theme; - - public CmsThemeResourceLoader(CmsTheme theme) { - super(); - this.theme = theme; - } - - @Override - public InputStream getResourceAsStream(String resourceName) throws IOException { - return theme.getResourceAsStream(resourceName); - } - -} diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsWebApp.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsWebApp.java deleted file mode 100644 index a1d54cfef..000000000 --- a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsWebApp.java +++ /dev/null @@ -1,163 +0,0 @@ -package org.argeo.cms.web; - -import java.util.Dictionary; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import org.argeo.api.cms.CmsApp; -import org.argeo.api.cms.CmsAppListener; -import org.argeo.api.cms.CmsLog; -import org.argeo.api.cms.ux.CmsTheme; -import org.argeo.api.cms.ux.CmsView; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.util.LangUtils; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.application.Application; -import org.eclipse.rap.rwt.application.Application.OperationMode; -import org.eclipse.rap.rwt.application.ApplicationConfiguration; -import org.eclipse.rap.rwt.application.ExceptionHandler; -import org.eclipse.rap.rwt.client.WebClient; -import org.eclipse.swt.widgets.Display; -import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceRegistration; -import org.osgi.service.event.EventAdmin; - -/** An RWT web app integrating with a {@link CmsApp}. */ -public class CmsWebApp implements ApplicationConfiguration, ExceptionHandler, CmsAppListener { - private final static CmsLog log = CmsLog.getLog(CmsWebApp.class); - - private BundleContext bundleContext; - private CmsApp cmsApp; -// private String cmsAppId; - private EventAdmin eventAdmin; - - private ServiceRegistration rwtAppReg; - - private final static String CONTEXT_NAME = "contextName"; - private String contextName; - - private final static String FAVICON_PNG = "favicon.png"; - - public void init(BundleContext bundleContext, Map properties) { - this.bundleContext = bundleContext; - contextName = properties.get(CONTEXT_NAME); - if (cmsApp != null) { - if (cmsApp.allThemesAvailable()) - publishWebApp(); - } - } - - public void destroy(BundleContext bundleContext, Map properties) { - if (cmsApp != null) { - cmsApp.removeCmsAppListener(this); - cmsApp = null; - } - } - - @Override - public void configure(Application application) { - // TODO make it configurable? - // SWT compatibility is required for: - // - Browser.execute() - // - blocking dialogs - application.setOperationMode(OperationMode.SWT_COMPATIBILITY); - for (String uiName : cmsApp.getUiNames()) { - CmsTheme theme = cmsApp.getTheme(uiName); - if (theme != null) - WebThemeUtils.apply(application, theme); - } - - Map properties = new HashMap<>(); - addEntryPoints(application, properties); - application.setExceptionHandler(this); - } - - @Override - public void handleException(Throwable throwable) { - Display display = Display.getCurrent(); - if (display != null && !display.isDisposed()) { - CmsView cmsView = CmsSwtUtils.getCmsView(display.getActiveShell()); - cmsView.exception(throwable); - } else { - log.error("Unexpected exception outside an UI thread", throwable); - } - - } - - protected void addEntryPoints(Application application, Map commonProperties) { - for (String uiName : cmsApp.getUiNames()) { - Map properties = new HashMap<>(commonProperties); - CmsTheme theme = cmsApp.getTheme(uiName); - if (theme != null) { - properties.put(WebClient.THEME_ID, theme.getThemeId()); - properties.put(WebClient.HEAD_HTML, theme.getHtmlHeaders()); - properties.put(WebClient.BODY_HTML, theme.getBodyHtml()); - Set imagePaths = theme.getImagesPaths(); - if (imagePaths.contains(FAVICON_PNG)) { - properties.put(WebClient.FAVICON, FAVICON_PNG); - } - } else { - properties.put(WebClient.THEME_ID, RWT.DEFAULT_THEME_ID); - } - String entryPointName = !uiName.equals("") ? "/" + uiName : "/"; - application.addEntryPoint(entryPointName, () -> { - CmsWebEntryPoint entryPoint = new CmsWebEntryPoint(this, uiName); - entryPoint.setEventAdmin(eventAdmin); - return entryPoint; - }, properties); - if (log.isDebugEnabled()) - log.info("Added web entry point " + (contextName != null ? "/" + contextName : "") + entryPointName); - } -// if (log.isDebugEnabled()) -// log.debug("Published CMS web app /" + (contextName != null ? contextName : "")); - } - - CmsApp getCmsApp() { - return cmsApp; - } - - BundleContext getBundleContext() { - return bundleContext; - } - - public void setCmsApp(CmsApp cmsApp, Map properties) { - this.cmsApp = cmsApp; -// this.cmsAppId = properties.get(Constants.SERVICE_PID); - this.cmsApp.addCmsAppListener(this); - } - - public void unsetCmsApp(CmsApp cmsApp, Map properties) { - String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY); - if (!contextName.equals(this.contextName)) - return; - if (this.cmsApp != null) { - this.cmsApp.removeCmsAppListener(this); - } - if (rwtAppReg != null) - rwtAppReg.unregister(); - this.cmsApp = null; - } - - @Override - public void themingUpdated() { - if (cmsApp != null && cmsApp.allThemesAvailable()) - publishWebApp(); - } - - protected void publishWebApp() { - Dictionary regProps = LangUtils.dict(CONTEXT_NAME, contextName); - if (rwtAppReg != null) - rwtAppReg.unregister(); - if (bundleContext != null) { - rwtAppReg = bundleContext.registerService(ApplicationConfiguration.class, this, regProps); - if (log.isDebugEnabled()) - log.debug("Publishing CMS web app /" + (contextName != null ? contextName : "") + " ..."); - } - } - - public void setEventAdmin(EventAdmin eventAdmin) { - this.eventAdmin = eventAdmin; - } - -} diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsWebEntryPoint.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsWebEntryPoint.java deleted file mode 100644 index 159719720..000000000 --- a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsWebEntryPoint.java +++ /dev/null @@ -1,368 +0,0 @@ -package org.argeo.cms.web; - -import static org.eclipse.rap.rwt.internal.service.ContextProvider.getApplicationContext; - -import java.security.PrivilegedAction; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import java.util.UUID; - -import javax.security.auth.Subject; -import javax.security.auth.login.LoginContext; -import javax.security.auth.login.LoginException; - -import org.argeo.api.cms.CmsApp; -import org.argeo.api.cms.CmsAuth; -import org.argeo.api.cms.CmsLog; -import org.argeo.api.cms.CmsSession; -import org.argeo.api.cms.ux.CmsImageManager; -import org.argeo.api.cms.ux.CmsUi; -import org.argeo.api.cms.ux.CmsView; -import org.argeo.api.cms.ux.UxContext; -import org.argeo.cms.LocaleUtils; -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.auth.RemoteAuthCallbackHandler; -import org.argeo.cms.servlet.ServletHttpRequest; -import org.argeo.cms.servlet.ServletHttpResponse; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.swt.SimpleSwtUxContext; -import org.argeo.cms.swt.dialogs.CmsFeedback; -import org.argeo.cms.ui.util.DefaultImageManager; -import org.argeo.eclipse.ui.specific.UiContext; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.application.EntryPoint; -import org.eclipse.rap.rwt.client.service.BrowserNavigation; -import org.eclipse.rap.rwt.client.service.BrowserNavigationEvent; -import org.eclipse.rap.rwt.client.service.BrowserNavigationListener; -import org.eclipse.rap.rwt.internal.lifecycle.RWTLifeCycle; -import org.eclipse.swt.SWT; -import org.eclipse.swt.SWTError; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Shell; -import org.osgi.service.event.Event; -import org.osgi.service.event.EventAdmin; - -/** The {@link CmsView} for a {@link CmsWebApp}. */ -@SuppressWarnings("restriction") -public class CmsWebEntryPoint implements EntryPoint, CmsView, BrowserNavigationListener { - private static final long serialVersionUID = 7733510691684570402L; - private final static CmsLog log = CmsLog.getLog(CmsWebEntryPoint.class); - - private EventAdmin eventAdmin; - - private final CmsWebApp cmsWebApp; - private final String uiName; - - private LoginContext loginContext; - private String state; - private Throwable exception; - private UxContext uxContext; - private CmsImageManager imageManager; - - private Display display; - private CmsUi ui; - - private String uid; - - // Client services - // private final JavaScriptExecutor jsExecutor; - private final BrowserNavigation browserNavigation; - - /** Experimental OS-like multi windows. */ - private boolean multipleShells = false; - - public CmsWebEntryPoint(CmsWebApp cmsWebApp, String uiName) { - assert cmsWebApp != null; - assert uiName != null; - this.cmsWebApp = cmsWebApp; - this.uiName = uiName; - uid = UUID.randomUUID().toString(); - - // Initial login - LoginContext lc; - try { - lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER, - new RemoteAuthCallbackHandler(new ServletHttpRequest(UiContext.getHttpRequest()), - new ServletHttpResponse(UiContext.getHttpResponse()))); - lc.login(); - } catch (LoginException e) { - try { - lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_ANONYMOUS, - new RemoteAuthCallbackHandler(new ServletHttpRequest(UiContext.getHttpRequest()), - new ServletHttpResponse(UiContext.getHttpResponse()))); - lc.login(); - } catch (LoginException e1) { - throw new IllegalStateException("Cannot log in as anonymous", e1); - } - } - authChange(lc); - - // jsExecutor = RWT.getClient().getService(JavaScriptExecutor.class); - browserNavigation = RWT.getClient().getService(BrowserNavigation.class); - if (browserNavigation != null) - browserNavigation.addBrowserNavigationListener(this); - } - - protected void createContents(Composite parent) { - Subject.doAs(loginContext.getSubject(), new PrivilegedAction() { - @Override - public Void run() { - try { - uxContext = new SimpleSwtUxContext(); - imageManager = new DefaultImageManager(); - CmsSession cmsSession = getCmsSession(); - if (cmsSession != null) { - UiContext.setLocale(cmsSession.getLocale()); - LocaleUtils.setThreadLocale(cmsSession.getLocale()); - } else { - Locale rwtLocale = RWT.getUISession().getLocale(); - LocaleUtils.setThreadLocale(rwtLocale); - } - parent.setData(CmsApp.UI_NAME_PROPERTY, uiName); - display = parent.getDisplay(); - ui = cmsWebApp.getCmsApp().initUi(parent); - if (ui instanceof Composite) - ((Composite) ui).setLayoutData(CmsSwtUtils.fillAll()); - // we need ui to be set before refresh so that CmsView can store UI context data - // in it. - cmsWebApp.getCmsApp().refreshUi(ui, null); - } catch (Exception e) { - throw new IllegalStateException("Cannot create entrypoint contents", e); - } - return null; - } - }); - } - - protected Subject getSubject() { - return loginContext.getSubject(); - } - - public T doAs(PrivilegedAction action) { - return Subject.doAs(getSubject(), action); - } - - @Override - public boolean isAnonymous() { - return CurrentUser.isAnonymous(getSubject()); - } - - @Override - public synchronized void logout() { - if (loginContext == null) - throw new IllegalArgumentException("Login context should not be null"); - try { - CurrentUser.logoutCmsSession(loginContext.getSubject()); - loginContext.logout(); - LoginContext anonymousLc = new LoginContext(CmsAuth.LOGIN_CONTEXT_ANONYMOUS, - new RemoteAuthCallbackHandler(new ServletHttpRequest(UiContext.getHttpRequest()), - new ServletHttpResponse(UiContext.getHttpResponse()))); - anonymousLc.login(); - authChange(anonymousLc); - } catch (LoginException e) { - log.error("Cannot logout", e); - } - } - - @Override - public synchronized void authChange(LoginContext lc) { - if (lc == null) - throw new IllegalArgumentException("Login context cannot be null"); - // logout previous login context - if (this.loginContext != null) - try { - this.loginContext.logout(); - } catch (LoginException e1) { - log.warn("Could not log out: " + e1); - } - this.loginContext = lc; - doRefresh(); - } - - @Override - public void exception(final Throwable e) { - if (e instanceof SWTError) { - SWTError swtError = (SWTError) e; - if (swtError.code == SWT.ERROR_FUNCTION_DISPOSED) - return; - } - display.syncExec(() -> { -// CmsFeedback.show("Unexpected exception in CMS", e); - exception = e; - log.error("Unexpected exception in CMS", e); - doRefresh(); - }); - } - - protected synchronized void doRefresh() { - if (ui != null) - Subject.doAs(getSubject(), new PrivilegedAction() { - @Override - public Void run() { - if (exception != null) { - // TODO internationalise - CmsFeedback.show("Unexpected exception", exception); - exception = null; - // TODO report - } - cmsWebApp.getCmsApp().refreshUi(ui, state); - return null; - } - }); - } - - /** Sets the state of the entry point and retrieve the related JCR node. */ - protected String setState(String newState) { - cmsWebApp.getCmsApp().setState(ui, newState); - state = newState; - return null; - } - - @Override - public UxContext getUxContext() { - return uxContext; - } - - @Override - public String getUid() { - return uid; - } - - @Override - public void navigateTo(String state) { - exception = null; - String title = setState(state); - if (title != null) - doRefresh(); - if (browserNavigation != null) - browserNavigation.pushState(state, title); - } - - public CmsImageManager getImageManager() { - return imageManager; - } - - @Override - public void navigated(BrowserNavigationEvent event) { - setState(event.getState()); - // doRefresh(); - } - - @Override - public void sendEvent(String topic, Map properties) { - if (properties == null) - properties = new HashMap<>(); - if (properties.containsKey(CMS_VIEW_UID_PROPERTY) && !properties.get(CMS_VIEW_UID_PROPERTY).equals(uid)) - throw new IllegalArgumentException("Property " + CMS_VIEW_UID_PROPERTY + " is set to another CMS view uid (" - + properties.get(CMS_VIEW_UID_PROPERTY) + ") then " + uid); - properties.put(CMS_VIEW_UID_PROPERTY, uid); - eventAdmin.sendEvent(new Event(topic, properties)); - } - - @Override - public void stateChanged(String state, String title) { - browserNavigation.pushState(state, title); - } - - @Override - public CmsSession getCmsSession() { - CmsSession cmsSession = cmsWebApp.getCmsApp().getCmsContext().getCmsSession(getSubject()); - if (cmsSession == null) - throw new IllegalStateException("No CMS session available for " + getSubject()); - return cmsSession; - } - - @Override - public Object getData(String key) { - if (ui != null) { - return ui.getData(key); - } else { - throw new IllegalStateException("UI is not initialized"); - } - } - - @Override - public void setData(String key, Object value) { - if (ui != null) { - ui.setData(key, value); - } else { - throw new IllegalStateException("UI is not initialized"); - } - } - - /* - * EntryPoint IMPLEMENTATION - */ - - @Override - public int createUI() { - Display display = new Display(); - Shell shell = createShell(display); - shell.setLayout(CmsSwtUtils.noSpaceGridLayout()); - CmsSwtUtils.registerCmsView(shell, this); - createContents(shell); - shell.layout(); -// if (shell.getMaximized()) { -// shell.layout(); -// } else { -//// shell.pack(); -// } - shell.open(); - if (getApplicationContext().getLifeCycleFactory().getLifeCycle() instanceof RWTLifeCycle) { - eventLoop: while (!shell.isDisposed()) { - try { - Subject.doAs(loginContext.getSubject(), new PrivilegedAction() { - @Override - public Void run() { - if (!display.readAndDispatch()) { - display.sleep(); - } - return null; - } - }); - } catch (Throwable e) { - if (e instanceof SWTError) { - SWTError swtError = (SWTError) e; - if (swtError.code == SWT.ERROR_FUNCTION_DISPOSED) { - log.error("Unexpected SWT error in event loop, ignoring it. " + e.getMessage()); - continue eventLoop; - } else { - log.error("Unexpected SWT error in event loop, shutting down...", e); - break eventLoop; - } - } else if (e instanceof ThreadDeath) { - throw (ThreadDeath) e; - } else if (e instanceof Error) { - log.error("Unexpected error in event loop, shutting down...", e); - break eventLoop; - } else { - log.error("Unexpected exception in event loop, ignoring it. " + e.getMessage()); - continue eventLoop; - } - } - } - if (!display.isDisposed()) - display.dispose(); - } - return 0; - } - - protected Shell createShell(Display display) { - Shell shell; - if (!multipleShells) { - shell = new Shell(display, SWT.NO_TRIM); - shell.setMaximized(true); - } else { - shell = new Shell(display, SWT.SHELL_TRIM); - shell.setSize(800, 600); - } - return shell; - } - - public void setEventAdmin(EventAdmin eventAdmin) { - this.eventAdmin = eventAdmin; - } - -} diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/MinimalWebApp.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/MinimalWebApp.java deleted file mode 100644 index 2eff71ee8..000000000 --- a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/MinimalWebApp.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.argeo.cms.web; - -import static org.argeo.cms.osgi.BundleCmsTheme.CMS_THEME_BUNDLE_PROPERTY; - -import java.util.HashMap; -import java.util.Map; - -import org.argeo.cms.osgi.BundleCmsTheme; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.application.Application; -import org.eclipse.rap.rwt.application.ApplicationConfiguration; -import org.eclipse.rap.rwt.client.WebClient; -import org.osgi.framework.BundleContext; - -/** Lightweight web app using only RWT and not the whole Eclipse platform. */ -public class MinimalWebApp implements ApplicationConfiguration { - - private BundleCmsTheme theme; - - public void init(BundleContext bundleContext, Map properties) { - if (properties.containsKey(CMS_THEME_BUNDLE_PROPERTY)) { - String cmsThemeBundle = properties.get(CMS_THEME_BUNDLE_PROPERTY).toString(); - theme = new BundleCmsTheme(bundleContext, cmsThemeBundle); - } - } - - public void destroy() { - - } - - /** To be overridden. Does nothing by default. */ - protected void addEntryPoints(Application application, Map properties) { - - } - - @Override - public void configure(Application application) { - if (theme != null) - WebThemeUtils.apply(application, theme); - - Map properties = new HashMap<>(); - if (theme != null) { - properties.put(WebClient.THEME_ID, theme.getThemeId()); - properties.put(WebClient.HEAD_HTML, theme.getHtmlHeaders()); - } else { - properties.put(WebClient.THEME_ID, RWT.DEFAULT_THEME_ID); - } - addEntryPoints(application, properties); - - } - - public void setTheme(BundleCmsTheme theme) { - this.theme = theme; - } - -} diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/SimpleApp.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/SimpleApp.java deleted file mode 100644 index 38a9b4449..000000000 --- a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/SimpleApp.java +++ /dev/null @@ -1,414 +0,0 @@ -package org.argeo.cms.web; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Hashtable; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.security.Privilege; -import javax.jcr.version.VersionManager; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.api.cms.CmsLog; -import org.argeo.cms.jcr.CmsJcrUtils; -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.ui.CmsUiConstants; -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.cms.ui.LifeCycleUiProvider; -import org.argeo.cms.ui.util.CmsUiUtils; -import org.argeo.cms.ui.util.StyleSheetResourceLoader; -import org.argeo.jcr.JcrUtils; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.application.Application; -import org.eclipse.rap.rwt.application.Application.OperationMode; -import org.eclipse.rap.rwt.application.ApplicationConfiguration; -import org.eclipse.rap.rwt.application.EntryPoint; -import org.eclipse.rap.rwt.application.EntryPointFactory; -import org.eclipse.rap.rwt.application.ExceptionHandler; -import org.eclipse.rap.rwt.client.WebClient; -import org.eclipse.rap.rwt.client.service.JavaScriptExecutor; -import org.eclipse.rap.rwt.service.ResourceLoader; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.layout.FillLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceRegistration; - -/** A basic generic app based on {@link SimpleErgonomics}. */ -@Deprecated -public class SimpleApp implements CmsUiConstants, ApplicationConfiguration { - private final static CmsLog log = CmsLog.getLog(SimpleApp.class); - - private String contextName = null; - - private Map> branding = new HashMap>(); - private Map> styleSheets = new HashMap>(); - - private List resources = new ArrayList(); - - private BundleContext bundleContext; - - private Repository repository; - private String workspace = null; - private String jcrBasePath = "/"; - private List roPrincipals = Arrays.asList(CmsConstants.ROLE_ANONYMOUS, CmsConstants.ROLE_USER); - private List rwPrincipals = Arrays.asList(CmsConstants.ROLE_USER); - - private CmsUiProvider header; - private Map pages = new LinkedHashMap(); - - private Integer headerHeight = 40; - - private ServiceRegistration appReg; - - public void configure(Application application) { - try { - BundleResourceLoader bundleRL = new BundleResourceLoader(bundleContext.getBundle()); - - application.setOperationMode(OperationMode.SWT_COMPATIBILITY); - // application.setOperationMode(OperationMode.JEE_COMPATIBILITY); - - application.setExceptionHandler(new CmsExceptionHandler()); - - // loading animated gif - application.addResource(LOADING_IMAGE, createResourceLoader(LOADING_IMAGE)); - // empty image - application.addResource(NO_IMAGE, createResourceLoader(NO_IMAGE)); - - for (String resource : resources) { - application.addResource(resource, bundleRL); - if (log.isTraceEnabled()) - log.trace("Resource " + resource); - } - - Map defaultBranding = null; - if (branding.containsKey("*")) - defaultBranding = branding.get("*"); - // String defaultTheme = defaultBranding.get(WebClient.THEME_ID); - - // entry points - for (String page : pages.keySet()) { - Map properties = defaultBranding != null ? new HashMap(defaultBranding) - : new HashMap(); - if (branding.containsKey(page)) { - properties.putAll(branding.get(page)); - } - // favicon - if (properties.containsKey(WebClient.FAVICON)) { - String themeId = defaultBranding.get(WebClient.THEME_ID); - Bundle themeBundle = findThemeBundle(bundleContext, themeId); - String faviconRelPath = properties.get(WebClient.FAVICON); - application.addResource(faviconRelPath, - new BundleResourceLoader(themeBundle != null ? themeBundle : bundleContext.getBundle())); - if (log.isTraceEnabled()) - log.trace("Favicon " + faviconRelPath); - - } - - // page title - if (!properties.containsKey(WebClient.PAGE_TITLE)) { - if (page.length() > 0) - properties.put(WebClient.PAGE_TITLE, Character.toUpperCase(page.charAt(0)) + page.substring(1)); - } - - // default body HTML - if (!properties.containsKey(WebClient.BODY_HTML)) - properties.put(WebClient.BODY_HTML, DEFAULT_LOADING_BODY); - - // - // ADD ENTRY POINT - // - application.addEntryPoint("/" + page, - new CmsEntryPointFactory(pages.get(page), repository, workspace, properties), properties); - log.info("Page /" + page); - } - - // stylesheets and themes - Set themeBundles = new HashSet<>(); - for (String themeId : styleSheets.keySet()) { - Bundle themeBundle = findThemeBundle(bundleContext, themeId); - StyleSheetResourceLoader styleSheetRL = new StyleSheetResourceLoader( - themeBundle != null ? themeBundle : bundleContext.getBundle()); - if (themeBundle != null) - themeBundles.add(themeBundle); - List cssLst = styleSheets.get(themeId); - if (log.isDebugEnabled()) - log.debug("Theme " + themeId); - for (String css : cssLst) { - application.addStyleSheet(themeId, css, styleSheetRL); - if (log.isDebugEnabled()) - log.debug(" CSS " + css); - } - - } - for (Bundle themeBundle : themeBundles) { - BundleResourceLoader themeBRL = new BundleResourceLoader(themeBundle); - SimpleApp.addThemeResources(application, themeBundle, themeBRL, "*.png"); - SimpleApp.addThemeResources(application, themeBundle, themeBRL, "*.gif"); - SimpleApp.addThemeResources(application, themeBundle, themeBRL, "*.jpg"); - } - } catch (RuntimeException e) { - // Easier access to initialisation errors - log.error("Unexpected exception when configuring RWT application.", e); - throw e; - } - } - - public void init() throws RepositoryException { - Session session = null; - try { - session = CmsJcrUtils.openDataAdminSession(repository, workspace); - // session = JcrUtils.loginOrCreateWorkspace(repository, workspace); - VersionManager vm = session.getWorkspace().getVersionManager(); - JcrUtils.mkdirs(session, jcrBasePath); - session.save(); - if (!vm.isCheckedOut(jcrBasePath)) - vm.checkout(jcrBasePath); - for (String principal : rwPrincipals) - JcrUtils.addPrivilege(session, jcrBasePath, principal, Privilege.JCR_WRITE); - for (String principal : roPrincipals) - JcrUtils.addPrivilege(session, jcrBasePath, principal, Privilege.JCR_READ); - - for (String pageName : pages.keySet()) { - try { - initPage(session, pages.get(pageName)); - session.save(); - } catch (Exception e) { - throw new CmsException("Cannot initialize page " + pageName, e); - } - } - - } finally { - JcrUtils.logoutQuietly(session); - } - - // publish to OSGi - register(); - } - - protected void initPage(Session adminSession, CmsUiProvider page) throws RepositoryException { - if (page instanceof LifeCycleUiProvider) - ((LifeCycleUiProvider) page).init(adminSession); - } - - public void destroy() { - for (String pageName : pages.keySet()) { - try { - CmsUiProvider page = pages.get(pageName); - if (page instanceof LifeCycleUiProvider) - ((LifeCycleUiProvider) page).destroy(); - } catch (Exception e) { - log.error("Cannot destroy page " + pageName, e); - } - } - } - - protected void register() { - Hashtable props = new Hashtable(); - if (contextName != null) - props.put("contextName", contextName); - appReg = bundleContext.registerService(ApplicationConfiguration.class, this, props); - if (log.isDebugEnabled()) - log.debug("Registered " + (contextName == null ? "/" : contextName)); - } - - protected void unregister() { - appReg.unregister(); - if (log.isDebugEnabled()) - log.debug("Unregistered " + (contextName == null ? "/" : contextName)); - } - - public void setRepository(Repository repository) { - this.repository = repository; - } - - public void setWorkspace(String workspace) { - this.workspace = workspace; - } - - public void setHeader(CmsUiProvider header) { - this.header = header; - } - - public void setPages(Map pages) { - this.pages = pages; - } - - public void setJcrBasePath(String basePath) { - this.jcrBasePath = basePath; - } - - public void setRoPrincipals(List roPrincipals) { - this.roPrincipals = roPrincipals; - } - - public void setRwPrincipals(List rwPrincipals) { - this.rwPrincipals = rwPrincipals; - } - - public void setHeaderHeight(Integer headerHeight) { - this.headerHeight = headerHeight; - } - - public void setBranding(Map> branding) { - this.branding = branding; - } - - public void setStyleSheets(Map> styleSheets) { - this.styleSheets = styleSheets; - } - - public void setBundleContext(BundleContext bundleContext) { - this.bundleContext = bundleContext; - } - - public void setResources(List resources) { - this.resources = resources; - } - - public void setContextName(String contextName) { - this.contextName = contextName; - } - - private static void addThemeResources(Application application, Bundle themeBundle, BundleResourceLoader themeBRL, - String pattern) { - Enumeration themeResources = themeBundle.findEntries("/", pattern, true); - if (themeResources == null) - return; - while (themeResources.hasMoreElements()) { - String resource = themeResources.nextElement().getPath(); - // remove first '/' so that RWT registers it - resource = resource.substring(1); - if (!resource.endsWith("/")) { - application.addResource(resource, themeBRL); - if (log.isTraceEnabled()) - log.trace("Registered " + resource + " from theme " + themeBundle); - } - - } - - } - - private static Bundle findThemeBundle(BundleContext bundleContext, String themeId) { - if (themeId == null) - return null; - // TODO optimize - // TODO deal with multiple versions - Bundle themeBundle = null; - if (themeId != null) { - for (Bundle bundle : bundleContext.getBundles()) - if (themeId.equals(bundle.getSymbolicName())) { - themeBundle = bundle; - break; - } - } - return themeBundle; - } - - class CmsExceptionHandler implements ExceptionHandler { - - @Override - public void handleException(Throwable throwable) { - // TODO be smarter - CmsUiUtils.getCmsView().exception(throwable); - } - - } - - private class CmsEntryPointFactory implements EntryPointFactory { - private final CmsUiProvider page; - private final Repository repository; - private final String workspace; - private final Map properties; - - public CmsEntryPointFactory(CmsUiProvider page, Repository repository, String workspace, - Map properties) { - this.page = page; - this.repository = repository; - this.workspace = workspace; - this.properties = properties; - } - - @Override - public EntryPoint create() { - SimpleErgonomics entryPoint = new SimpleErgonomics(repository, workspace, jcrBasePath, page, properties) { - private static final long serialVersionUID = -637940404865527290L; - - @Override - protected void createAdminArea(Composite parent) { - Composite adminArea = new Composite(parent, SWT.NONE); - adminArea.setLayout(new FillLayout()); - Button refresh = new Button(adminArea, SWT.PUSH); - refresh.setText("Reload App"); - refresh.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = -7671999525536351366L; - - @Override - public void widgetSelected(SelectionEvent e) { - long timeBeforeReload = 1000; - RWT.getClient().getService(JavaScriptExecutor.class).execute( - "setTimeout(function() { " + "location.reload();" + "}," + timeBeforeReload + ");"); - reloadApp(); - } - }); - } - }; - // entryPoint.setState(""); - entryPoint.setHeader(header); - entryPoint.setHeaderHeight(headerHeight); - // CmsSession.current.set(entryPoint); - return entryPoint; - } - - private void reloadApp() { - new Thread("Refresh app") { - @Override - public void run() { - unregister(); - register(); - } - }.start(); - } - } - - private static ResourceLoader createResourceLoader(final String resourceName) { - return new ResourceLoader() { - public InputStream getResourceAsStream(String resourceName) throws IOException { - return getClass().getClassLoader().getResourceAsStream(resourceName); - } - }; - } - - // private static ResourceLoader createUrlResourceLoader(final URL url) { - // return new ResourceLoader() { - // public InputStream getResourceAsStream(String resourceName) - // throws IOException { - // return url.openStream(); - // } - // }; - // } - - /* - * TEXTS - */ - private static String DEFAULT_LOADING_BODY = "" - + "" + ""; -} diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/SimpleErgonomics.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/SimpleErgonomics.java deleted file mode 100644 index 783f6eb73..000000000 --- a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/SimpleErgonomics.java +++ /dev/null @@ -1,238 +0,0 @@ -package org.argeo.cms.web; - -import java.util.Map; -import java.util.UUID; - -import javax.jcr.Node; -import javax.jcr.Repository; -import javax.jcr.RepositoryException; - -import org.argeo.api.cms.CmsLog; -import org.argeo.api.cms.ux.CmsImageManager; -import org.argeo.api.cms.ux.UxContext; -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.swt.CmsStyles; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.swt.SimpleSwtUxContext; -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.cms.ui.util.DefaultImageManager; -import org.argeo.cms.ui.util.SystemNotifications; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.FillLayout; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -/** Simple header/body ergonomics. */ -@Deprecated -public class SimpleErgonomics extends AbstractCmsEntryPoint { - private static final long serialVersionUID = 8743413921359548523L; - - private final static CmsLog log = CmsLog.getLog(SimpleErgonomics.class); - - private boolean uiInitialized = false; - private Composite headerArea; - private Composite leftArea; - private Composite rightArea; - private Composite footerArea; - private Composite bodyArea; - private final CmsUiProvider uiProvider; - - private CmsUiProvider header; - private Integer headerHeight = 0; - private Integer footerHeight = 0; - private CmsUiProvider lead; - private CmsUiProvider end; - private CmsUiProvider footer; - - private CmsImageManager imageManager = new DefaultImageManager(); - private UxContext uxContext = null; - private String uid; - - public SimpleErgonomics(Repository repository, String workspace, String defaultPath, CmsUiProvider uiProvider, - Map factoryProperties) { - super(repository, workspace, defaultPath, factoryProperties); - this.uiProvider = uiProvider; - } - - @Override - protected void initUi(Composite parent) { - uid = UUID.randomUUID().toString(); - parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - parent.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(3, false))); - - uxContext = new SimpleSwtUxContext(); - if (!getUxContext().isMasterData()) - createAdminArea(parent); - headerArea = new Composite(parent, SWT.NONE); - headerArea.setLayout(new FillLayout()); - GridData headerData = new GridData(SWT.FILL, SWT.FILL, false, false, 3, 1); - headerData.heightHint = headerHeight; - headerArea.setLayoutData(headerData); - - // TODO: bi-directional - leftArea = new Composite(parent, SWT.NONE); - leftArea.setLayoutData(new GridData(SWT.LEAD, SWT.TOP, false, false)); - leftArea.setLayout(CmsSwtUtils.noSpaceGridLayout()); - - bodyArea = new Composite(parent, SWT.NONE); - bodyArea.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_BODY); - bodyArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - bodyArea.setLayout(CmsSwtUtils.noSpaceGridLayout()); - - // TODO: bi-directional - rightArea = new Composite(parent, SWT.NONE); - rightArea.setLayoutData(new GridData(SWT.END, SWT.TOP, false, false)); - rightArea.setLayout(CmsSwtUtils.noSpaceGridLayout()); - - footerArea = new Composite(parent, SWT.NONE); - // footerArea.setLayout(new FillLayout()); - GridData footerData = new GridData(SWT.FILL, SWT.FILL, false, false, 3, 1); - footerData.heightHint = footerHeight; - footerArea.setLayoutData(footerData); - - uiInitialized = true; - refresh(); - } - - @Override - protected void refresh() { - if (!uiInitialized) - return; - if (getState() == null) - setState(""); - refreshSides(); - refreshBody(); - if (log.isTraceEnabled()) - log.trace("UI refreshed " + getNode()); - } - - protected void createAdminArea(Composite parent) { - } - - @Deprecated - protected void refreshHeader() { - if (header == null) - return; - - for (Control child : headerArea.getChildren()) - child.dispose(); - try { - header.createUi(headerArea, getNode()); - } catch (RepositoryException e) { - throw new CmsException("Cannot refresh header", e); - } - headerArea.layout(true, true); - } - - protected void refreshSides() { - refresh(headerArea, header, CmsStyles.CMS_HEADER); - refresh(leftArea, lead, CmsStyles.CMS_LEAD); - refresh(rightArea, end, CmsStyles.CMS_END); - refresh(footerArea, footer, CmsStyles.CMS_FOOTER); - } - - private void refresh(Composite area, CmsUiProvider uiProvider, String style) { - if (uiProvider == null) - return; - - for (Control child : area.getChildren()) - child.dispose(); - CmsSwtUtils.style(area, style); - try { - uiProvider.createUi(area, getNode()); - } catch (RepositoryException e) { - throw new CmsException("Cannot refresh header", e); - } - area.layout(true, true); - } - - protected void refreshBody() { - // Exception - Throwable exception = getException(); - if (exception != null) { - SystemNotifications systemNotifications = new SystemNotifications(bodyArea); - systemNotifications.notifyException(exception); - resetException(); - return; - // TODO report - } - - // clear - for (Control child : bodyArea.getChildren()) - child.dispose(); - bodyArea.setLayout(CmsSwtUtils.noSpaceGridLayout()); - - try { - Node node = getNode(); -// if (node == null) -// log.error("Context cannot be null"); -// else - uiProvider.createUi(bodyArea, node); - } catch (RepositoryException e) { - throw new CmsException("Cannot refresh body", e); - } - - bodyArea.layout(true, true); - } - - @Override - public UxContext getUxContext() { - return uxContext; - } - @Override - public String getUid() { - return uid; - } - - public CmsImageManager getImageManager() { - return imageManager; - } - - public void setHeader(CmsUiProvider header) { - this.header = header; - } - - public void setHeaderHeight(Integer headerHeight) { - this.headerHeight = headerHeight; - } - - public void setImageManager(CmsImageManager imageManager) { - this.imageManager = imageManager; - } - - public CmsUiProvider getLead() { - return lead; - } - - public void setLead(CmsUiProvider lead) { - this.lead = lead; - } - - public CmsUiProvider getEnd() { - return end; - } - - public void setEnd(CmsUiProvider end) { - this.end = end; - } - - public CmsUiProvider getFooter() { - return footer; - } - - public void setFooter(CmsUiProvider footer) { - this.footer = footer; - } - - public CmsUiProvider getHeader() { - return header; - } - - public void setFooterHeight(Integer footerHeight) { - this.footerHeight = footerHeight; - } - -} diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/WebThemeUtils.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/WebThemeUtils.java deleted file mode 100644 index e51644b9f..000000000 --- a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/WebThemeUtils.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.argeo.cms.web; - -import org.argeo.api.cms.CmsLog; -import org.argeo.api.cms.ux.CmsTheme; -import org.eclipse.rap.rwt.application.Application; -import org.eclipse.rap.rwt.service.ResourceLoader; - -/** Web specific utilities around theming. */ -public class WebThemeUtils { - private final static CmsLog log = CmsLog.getLog(WebThemeUtils.class); - - public static void apply(Application application, CmsTheme theme) { - ResourceLoader resourceLoader = new CmsThemeResourceLoader(theme); - resources: for (String path : theme.getImagesPaths()) { - if (path.startsWith("target/")) - continue resources; // skip maven output - application.addResource(path, resourceLoader); - if (log.isTraceEnabled()) - log.trace("Theme " + theme.getThemeId() + ": added resource " + path); - } - for (String path : theme.getRapCssPaths()) { - application.addStyleSheet(theme.getThemeId(), path, resourceLoader); - if (log.isDebugEnabled()) - log.debug("Theme " + theme.getThemeId() + ": added RAP CSS " + path); - } - } - -} diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/osgi/CmsWebAppFactory.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/osgi/CmsWebAppFactory.java deleted file mode 100644 index 8380a85dd..000000000 --- a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/osgi/CmsWebAppFactory.java +++ /dev/null @@ -1,54 +0,0 @@ -package org.argeo.cms.web.osgi; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.Map; - -import org.argeo.api.cms.CmsApp; -import org.argeo.cms.web.CmsWebApp; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; -import org.osgi.service.event.EventAdmin; - -/** Publish a CmsApp as a RAP application. */ -public class CmsWebAppFactory { - private BundleContext bundleContext = FrameworkUtil.getBundle(CmsWebAppFactory.class).getBundleContext(); - private final static String CONTEXT_NAME = "contextName"; - - private EventAdmin eventAdmin; - - private Map registrations = Collections.synchronizedMap(new HashMap<>()); - - public void addCmsApp(CmsApp cmsApp, Map properties) { - String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY); - if (contextName != null) { - CmsWebApp cmsWebApp = new CmsWebApp(); - cmsWebApp.setEventAdmin(eventAdmin); - cmsWebApp.setCmsApp(cmsApp, properties); - Hashtable serviceProperties = new Hashtable<>(); - if (!contextName.equals("")) - serviceProperties.put(CONTEXT_NAME, contextName); - cmsWebApp.init(bundleContext, serviceProperties); - registrations.put(contextName, cmsWebApp); - } - } - - public void removeCmsApp(CmsApp cmsApp, Map properties) { - String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY); - if (contextName != null) { - CmsWebApp cmsWebApp = registrations.get(contextName); - if (cmsWebApp != null) { - cmsWebApp.destroy(bundleContext, new HashMap<>()); - cmsWebApp.unsetCmsApp(cmsApp, properties); - } else { - // TODO log warning - } - } - } - - public void setEventAdmin(EventAdmin eventAdmin) { - this.eventAdmin = eventAdmin; - } - -} diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/eclipse/ui/jetty/RwtRunner.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/eclipse/ui/jetty/RwtRunner.java deleted file mode 100644 index 29165a4ef..000000000 --- a/rap/org.argeo.cms.ui.rap/src/org/argeo/eclipse/ui/jetty/RwtRunner.java +++ /dev/null @@ -1,135 +0,0 @@ -package org.argeo.eclipse.ui.jetty; - -import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.nio.file.Files; -import java.nio.file.Path; - -import javax.servlet.ServletContextEvent; -import javax.servlet.ServletContextListener; - -import org.eclipse.jetty.server.Connector; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.ServerConnector; -import org.eclipse.jetty.servlet.DefaultServlet; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.eclipse.jetty.util.resource.Resource; -import org.eclipse.jetty.util.thread.QueuedThreadPool; -import org.eclipse.rap.rwt.application.AbstractEntryPoint; -import org.eclipse.rap.rwt.application.ApplicationRunner; -import org.eclipse.rap.rwt.engine.RWTServlet; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; - -/** A minimal RWT runner based on embedded Jetty. */ -public class RwtRunner { - - private final Server server; - private final ServerConnector serverConnector; - private Path tempDir; - - public RwtRunner() { - server = new Server(new QueuedThreadPool(10, 1)); - serverConnector = new ServerConnector(server); - serverConnector.setPort(0); - server.setConnectors(new Connector[] { serverConnector }); - } - - protected Control createUi(Composite parent, Object context) { - return new Label(parent, SWT.NONE); - } - - public void init() { - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); - context.setContextPath("/"); - server.setHandler(context); - - String entryPoint = "app"; - - // rwt-resources requires a file system - try { - tempDir = Files.createTempDirectory("argeo-rwtRunner"); - context.setBaseResource(Resource.newResource(tempDir.resolve("www").toString())); - } catch (IOException e) { - throw new IllegalStateException("Cannot create temporary directory", e); - } - context.addEventListener(new ServletContextListener() { - ApplicationRunner applicationRunner; - - @Override - public void contextInitialized(ServletContextEvent sce) { - applicationRunner = new ApplicationRunner( - (application) -> application.addEntryPoint("/" + entryPoint, () -> new AbstractEntryPoint() { - private static final long serialVersionUID = 5678385921969090733L; - - @Override - protected void createContents(Composite parent) { - createUi(parent, null); - } - }, null), sce.getServletContext()); - applicationRunner.start(); - } - - @Override - public void contextDestroyed(ServletContextEvent sce) { - applicationRunner.stop(); - } - }); - - context.addServlet(new ServletHolder(new RWTServlet()), "/" + entryPoint); - - // Required to serve rwt-resources. It is important that this is last. - ServletHolder holderPwd = new ServletHolder("default", DefaultServlet.class); - context.addServlet(holderPwd, "/"); - - try { - server.start(); - } catch (Exception e) { - throw new IllegalStateException("Cannot start Jetty server", e); - } - } - - public void destroy() { - try { - serverConnector.close(); - server.stop(); - // TODO delete temp dir - } catch (Exception e) { - e.printStackTrace(); - } - } - - public Integer getEffectivePort() { - return serverConnector.getLocalPort(); - } - - public void waitFor() throws InterruptedException { - server.join(); - } - - public static void main(String[] args) throws Exception { - RwtRunner rwtRunner = new RwtRunner() { - - @Override - protected Control createUi(Composite parent, Object context) { - Label label = new Label(parent, SWT.NONE); - label.setText("Hello world!"); - return label; - } - }; - rwtRunner.init(); - Runtime.getRuntime().addShutdownHook(new Thread(() -> rwtRunner.destroy(), "Jetty shutdown")); - - long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime(); - System.out.println("App available in " + jvmUptime + " ms, on port " + rwtRunner.getEffectivePort()); - - // open browser in app mode - Thread.sleep(2000);// wait for RWT to be ready - Runtime.getRuntime().exec("google-chrome --app=http://localhost:" + rwtRunner.getEffectivePort() + "/app"); - - rwtRunner.waitFor(); - } -} diff --git a/rap/org.argeo.swt.specific.rap/.classpath b/rap/org.argeo.swt.specific.rap/.classpath deleted file mode 100644 index e03d341b1..000000000 --- a/rap/org.argeo.swt.specific.rap/.classpath +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - diff --git a/rap/org.argeo.swt.specific.rap/.project b/rap/org.argeo.swt.specific.rap/.project deleted file mode 100644 index 53d797685..000000000 --- a/rap/org.argeo.swt.specific.rap/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - org.argeo.swt.specific.rap - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/rap/org.argeo.swt.specific.rap/META-INF/.gitignore b/rap/org.argeo.swt.specific.rap/META-INF/.gitignore deleted file mode 100644 index 4854a41b9..000000000 --- a/rap/org.argeo.swt.specific.rap/META-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/MANIFEST.MF diff --git a/rap/org.argeo.swt.specific.rap/bnd.bnd b/rap/org.argeo.swt.specific.rap/bnd.bnd deleted file mode 100644 index bcd9b195f..000000000 --- a/rap/org.argeo.swt.specific.rap/bnd.bnd +++ /dev/null @@ -1,5 +0,0 @@ -Import-Package: org.eclipse.swt,\ -org.eclipse.jface.dialogs,\ -org.eclipse.swt.events,\ -javax.servlet.http;version="[3,5)",\ -* diff --git a/rap/org.argeo.swt.specific.rap/build.properties b/rap/org.argeo.swt.specific.rap/build.properties deleted file mode 100644 index fd806ca05..000000000 --- a/rap/org.argeo.swt.specific.rap/build.properties +++ /dev/null @@ -1,2 +0,0 @@ -source.. = src/ -output.. = bin/ diff --git a/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/BufferedImageDisplay.java b/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/BufferedImageDisplay.java deleted file mode 100644 index ac4e0dfb1..000000000 --- a/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/BufferedImageDisplay.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.argeo.eclipse.ui.specific; - -import java.awt.image.BufferedImage; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; - -public class BufferedImageDisplay extends Composite { - private static final long serialVersionUID = 4541163690514461514L; - private BufferedImage image; - - public BufferedImageDisplay(Composite parent, int style) { - super(parent, SWT.NO_BACKGROUND); - } - - public void setImage(BufferedImage image) { - this.image = image; - } - -} diff --git a/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/CmsFileDialog.java b/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/CmsFileDialog.java deleted file mode 100644 index 6100c1a83..000000000 --- a/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/CmsFileDialog.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.argeo.eclipse.ui.specific; - -import org.eclipse.swt.widgets.FileDialog; -import org.eclipse.swt.widgets.Shell; - -public class CmsFileDialog extends FileDialog { - private static final long serialVersionUID = -7540791204102318801L; - - public CmsFileDialog(Shell parent, int style) { - super(parent, style); - } - - public CmsFileDialog(Shell parent) { - super(parent); - } - -} diff --git a/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/CmsFileUpload.java b/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/CmsFileUpload.java deleted file mode 100644 index 3f30bde25..000000000 --- a/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/CmsFileUpload.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.argeo.eclipse.ui.specific; - -import org.eclipse.rap.rwt.widgets.FileUpload; -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.widgets.Composite; - -public class CmsFileUpload extends FileUpload { - private static final long serialVersionUID = 8027963992680980549L; - - public CmsFileUpload(Composite parent, int style) { - super(parent, style); - } - - @Override - public void setText(String text) { - super.setText(text); - } - - @Override - public String getFileName() { - return super.getFileName(); - } - - @Override - public String[] getFileNames() { - return super.getFileNames(); - } - - @Override - public void addSelectionListener(SelectionListener listener) { - super.addSelectionListener(listener); - } - -} diff --git a/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java b/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java deleted file mode 100644 index a89b921cd..000000000 --- a/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.argeo.eclipse.ui.specific; - -import org.eclipse.jface.viewers.AbstractTableViewer; -import org.eclipse.jface.viewers.ColumnViewer; -import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.swt.widgets.Widget; - -/** Static utilities to bridge differences between RCP and RAP */ -public class EclipseUiSpecificUtils { - - public static void setStyleData(Widget widget, Object data) { - widget.setData(RWT.CUSTOM_VARIANT, data); - } - - public static Object getStyleData(Widget widget) { - return widget.getData(RWT.CUSTOM_VARIANT); - } - - public static void setMarkupData(Widget widget) { - widget.setData(RWT.MARKUP_ENABLED, true); - } - - public static void setMarkupValidationDisabledData(Widget widget) { - widget.setData("org.eclipse.rap.rwt.markupValidationDisabled", Boolean.TRUE); - } - - /** - * TootlTip support is supported only for {@link AbstractTableViewer} in RAP - */ - public static void enableToolTipSupport(Viewer viewer) { - if (viewer instanceof ColumnViewer) - ColumnViewerToolTipSupport.enableFor((ColumnViewer) viewer); - } - - private EclipseUiSpecificUtils() { - } -} diff --git a/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/FileDropAdapter.java b/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/FileDropAdapter.java deleted file mode 100644 index f9ca81682..000000000 --- a/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/FileDropAdapter.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.argeo.eclipse.ui.specific; - -import java.io.IOException; -import java.io.InputStream; - -import org.eclipse.rap.fileupload.FileDetails; -import org.eclipse.rap.fileupload.FileUploadHandler; -import org.eclipse.rap.fileupload.FileUploadReceiver; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.client.ClientFile; -import org.eclipse.rap.rwt.client.service.ClientFileUploader; -import org.eclipse.rap.rwt.dnd.ClientFileTransfer; -import org.eclipse.swt.dnd.DND; -import org.eclipse.swt.dnd.DropTarget; -import org.eclipse.swt.dnd.DropTargetAdapter; -import org.eclipse.swt.dnd.DropTargetEvent; -import org.eclipse.swt.dnd.Transfer; -import org.eclipse.swt.widgets.Control; - -/** Configures a {@link Control} to receive files drop from the client OS. */ -public class FileDropAdapter { - - public void prepareDropTarget(Control control, DropTarget dropTarget) { - dropTarget.setTransfer(new Transfer[] { ClientFileTransfer.getInstance() }); - dropTarget.addDropListener(new DropTargetAdapter() { - private static final long serialVersionUID = 5361645765549463168L; - - @Override - public void dropAccept(DropTargetEvent event) { - if (!ClientFileTransfer.getInstance().isSupportedType(event.currentDataType)) { - event.detail = DND.DROP_NONE; - } - } - - @Override - public void drop(DropTargetEvent event) { - handleFileDrop(control, event); - } - }); - } - - public void handleFileDrop(Control control, DropTargetEvent event) { - ClientFile[] clientFiles = (ClientFile[]) event.data; - ClientFileUploader service = RWT.getClient().getService(ClientFileUploader.class); -// DiskFileUploadReceiver receiver = new DiskFileUploadReceiver(); - FileUploadReceiver receiver = new FileUploadReceiver() { - - @Override - public void receive(InputStream stream, FileDetails details) throws IOException { - control.getDisplay().syncExec(() -> { - try { - processUpload(stream, details.getFileName(), details.getContentType()); - } catch (IOException e) { - throw new IllegalStateException("Cannot process upload of " + details.getFileName(), e); - } - }); - } - }; - FileUploadHandler handler = new FileUploadHandler(receiver); -// handler.setMaxFileSize( sizeLimit ); -// handler.setUploadTimeLimit( timeLimit ); - service.submit(handler.getUploadUrl(), clientFiles); -// for (File file : receiver.getTargetFiles()) { -// paths.add(file.toPath()); -// } - } - - /** Executed in UI thread */ - protected void processUpload(InputStream in, String fileName, String contentType) throws IOException { - - } - -} diff --git a/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/UiContext.java b/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/UiContext.java deleted file mode 100644 index 72e17a22d..000000000 --- a/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/UiContext.java +++ /dev/null @@ -1,59 +0,0 @@ -package org.argeo.eclipse.ui.specific; - -import java.util.Locale; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.eclipse.rap.rwt.RWT; -import org.eclipse.swt.widgets.Display; - -/** Singleton class providing single sources infos about the UI context. */ -public class UiContext { - /** Can be null, thus indicating that we are not in a web context. */ - public static HttpServletRequest getHttpRequest() { - return RWT.getRequest(); - } - - public static HttpServletResponse getHttpResponse() { - return RWT.getResponse(); - } - - public static Locale getLocale() { - if (Display.getCurrent() != null) - return RWT.getUISession().getLocale(); - else - return Locale.getDefault(); - } - - public static void setLocale(Locale locale) { - if (Display.getCurrent() != null) - RWT.getUISession().setLocale(locale); - else - Locale.setDefault(locale); - } - - /** Can always be null */ - @SuppressWarnings("unchecked") - public static T getData(String key) { - Display display = getDisplay(); - if (display == null) - return null; - return (T) display.getData(key); - } - - public static void setData(String key, Object value) { - Display display = getDisplay(); - if (display == null) - throw new IllegalStateException("Not display available"); - display.setData(key, value); - } - - private static Display getDisplay() { - return Display.getCurrent(); - } - - private UiContext() { - } - -} diff --git a/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/package-info.java b/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/package-info.java deleted file mode 100644 index 4ec451f8a..000000000 --- a/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Eclipse RAP-specific SWT/JFace utilities, to simplify single-sourcing. */ -package org.argeo.eclipse.ui.specific; \ No newline at end of file diff --git a/rcp/org.argeo.cms.e4.rcp/.classpath b/rcp/org.argeo.cms.e4.rcp/.classpath deleted file mode 100644 index eca7bdba8..000000000 --- a/rcp/org.argeo.cms.e4.rcp/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/rcp/org.argeo.cms.e4.rcp/.gitignore b/rcp/org.argeo.cms.e4.rcp/.gitignore deleted file mode 100644 index 710cd6893..000000000 --- a/rcp/org.argeo.cms.e4.rcp/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/bin/ -/target/ -/exec diff --git a/rcp/org.argeo.cms.e4.rcp/.project b/rcp/org.argeo.cms.e4.rcp/.project deleted file mode 100644 index 64d561913..000000000 --- a/rcp/org.argeo.cms.e4.rcp/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - org.argeo.cms.e4.rcp - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/rcp/org.argeo.cms.e4.rcp/.settings/org.eclipse.jdt.core.prefs b/rcp/org.argeo.cms.e4.rcp/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 0c68a61dc..000000000 --- a/rcp/org.argeo.cms.e4.rcp/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,7 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 -org.eclipse.jdt.core.compiler.compliance=1.8 -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.source=1.8 diff --git a/rcp/org.argeo.cms.e4.rcp/.settings/org.eclipse.pde.core.prefs b/rcp/org.argeo.cms.e4.rcp/.settings/org.eclipse.pde.core.prefs deleted file mode 100644 index f29e940a0..000000000 --- a/rcp/org.argeo.cms.e4.rcp/.settings/org.eclipse.pde.core.prefs +++ /dev/null @@ -1,3 +0,0 @@ -eclipse.preferences.version=1 -pluginProject.extensions=false -resolve.requirebundle=false diff --git a/rcp/org.argeo.cms.e4.rcp/META-INF/.gitignore b/rcp/org.argeo.cms.e4.rcp/META-INF/.gitignore deleted file mode 100644 index 4854a41b9..000000000 --- a/rcp/org.argeo.cms.e4.rcp/META-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/MANIFEST.MF diff --git a/rcp/org.argeo.cms.e4.rcp/argeo-companion.e4xmi b/rcp/org.argeo.cms.e4.rcp/argeo-companion.e4xmi deleted file mode 100644 index 5b250eebf..000000000 --- a/rcp/org.argeo.cms.e4.rcp/argeo-companion.e4xmi +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - editorArea - - - - - - - - - - - - - - diff --git a/rcp/org.argeo.cms.e4.rcp/argeo-companion.properties b/rcp/org.argeo.cms.e4.rcp/argeo-companion.properties deleted file mode 100644 index 0a0da7581..000000000 --- a/rcp/org.argeo.cms.e4.rcp/argeo-companion.properties +++ /dev/null @@ -1,23 +0,0 @@ -argeo.osgi.start.2.node=\ -org.eclipse.equinox.http.servlet,\ -org.eclipse.equinox.metatype,\ -org.eclipse.equinox.cm,\ -org.argeo.init - -argeo.osgi.start.3.node=\ -org.argeo.cms,\ -org.argeo.cms.jcr,\ - -applicationXMI=org.argeo.cms.e4.rcp/argeo-companion.e4xmi -lifeCycleURI=bundleclass://org.argeo.cms.e4.rcp/org.argeo.cms.e4.rcp.CmsRcpLifeCycle -clearPersistedState=true -#argeo.cms.desktop.inTray=true - -# Remote node: -#argeo.node.repo.labeledUri=http://root:demo@localhost:7070/jcr/node - -# Logging -log.org.argeo=DEBUG - -argeo.node.useradmin.uris=os:/// -eclipse.application=org.argeo.cms.e4.rcp.CmsE4Application diff --git a/rcp/org.argeo.cms.e4.rcp/bnd.bnd b/rcp/org.argeo.cms.e4.rcp/bnd.bnd deleted file mode 100644 index ff79c8041..000000000 --- a/rcp/org.argeo.cms.e4.rcp/bnd.bnd +++ /dev/null @@ -1,7 +0,0 @@ -Bundle-SymbolicName: org.argeo.cms.e4.rcp;singleton=true - -Require-Bundle: org.eclipse.core.runtime - -Import-Package: !org.eclipse.core.runtime,\ -org.eclipse.swt,\ -* diff --git a/rcp/org.argeo.cms.e4.rcp/build.properties b/rcp/org.argeo.cms.e4.rcp/build.properties deleted file mode 100644 index 355413e4f..000000000 --- a/rcp/org.argeo.cms.e4.rcp/build.properties +++ /dev/null @@ -1,5 +0,0 @@ -output.. = bin/ -bin.includes = META-INF/,\ - .,\ - argeo-companion.e4xmi -source.. = src/ diff --git a/rcp/org.argeo.cms.e4.rcp/log4j.properties b/rcp/org.argeo.cms.e4.rcp/log4j.properties deleted file mode 100644 index 13f949ff5..000000000 --- a/rcp/org.argeo.cms.e4.rcp/log4j.properties +++ /dev/null @@ -1,32 +0,0 @@ -log4j.rootLogger=WARN, development - -## Levels -log4j.logger.org.argeo=DEBUG -log4j.logger.org.argeo.jackrabbit.remote.ExtendedDispatcherServlet=WARN -log4j.logger.org.argeo.server.webextender.TomcatDeployer=INFO - -#log4j.logger.org.springframework.security=DEBUG -#log4j.logger.org.apache.commons.exec=DEBUG -#log4j.logger.org.apache.jackrabbit.webdav=DEBUG -#log4j.logger.org.apache.jackrabbit.remote=DEBUG -#log4j.logger.org.apache.jackrabbit.core.observation=DEBUG - -log4j.logger.org.apache.catalina=INFO -log4j.logger.org.apache.coyote=INFO - -log4j.logger.org.apache.directory=INFO -log4j.logger.org.apache.directory.server=ERROR -log4j.logger.org.apache.jackrabbit.core.query.lucene=ERROR - -## Appenders -# console is set to be a ConsoleAppender. -log4j.appender.console=org.apache.log4j.ConsoleAppender - -# console uses PatternLayout. -log4j.appender.console.layout=org.apache.log4j.PatternLayout -log4j.appender.console.layout.ConversionPattern= %-5p %d{ISO8601} %m - %c - [%t]%n - -# development appender (slow!) -log4j.appender.development=org.apache.log4j.ConsoleAppender -log4j.appender.development.layout=org.apache.log4j.PatternLayout -log4j.appender.development.layout.ConversionPattern=%d{HH:mm:ss} [%16.16t] %5p %m (%F:%L) %c%n diff --git a/rcp/org.argeo.cms.e4.rcp/plugin.xml b/rcp/org.argeo.cms.e4.rcp/plugin.xml deleted file mode 100644 index 3e6896beb..000000000 --- a/rcp/org.argeo.cms.e4.rcp/plugin.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - diff --git a/rcp/org.argeo.cms.e4.rcp/src/org/argeo/cms/e4/rcp/CmsE4Application.java b/rcp/org.argeo.cms.e4.rcp/src/org/argeo/cms/e4/rcp/CmsE4Application.java deleted file mode 100644 index b37a76587..000000000 --- a/rcp/org.argeo.cms.e4.rcp/src/org/argeo/cms/e4/rcp/CmsE4Application.java +++ /dev/null @@ -1,207 +0,0 @@ -package org.argeo.cms.e4.rcp; - -import java.security.PrivilegedExceptionAction; -import java.util.UUID; - -import javax.security.auth.Subject; -import javax.security.auth.login.LoginContext; -import javax.security.auth.login.LoginException; - -import org.argeo.api.cms.CmsAuth; -import org.argeo.api.cms.ux.CmsImageManager; -import org.argeo.api.cms.ux.CmsView; -import org.argeo.api.cms.ux.UxContext; -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.swt.SimpleSwtUxContext; -import org.argeo.cms.swt.auth.CmsLoginShell; -import org.eclipse.core.runtime.IConfigurationElement; -import org.eclipse.core.runtime.IExtension; -import org.eclipse.core.runtime.Platform; -import org.eclipse.equinox.app.IApplication; -import org.eclipse.equinox.app.IApplicationContext; -import org.eclipse.swt.widgets.Display; - -public class CmsE4Application implements IApplication, CmsView { - private LoginContext loginContext; - private IApplication e4Application; - private UxContext uxContext; - private String uid; - - @Override - public Object start(IApplicationContext context) throws Exception { - // TODO wait for CMS to be ready - Thread.sleep(5000); - - uid = UUID.randomUUID().toString(); - Subject subject = new Subject(); - Display display = createDisplay(); - CmsLoginShell loginShell = new CmsLoginShell(this, null); - // TODO customize CmsLoginShell to be smaller and centered - loginShell.setSubject(subject); - try { - // try pre-auth - loginContext = new LoginContext(CmsAuth.LOGIN_CONTEXT_SINGLE_USER, subject, loginShell); - loginContext.login(); - } catch (LoginException e) { - e.printStackTrace(); - loginShell.createUi(); - loginShell.open(); - - while (!loginShell.getShell().isDisposed()) { - if (!display.readAndDispatch()) - display.sleep(); - } - } - if (CurrentUser.getUsername(getSubject()) == null) - throw new IllegalStateException("Cannot log in"); - - // try { - // CallbackHandler callbackHandler = new DefaultLoginDialog( - // display.getActiveShell()); - // loginContext = new LoginContext( - // NodeConstants.LOGIN_CONTEXT_SINGLE_USER, subject, - // callbackHandler); - // } catch (LoginException e1) { - // throw new CmsException("Cannot initialize login context", e1); - // } - // - // // login - // try { - // loginContext.login(); - // subject = loginContext.getSubject(); - // } catch (LoginException e) { - // e.printStackTrace(); - // display.dispose(); - // try { - // Thread.sleep(2000); - // } catch (InterruptedException e1) { - // // silent - // } - // return null; - // } - - uxContext = new SimpleSwtUxContext(); - // UiContext.setData(CmsView.KEY, this); - CmsSwtUtils.registerCmsView(loginShell.getShell(), this); - e4Application = getApplication(null); - Object res = Subject.doAs(subject, new PrivilegedExceptionAction() { - - @Override - public Object run() throws Exception { - return e4Application.start(context); - } - - }); - return res; - } - - @Override - public void stop() { - if (e4Application != null) - e4Application.stop(); - } - - static IApplication getApplication(String[] args) { - IExtension extension = Platform.getExtensionRegistry().getExtension(Platform.PI_RUNTIME, - Platform.PT_APPLICATIONS, "org.eclipse.e4.ui.workbench.swt.E4Application"); - try { - IConfigurationElement[] elements = extension.getConfigurationElements(); - if (elements.length > 0) { - IConfigurationElement[] runs = elements[0].getChildren("run"); - if (runs.length > 0) { - Object runnable; - runnable = runs[0].createExecutableExtension("class"); - if (runnable instanceof IApplication) - return (IApplication) runnable; - } - } - } catch (Exception e) { - throw new IllegalStateException("Cannot find e4 application", e); - } - throw new IllegalStateException("Cannot find e4 application"); - } - - public static Display createDisplay() { - Display.setAppName("Argeo CMS RCP"); - - // create the display - Display newDisplay = Display.getCurrent(); - if (newDisplay == null) { - newDisplay = new Display(); - } - // Set the priority higher than normal so as to be higher - // than the JobManager. - Thread.currentThread().setPriority(Math.min(Thread.MAX_PRIORITY, Thread.NORM_PRIORITY + 1)); - return newDisplay; - } - - // - // CMS VIEW - // - - @Override - public UxContext getUxContext() { - return uxContext; - } - - @Override - public void navigateTo(String state) { - // TODO Auto-generated method stub - - } - - @Override - public void authChange(LoginContext loginContext) { - if (loginContext == null) - throw new CmsException("Login context cannot be null"); - // logout previous login context - // if (this.loginContext != null) - // try { - // this.loginContext.logout(); - // } catch (LoginException e1) { - // System.err.println("Could not log out: " + e1); - // } - this.loginContext = loginContext; - } - - @Override - public void logout() { - if (loginContext == null) - throw new CmsException("Login context should not bet null"); - try { - CurrentUser.logoutCmsSession(loginContext.getSubject()); - loginContext.logout(); - } catch (LoginException e) { - throw new CmsException("Cannot log out", e); - } - } - - @Override - public void exception(Throwable e) { - // TODO Auto-generated method stub - - } - - @Override - public CmsImageManager getImageManager() { - // TODO Auto-generated method stub - return null; - } - - protected Subject getSubject() { - return loginContext.getSubject(); - } - - @Override - public boolean isAnonymous() { - return CurrentUser.isAnonymous(getSubject()); - } - - @Override - public String getUid() { - return uid; - } - -} diff --git a/rcp/org.argeo.cms.e4.rcp/src/org/argeo/cms/e4/rcp/CmsRcpLifeCycle.java b/rcp/org.argeo.cms.e4.rcp/src/org/argeo/cms/e4/rcp/CmsRcpLifeCycle.java deleted file mode 100644 index 1d38fe718..000000000 --- a/rcp/org.argeo.cms.e4.rcp/src/org/argeo/cms/e4/rcp/CmsRcpLifeCycle.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.argeo.cms.e4.rcp; - -import org.eclipse.e4.core.contexts.IEclipseContext; -import org.eclipse.e4.ui.workbench.lifecycle.PostContextCreate; -import org.eclipse.e4.ui.workbench.lifecycle.PreSave; -import org.eclipse.e4.ui.workbench.lifecycle.ProcessAdditions; -import org.eclipse.e4.ui.workbench.lifecycle.ProcessRemovals; - -@SuppressWarnings("restriction") -public class CmsRcpLifeCycle { - - @PostContextCreate - void postContextCreate(IEclipseContext workbenchContext) { - } - - @PreSave - void preSave(IEclipseContext workbenchContext) { - } - - @ProcessAdditions - void processAdditions(IEclipseContext workbenchContext) { - } - - @ProcessRemovals - void processRemovals(IEclipseContext workbenchContext) { - } -} diff --git a/rcp/org.argeo.cms.ui.rcp/.classpath b/rcp/org.argeo.cms.ui.rcp/.classpath deleted file mode 100644 index e801ebfb4..000000000 --- a/rcp/org.argeo.cms.ui.rcp/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/rcp/org.argeo.cms.ui.rcp/.gitignore b/rcp/org.argeo.cms.ui.rcp/.gitignore deleted file mode 100644 index 09e3bc9b2..000000000 --- a/rcp/org.argeo.cms.ui.rcp/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/bin/ -/target/ diff --git a/rcp/org.argeo.cms.ui.rcp/.project b/rcp/org.argeo.cms.ui.rcp/.project deleted file mode 100644 index c9c2a44bf..000000000 --- a/rcp/org.argeo.cms.ui.rcp/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.argeo.cms.ui.rcp - - - - - - 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/rcp/org.argeo.cms.ui.rcp/META-INF/.gitignore b/rcp/org.argeo.cms.ui.rcp/META-INF/.gitignore deleted file mode 100644 index 4854a41b9..000000000 --- a/rcp/org.argeo.cms.ui.rcp/META-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/MANIFEST.MF diff --git a/rcp/org.argeo.cms.ui.rcp/OSGI-INF/cmsRcpDisplayFactory.xml b/rcp/org.argeo.cms.ui.rcp/OSGI-INF/cmsRcpDisplayFactory.xml deleted file mode 100644 index a0c0f0f5c..000000000 --- a/rcp/org.argeo.cms.ui.rcp/OSGI-INF/cmsRcpDisplayFactory.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/rcp/org.argeo.cms.ui.rcp/OSGI-INF/cmsRcpServletFactory.xml b/rcp/org.argeo.cms.ui.rcp/OSGI-INF/cmsRcpServletFactory.xml deleted file mode 100644 index a1f0b3a2a..000000000 --- a/rcp/org.argeo.cms.ui.rcp/OSGI-INF/cmsRcpServletFactory.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/rcp/org.argeo.cms.ui.rcp/bnd.bnd b/rcp/org.argeo.cms.ui.rcp/bnd.bnd deleted file mode 100644 index 91f0a8a37..000000000 --- a/rcp/org.argeo.cms.ui.rcp/bnd.bnd +++ /dev/null @@ -1,12 +0,0 @@ - -Import-Package:\ -org.argeo.cms.auth,\ -org.eclipse.swt,\ -org.eclipse.swt.graphics,\ -org.w3c.css.sac,\ -* - -Service-Component:\ -OSGI-INF/cmsRcpDisplayFactory.xml,\ -OSGI-INF/cmsRcpServletFactory.xml - diff --git a/rcp/org.argeo.cms.ui.rcp/build.properties b/rcp/org.argeo.cms.ui.rcp/build.properties deleted file mode 100644 index 5eef7058e..000000000 --- a/rcp/org.argeo.cms.ui.rcp/build.properties +++ /dev/null @@ -1,6 +0,0 @@ -output.. = bin/ -bin.includes = META-INF/,\ - .,\ - OSGI-INF/,\ - OSGI-INF/cmsRcpServletFactory.xml -source.. = src/ diff --git a/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/CmsRcpApp.java b/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/CmsRcpApp.java deleted file mode 100644 index e25a9f711..000000000 --- a/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/CmsRcpApp.java +++ /dev/null @@ -1,230 +0,0 @@ -package org.argeo.cms.ui.rcp; - -import java.io.IOException; -import java.io.InputStream; -import java.security.PrivilegedAction; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -import javax.security.auth.Subject; -import javax.security.auth.login.LoginContext; -import javax.security.auth.login.LoginException; - -import org.argeo.api.cms.CmsApp; -import org.argeo.api.cms.CmsAuth; -import org.argeo.api.cms.CmsLog; -import org.argeo.api.cms.CmsSession; -import org.argeo.api.cms.ux.CmsImageManager; -import org.argeo.api.cms.ux.CmsTheme; -import org.argeo.api.cms.ux.CmsUi; -import org.argeo.api.cms.ux.CmsView; -import org.argeo.api.cms.ux.UxContext; -import org.argeo.cms.swt.CmsSwtUtils; -import org.eclipse.e4.ui.css.core.engine.CSSEngine; -import org.eclipse.e4.ui.css.core.engine.CSSErrorHandler; -import org.eclipse.e4.ui.css.swt.engine.CSSSWTEngineImpl; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Shell; -import org.osgi.service.event.Event; -import org.osgi.service.event.EventAdmin; - -/** Runs a {@link CmsApp} as an SWT desktop application. */ -@SuppressWarnings("restriction") -public class CmsRcpApp implements CmsView { - private final static CmsLog log = CmsLog.getLog(CmsRcpApp.class); - - // private BundleContext bundleContext = - // FrameworkUtil.getBundle(CmsRcpApp.class).getBundleContext(); - - private Shell shell; - private CmsApp cmsApp; - - // CMS View - private String uid; - private LoginContext loginContext; - - private EventAdmin eventAdmin; - - private CSSEngine cssEngine; - - private CmsUi ui; - // TODO make it configurable - private String uiName = "desktop"; - - public CmsRcpApp(String uiName) { - uid = UUID.randomUUID().toString(); - this.uiName = uiName; - } - - public void initRcpApp() { - Display display = Display.getCurrent(); - shell = new Shell(display); - shell.setText("Argeo CMS"); - Composite parent = shell; - parent.setLayout(CmsSwtUtils.noSpaceGridLayout()); - CmsSwtUtils.registerCmsView(shell, CmsRcpApp.this); - - try { - loginContext = new LoginContext(CmsAuth.SINGLE_USER.getLoginContextName()); - loginContext.login(); - } catch (LoginException e) { - throw new IllegalStateException("Could not log in.", e); - } - if (log.isDebugEnabled()) - log.debug("Logged in to desktop: " + loginContext.getSubject()); - - Subject.doAs(loginContext.getSubject(), (PrivilegedAction) () -> { - - // TODO factorise with web app - parent.setData(CmsApp.UI_NAME_PROPERTY, uiName); - ui = cmsApp.initUi(parent); - if (ui instanceof Composite) - ((Composite) ui).setLayoutData(CmsSwtUtils.fillAll()); - // we need ui to be set before refresh so that CmsView can store UI context data - // in it. - cmsApp.refreshUi(ui, null); - - // Styling - CmsTheme theme = CmsSwtUtils.getCmsTheme(parent); - if (theme != null) { - cssEngine = new CSSSWTEngineImpl(display); - for (String path : theme.getSwtCssPaths()) { - try (InputStream in = theme.loadPath(path)) { - cssEngine.parseStyleSheet(in); - } catch (IOException e) { - throw new IllegalStateException("Cannot load stylesheet " + path, e); - } - } - cssEngine.setErrorHandler(new CSSErrorHandler() { - public void error(Exception e) { - log.error("SWT styling error: ", e); - } - }); - applyStyles(shell); - } - shell.layout(true, true); - - shell.open(); - return null; - }); - } - - /* - * CMS VIEW - */ - - @Override - public String getUid() { - return uid; - } - - @Override - public UxContext getUxContext() { - throw new UnsupportedOperationException(); - } - - @Override - public void navigateTo(String state) { - throw new UnsupportedOperationException(); - } - - @Override - public void authChange(LoginContext loginContext) { - } - - @Override - public void logout() { - if (loginContext != null) - try { - loginContext.logout(); - } catch (LoginException e) { - log.error("Cannot log out", e); - } - } - - @Override - public void exception(Throwable e) { - log.error("Unexpected exception in CMS RCP", e); - } - - @Override - public CmsImageManager getImageManager() { - throw new UnsupportedOperationException(); - } - - @Override - public CmsSession getCmsSession() { - CmsSession cmsSession = cmsApp.getCmsContext().getCmsSession(getSubject()); - return cmsSession; - } - - @Override - public Object getData(String key) { - if (ui != null) { - return ui.getData(key); - } else { - throw new IllegalStateException("UI is not initialized"); - } - } - - @Override - public void setData(String key, Object value) { - if (ui != null) { - ui.setData(key, value); - } else { - throw new IllegalStateException("UI is not initialized"); - } - } - - @Override - public boolean isAnonymous() { - return false; - } - - @Override - public void applyStyles(Object node) { - if (cssEngine != null) - cssEngine.applyStyles(node, true); - } - - @Override - public void sendEvent(String topic, Map properties) { - if (properties == null) - properties = new HashMap<>(); - if (properties.containsKey(CMS_VIEW_UID_PROPERTY) && !properties.get(CMS_VIEW_UID_PROPERTY).equals(uid)) - throw new IllegalArgumentException("Property " + CMS_VIEW_UID_PROPERTY + " is set to another CMS view uid (" - + properties.get(CMS_VIEW_UID_PROPERTY) + ") then " + uid); - properties.put(CMS_VIEW_UID_PROPERTY, uid); - eventAdmin.sendEvent(new Event(topic, properties)); - } - - public T doAs(PrivilegedAction action) { - return Subject.doAs(getSubject(), action); - } - - protected Subject getSubject() { - return loginContext.getSubject(); - } - - public Shell getShell() { - return shell; - } - - /* - * DEPENDENCY INJECTION - */ - public void setCmsApp(CmsApp cmsApp, Map properties) { - this.cmsApp = cmsApp; - } - - public void unsetCmsApp(CmsApp cmsApp, Map properties) { - this.cmsApp = null; - } - - public void setEventAdmin(EventAdmin eventAdmin) { - this.eventAdmin = eventAdmin; - } - -} diff --git a/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/CmsRcpDisplayFactory.java b/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/CmsRcpDisplayFactory.java deleted file mode 100644 index ec471c021..000000000 --- a/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/CmsRcpDisplayFactory.java +++ /dev/null @@ -1,90 +0,0 @@ -package org.argeo.cms.ui.rcp; - -import java.nio.file.Path; - -import org.argeo.api.cms.CmsApp; -import org.argeo.util.OS; -import org.eclipse.swt.widgets.Display; -import org.osgi.service.event.EventAdmin; -import org.eclipse.swt.events.DisposeListener; - -/** Creates the SWT {@link Display} in a dedicated thread. */ -public class CmsRcpDisplayFactory { - /** File name in a run directory */ - private final static String ARGEO_RCP_URL = "argeo.rcp.url"; - - /** There is only one display in RCP mode */ - private static Display display; - - private CmsUiThread uiThread; - - private boolean shutdown = false; - - public void init() { - uiThread = new CmsUiThread(); - uiThread.start(); - while (display == null) - try { - Thread.sleep(100); - } catch (InterruptedException e) { - // silent - } - } - - public void destroy() { - shutdown = true; - display.wake(); - try { - uiThread.join(); - } catch (InterruptedException e) { - // silent - } finally { - uiThread = null; - } - } - - class CmsUiThread extends Thread { - - public CmsUiThread() { - super("CMS UI"); - } - - @Override - public void run() { - display = Display.getDefault(); - display.setRuntimeExceptionHandler((e) -> e.printStackTrace()); - display.setErrorHandler((e) -> e.printStackTrace()); - -// for (String contextName : cmsApps.keySet()) { -// openCmsApp(contextName); -// } - - while (!shutdown) { - if (!display.readAndDispatch()) - display.sleep(); - } - display.dispose(); - display = null; - } - } - - public static Display getDisplay() { - return display; - } - - public static void openCmsApp(EventAdmin eventAdmin, CmsApp cmsApp, String uiName, - DisposeListener disposeListener) { - CmsRcpDisplayFactory.getDisplay().syncExec(() -> { - CmsRcpApp cmsRcpApp = new CmsRcpApp(uiName); - cmsRcpApp.setEventAdmin(eventAdmin); - cmsRcpApp.setCmsApp(cmsApp, null); - cmsRcpApp.initRcpApp(); - if (disposeListener != null) - cmsRcpApp.getShell().addDisposeListener(disposeListener); - }); - } - - public static Path getUrlRunFile() { - return OS.getRunDir().resolve(CmsRcpDisplayFactory.ARGEO_RCP_URL); - } -} diff --git a/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServlet.java b/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServlet.java deleted file mode 100644 index f8aecd39b..000000000 --- a/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServlet.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.argeo.cms.ui.rcp.servlet; - -import java.io.IOException; -import java.lang.System.Logger; -import java.lang.System.Logger.Level; -import java.util.Objects; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.argeo.api.cms.CmsApp; -import org.argeo.cms.ui.rcp.CmsRcpDisplayFactory; -import org.osgi.service.event.EventAdmin; - -/** Open the related app when called. */ -public class CmsRcpServlet extends HttpServlet { - private static final long serialVersionUID = -3944472431354848923L; - private final static Logger logger = System.getLogger(CmsRcpServlet.class.getName()); - - private CmsApp cmsApp; - private EventAdmin eventAdmin; - - public CmsRcpServlet(EventAdmin eventAdmin, CmsApp cmsApp) { - Objects.requireNonNull(eventAdmin); - Objects.requireNonNull(cmsApp); - this.cmsApp = cmsApp; - this.eventAdmin = eventAdmin; - } - - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - String path = req.getPathInfo(); - String uiName = path != null ? path.substring(path.lastIndexOf('/') + 1) : ""; - CmsRcpDisplayFactory.openCmsApp(eventAdmin, cmsApp, uiName, null); - logger.log(Level.DEBUG, "Opened RCP UI " + uiName + " of CMS App " + req.getServletPath()); - } - -} diff --git a/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServletFactory.java b/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServletFactory.java deleted file mode 100644 index 7c24f87d9..000000000 --- a/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServletFactory.java +++ /dev/null @@ -1,131 +0,0 @@ -package org.argeo.cms.ui.rcp.servlet; - -import java.io.IOException; -import java.lang.System.Logger; -import java.lang.System.Logger.Level; -import java.net.DatagramSocket; -import java.net.ServerSocket; -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Collections; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.Map; -import java.util.concurrent.CompletableFuture; - -import javax.servlet.Servlet; - -import org.argeo.api.cms.CmsApp; -import org.argeo.cms.ui.rcp.CmsRcpDisplayFactory; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.ServiceRegistration; -import org.osgi.service.event.EventAdmin; -import org.osgi.service.http.HttpService; - -/** Publishes one {@link CmsRcpServlet} per {@link CmsApp}. */ -public class CmsRcpServletFactory { - private final static Logger logger = System.getLogger(CmsRcpServletFactory.class.getName()); - - private BundleContext bundleContext = FrameworkUtil.getBundle(CmsRcpServletFactory.class).getBundleContext(); - - private CompletableFuture eventAdmin = new CompletableFuture<>(); - - private Map> registrations = Collections.synchronizedMap(new HashMap<>()); - - public void init() { - - } - - public void destroy() { - Path runFile = CmsRcpDisplayFactory.getUrlRunFile(); - try { - if (Files.exists(runFile)) { - Files.delete(runFile); - } - } catch (IOException e) { - logger.log(Level.ERROR, "Cannot delete " + runFile, e); - } - } - - public void addCmsApp(CmsApp cmsApp, Map properties) { - String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY); - if (contextName != null) { - eventAdmin.thenAccept((eventAdmin) -> { - CmsRcpServlet servlet = new CmsRcpServlet(eventAdmin, cmsApp); - Hashtable serviceProperties = new Hashtable<>(); - serviceProperties.put("osgi.http.whiteboard.servlet.pattern", "/" + contextName + "/*"); - ServiceRegistration sr = bundleContext.registerService(Servlet.class, servlet, - serviceProperties); - registrations.put(contextName, sr); - }); - } - } - - public void removeCmsApp(CmsApp cmsApp, Map properties) { - String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY); - if (contextName != null) { - ServiceRegistration sr = registrations.get(contextName); - sr.unregister(); - } - } - - public void setEventAdmin(EventAdmin eventAdmin) { - this.eventAdmin.complete(eventAdmin); - } - - public void setHttpService(HttpService httpService, Map properties) { - Integer httpPort = Integer.parseInt(properties.get("http.port").toString()); - String baseUrl = "http://localhost:" + httpPort + "/"; - Path runFile = CmsRcpDisplayFactory.getUrlRunFile(); - try { - if (!Files.exists(runFile)) { - Files.createDirectories(runFile.getParent()); - // TODO give read permission only to the owner - Files.createFile(runFile); - } else { - URI uri = URI.create(Files.readString(runFile)); - if (!httpPort.equals(uri.getPort())) - if (!isPortAvailable(uri.getPort())) { - throw new IllegalStateException("Another CMS is running on " + runFile); - } else { - logger.log(Level.WARNING, - "Run file " + runFile + " found but port of " + uri + " is available. Overwriting..."); - } - } - Files.writeString(runFile, baseUrl, StandardCharsets.UTF_8); - } catch (IOException e) { - throw new RuntimeException("Cannot write run file to " + runFile, e); - } - logger.log(Level.DEBUG, "RCP available under " + baseUrl + ", written to " + runFile); - } - - protected boolean isPortAvailable(int port) { - ServerSocket ss = null; - DatagramSocket ds = null; - try { - ss = new ServerSocket(port); - ss.setReuseAddress(true); - ds = new DatagramSocket(port); - ds.setReuseAddress(true); - return true; - } catch (IOException e) { - } finally { - if (ds != null) { - ds.close(); - } - - if (ss != null) { - try { - ss.close(); - } catch (IOException e) { - /* should not be thrown */ - } - } - } - - return false; - } -} diff --git a/rcp/org.argeo.swt.specific.rcp/.classpath b/rcp/org.argeo.swt.specific.rcp/.classpath deleted file mode 100644 index 457b11571..000000000 --- a/rcp/org.argeo.swt.specific.rcp/.classpath +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - diff --git a/rcp/org.argeo.swt.specific.rcp/.gitignore b/rcp/org.argeo.swt.specific.rcp/.gitignore deleted file mode 100644 index 5e77890b2..000000000 --- a/rcp/org.argeo.swt.specific.rcp/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/target/ -/bin/ -*.log \ No newline at end of file diff --git a/rcp/org.argeo.swt.specific.rcp/.project b/rcp/org.argeo.swt.specific.rcp/.project deleted file mode 100644 index c79ee3fc9..000000000 --- a/rcp/org.argeo.swt.specific.rcp/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - org.argeo.swt.specific.rcp - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/rcp/org.argeo.swt.specific.rcp/META-INF/.gitignore b/rcp/org.argeo.swt.specific.rcp/META-INF/.gitignore deleted file mode 100644 index 4854a41b9..000000000 --- a/rcp/org.argeo.swt.specific.rcp/META-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/MANIFEST.MF diff --git a/rcp/org.argeo.swt.specific.rcp/bnd.bnd b/rcp/org.argeo.swt.specific.rcp/bnd.bnd deleted file mode 100644 index bb88efda7..000000000 --- a/rcp/org.argeo.swt.specific.rcp/bnd.bnd +++ /dev/null @@ -1,20 +0,0 @@ -Import-Package: \ -!java.*,\ -org.apache.commons.io,\ -org.eclipse.core.commands,\ -!org.eclipse.core.runtime,\ -!org.eclipse.ui.plugin,\ -org.eclipse.swt,\ -javax.servlet.http;version="[3,5)",\ -javax.servlet;version="[3,5)",\ -* - -Export-Package: org.argeo.*,\ -org.eclipse.rap.fileupload.*;version="3.10",\ -org.eclipse.rap.rwt.*;version="3.10" - -# Was !org.eclipse.core.commands,\ why ? - -#Bundle-Activator: org.argeo.eclipse.ui.ArgeoUiPlugin -#Bundle-ActivationPolicy: lazy -#Ignore-Package: org.eclipse.core.commands \ No newline at end of file diff --git a/rcp/org.argeo.swt.specific.rcp/build.properties b/rcp/org.argeo.swt.specific.rcp/build.properties deleted file mode 100644 index c6b651a59..000000000 --- a/rcp/org.argeo.swt.specific.rcp/build.properties +++ /dev/null @@ -1,3 +0,0 @@ -source.. = src/ -output.. = bin/ -bin.includes = META-INF/ diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/rcp/internal/rwt/RcpClient.java b/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/rcp/internal/rwt/RcpClient.java deleted file mode 100644 index 0d9ce481d..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/rcp/internal/rwt/RcpClient.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.argeo.eclipse.ui.rcp.internal.rwt; - -import org.eclipse.rap.rwt.client.Client; -import org.eclipse.rap.rwt.client.service.BrowserNavigation; -import org.eclipse.rap.rwt.client.service.BrowserNavigationListener; -import org.eclipse.rap.rwt.client.service.ClientService; -import org.eclipse.rap.rwt.client.service.JavaScriptExecutor; - -public class RcpClient implements Client { - - @Override - public T getService(Class type) { - if (type.isAssignableFrom(JavaScriptExecutor.class)) - return (T) javaScriptExecutor; - else if (type.isAssignableFrom(BrowserNavigation.class)) - return (T) browserNavigation; - else - return null; - } - - private JavaScriptExecutor javaScriptExecutor = new JavaScriptExecutor() { - - @Override - public void execute(String code) { - // TODO Auto-generated method stub - - } - }; - private BrowserNavigation browserNavigation = new BrowserNavigation() { - - @Override - public void pushState(String state, String title) { - // TODO Auto-generated method stub - - } - - @Override - public void addBrowserNavigationListener( - BrowserNavigationListener listener) { - // TODO Auto-generated method stub - - } - }; -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/rcp/internal/rwt/RcpResourceManager.java b/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/rcp/internal/rwt/RcpResourceManager.java deleted file mode 100644 index 91109a9de..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/rcp/internal/rwt/RcpResourceManager.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.argeo.eclipse.ui.rcp.internal.rwt; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Collections; -import java.util.Map; -import java.util.TreeMap; - -import org.apache.commons.io.IOUtils; -import org.eclipse.rap.rwt.service.ResourceManager; - -public class RcpResourceManager implements ResourceManager { - private Map register = Collections - .synchronizedMap(new TreeMap()); - - @Override - public void register(String name, InputStream in) { - try { - register.put(name, IOUtils.toByteArray(in)); - } catch (IOException e) { - throw new RuntimeException("Cannot register " + name, e); - } - } - - @Override - public boolean unregister(String name) { - return register.remove(name) != null; - } - - @Override - public InputStream getRegisteredContent(String name) { - return new ByteArrayInputStream(register.get(name)); - } - - @Override - public String getLocation(String name) { - return name; - } - - @Override - public boolean isRegistered(String name) { - return register.containsKey(name); - } - -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/BufferedImageDisplay.java b/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/BufferedImageDisplay.java deleted file mode 100644 index 7fd2db1d1..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/BufferedImageDisplay.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.argeo.eclipse.ui.specific; - -import java.awt.BorderLayout; -import java.awt.Frame; -import java.awt.Graphics; -import java.awt.image.BufferedImage; - -import javax.swing.JPanel; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.awt.SWT_AWT; -import org.eclipse.swt.widgets.Composite; - -public class BufferedImageDisplay extends Composite { - private BufferedImage image; - - public BufferedImageDisplay(Composite parent, int style) { - super(parent, SWT.EMBEDDED | SWT.NO_BACKGROUND); - Frame frame = SWT_AWT.new_Frame(this); - frame.setLayout(new BorderLayout()); - frame.add(new JPanel() { - private static final long serialVersionUID = 8924410573598922364L; - - public void paintComponent(Graphics g) { - super.paintComponent(g); - if (image != null) - g.drawImage(image, 0, 0, this); - } - - }, BorderLayout.CENTER); - frame.setVisible(true); - } - - public void setImage(BufferedImage image) { - this.image = image; - } - -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/CmsFileDialog.java b/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/CmsFileDialog.java deleted file mode 100644 index 0c5d34699..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/CmsFileDialog.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.argeo.eclipse.ui.specific; - -import org.eclipse.swt.widgets.FileDialog; -import org.eclipse.swt.widgets.Shell; - -public class CmsFileDialog extends FileDialog { - public CmsFileDialog(Shell parent, int style) { - super(parent, style); - } - - public CmsFileDialog(Shell parent) { - super(parent); - } - -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/CmsFileUpload.java b/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/CmsFileUpload.java deleted file mode 100644 index 638859a85..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/CmsFileUpload.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.argeo.eclipse.ui.specific; - -import org.eclipse.rap.rwt.widgets.FileUpload; -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.widgets.Composite; - -public class CmsFileUpload extends FileUpload { - public CmsFileUpload(Composite parent, int style) { - super(parent, style); - } - - @Override - public void setText(String text) { - super.setText(text); - } - - @Override - public String getFileName() { - return super.getFileName(); - } - - @Override - public String[] getFileNames() { - return super.getFileNames(); - } - - @Override - public void addSelectionListener(SelectionListener listener) { - super.addSelectionListener(listener); - } - -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/DefaultNLS.java b/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/DefaultNLS.java deleted file mode 100644 index fbb4fbf83..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/DefaultNLS.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.argeo.eclipse.ui.specific; - -/** RCP specific {@link NLS} to be extended */ -public class DefaultNLS {// extends NLS { -// public final static String DEFAULT_BUNDLE_LOCATION = "/properties/plugin"; -// -// public DefaultNLS() { -// this(DEFAULT_BUNDLE_LOCATION); -// } -// -// public DefaultNLS(String bundleName) { -// NLS.initializeMessages(bundleName, getClass()); -// } -} \ No newline at end of file diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/EclipseUiConstants.java b/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/EclipseUiConstants.java deleted file mode 100644 index ac862d794..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/EclipseUiConstants.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.argeo.eclipse.ui.specific; - -/** Constants which are specific to RWT.*/ -public interface EclipseUiConstants { - final static String CSS_CLASS = "org.eclipse.e4.ui.css.CssClassName"; - final static String MARKUP_SUPPORT = null; -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java b/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java deleted file mode 100644 index d1acbcfc0..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.argeo.eclipse.ui.specific; - -import org.eclipse.jface.viewers.ColumnViewer; -import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.swt.widgets.Widget; - -/** Static utilities to bridge differences between RCP and RAP */ -public class EclipseUiSpecificUtils { - private final static String CSS_CLASS = "org.eclipse.e4.ui.css.CssClassName"; - - public static void setStyleData(Widget widget, Object data) { - widget.setData(CSS_CLASS, data); - } - - public static Object getStyleData(Widget widget) { - return widget.getData(CSS_CLASS); - } - - public static void setMarkupData(Widget widget) { - // does nothing - } - - public static void setMarkupValidationDisabledData(Widget widget) { - // does nothing - } - - /** - * TootlTip support is supported for {@link ColumnViewer} in RCP - * - * @see ColumnViewerToolTipSupport#enableFor(Viewer) - */ - public static void enableToolTipSupport(Viewer viewer) { - if (viewer instanceof ColumnViewer) - ColumnViewerToolTipSupport.enableFor((ColumnViewer) viewer); - } - - private EclipseUiSpecificUtils() { - } -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/FileDropAdapter.java b/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/FileDropAdapter.java deleted file mode 100644 index 524447ed0..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/FileDropAdapter.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.argeo.eclipse.ui.specific; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Arrays; - -import org.eclipse.swt.dnd.DND; -import org.eclipse.swt.dnd.DropTarget; -import org.eclipse.swt.dnd.DropTargetAdapter; -import org.eclipse.swt.dnd.DropTargetEvent; -import org.eclipse.swt.dnd.FileTransfer; -import org.eclipse.swt.dnd.Transfer; -import org.eclipse.swt.widgets.Control; - -public class FileDropAdapter { - - public void prepareDropTarget(Control control, DropTarget dropTarget) { - dropTarget.setTransfer(new Transfer[] { FileTransfer.getInstance() }); - dropTarget.addDropListener(new DropTargetAdapter() { - @Override - public void dropAccept(DropTargetEvent event) { - if (!FileTransfer.getInstance().isSupportedType(event.currentDataType)) { - event.detail = DND.DROP_NONE; - } - } - - @Override - public void drop(DropTargetEvent event) { - handleFileDrop(control, event); - } - }); - } - - public void handleFileDrop(Control control, DropTargetEvent event) { - String fileList[] = null; - FileTransfer ft = FileTransfer.getInstance(); - if (ft.isSupportedType(event.currentDataType)) { - fileList = (String[]) event.data; - } - System.out.println(Arrays.toString(fileList)); - } - - /** Executed in UI thread */ - protected void processUpload(InputStream in, String fileName, String contentType) throws IOException { - - } - -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/UiContext.java b/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/UiContext.java deleted file mode 100644 index 20163cffa..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/UiContext.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.argeo.eclipse.ui.specific; - -import java.util.Locale; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.eclipse.swt.widgets.Display; - -/** Singleton class providing single sources infos about the UI context. */ -public class UiContext { - - public static HttpServletRequest getHttpRequest() { - return null; - } - - public static HttpServletResponse getHttpResponse() { - return null; - } - - public static Locale getLocale() { - return Locale.getDefault(); - } - - public static void setLocale(Locale locale) { - Locale.setDefault(locale); - } - - /** Can always be null */ - @SuppressWarnings("unchecked") - public static T getData(String key) { - Display display = getDisplay(); - if (display == null) - return null; - return (T) display.getData(key); - } - - public static void setData(String key, Object value) { - Display display = getDisplay(); - if (display == null) - throw new IllegalStateException("Not display available"); - display.setData(key, value); - } - - private static Display getDisplay() { - return Display.getCurrent(); - } - - private UiContext() { - } - -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileDetails.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileDetails.java deleted file mode 100644 index fbb36ddd4..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileDetails.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.eclipse.rap.fileupload; - -public interface FileDetails { - String getContentType(); - - long getContentLength(); - - String getFileName(); -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadEvent.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadEvent.java deleted file mode 100644 index a7452806a..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadEvent.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.eclipse.rap.fileupload; - -import java.util.EventObject; - -public abstract class FileUploadEvent extends EventObject { - - private static final long serialVersionUID = 1L; - - protected FileUploadEvent(FileUploadHandler source) { - super(source); - } - - public abstract FileDetails[] getFileDetails(); - - public abstract long getContentLength(); - - public abstract long getBytesRead(); - - public abstract Exception getException(); - -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadHandler.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadHandler.java deleted file mode 100644 index 7d89300f3..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadHandler.java +++ /dev/null @@ -1,38 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2011, 2012 EclipseSource and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * EclipseSource - initial API and implementation - ******************************************************************************/ -package org.eclipse.rap.fileupload; - -/** - * A file upload handler is used to accept file uploads from a client. After - * creating a file upload handler, the server will accept file uploads to the - * URL returned by getUploadUrl(). Upload listeners can be attached - * to react on progress. When the upload has finished, a FileUploadHandler has - * to be disposed of by calling its dispose() method. - * - * @noextend This class is not intended to be subclassed by clients. - */ -public class FileUploadHandler { - - public FileUploadHandler(FileUploadReceiver fileUploadReceiver) { - } - - public void dispose() { - - } - - public void addUploadListener(FileUploadListener listener) { - - } - - public String getUploadUrl() { - return null; - } -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadListener.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadListener.java deleted file mode 100644 index b59fd39ea..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadListener.java +++ /dev/null @@ -1,51 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2011, 2012 EclipseSource and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * EclipseSource - initial API and implementation - ******************************************************************************/ -package org.eclipse.rap.fileupload; - -import org.eclipse.swt.widgets.Display; - - -/** - * Listener to react on progress and completion of a file upload. - *

- * Note: This listener will be called from a different thread than the UI thread. - * Implementations must use {@link Display#asyncExec(Runnable)} to access the UI. - *

- * - * @see FileUploadEvent - */ -public interface FileUploadListener { - - /** - * Called when new information about an in-progress upload is available. - * - * @param event event object that contains information about the uploaded file - * @see FileUploadEvent#getBytesRead() - */ - void uploadProgress( FileUploadEvent event ); - - /** - * Called when a file upload has finished successfully. - * - * @param event event object that contains information about the uploaded file - * @see FileUploadEvent - */ - void uploadFinished( FileUploadEvent event ); - - /** - * Called when a file upload failed. - * - * @param event event object that contains information about the uploaded file - * @see FileUploadEvent#getErrorMessage() - */ - void uploadFailed( FileUploadEvent event ); - -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadReceiver.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadReceiver.java deleted file mode 100644 index 3f4cf47c4..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadReceiver.java +++ /dev/null @@ -1,32 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2011, 2013 EclipseSource and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * EclipseSource - initial API and implementation - ******************************************************************************/ -package org.eclipse.rap.fileupload; - -import java.io.IOException; -import java.io.InputStream; - - -/** - * Instances of this interface are responsible for reading and processing the data from a file - * upload. - */ -public abstract class FileUploadReceiver { - - /** - * Reads and processes all data from the provided input stream. - * - * @param stream the stream to read from - * @param details the details of the uploaded file like file name, content-type and size - * @throws IOException if an input / output error occurs - */ - public abstract void receive( InputStream stream, FileDetails details ) throws IOException; - -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/RWT.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/RWT.java deleted file mode 100644 index 1688594bb..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/RWT.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.eclipse.rap.rwt; - -import java.util.Locale; - -import javax.servlet.http.HttpServletRequest; - -import org.argeo.eclipse.ui.rcp.internal.rwt.RcpClient; -import org.argeo.eclipse.ui.rcp.internal.rwt.RcpResourceManager; -import org.eclipse.rap.rwt.client.Client; -import org.eclipse.rap.rwt.service.ResourceManager; - -public class RWT { - public final static String CUSTOM_VARIANT = "argeo-rcp:CUSTOM_VARIANT"; - public final static String MARKUP_ENABLED = "argeo-rcp:MARKUP_ENABLED"; - public static final String TOOLTIP_MARKUP_ENABLED = "argeo-rcp:TOOLTIP_MARKUP_ENABLED"; - public final static String CUSTOM_ITEM_HEIGHT = "argeo-rcp:CUSTOM_ITEM_HEIGHT"; - public final static String ACTIVE_KEYS = "argeo-rcp:ACTIVE_KEYS"; - public final static String CANCEL_KEYS = "argeo-rcp:CANCEL_KEYS"; - public final static String DEFAULT_THEME_ID = "argeo-rcp:DEFAULT_THEME_ID"; - - public final static int HYPERLINK = 0; - - private static Locale locale = Locale.getDefault(); - private static RcpClient client = new RcpClient(); - private static ResourceManager resourceManager = new RcpResourceManager(); - static { - - } - - public static Locale getLocale() { - return locale; - } - - public static HttpServletRequest getRequest() { - return null; - } - - public static ResourceManager getResourceManager() { - return resourceManager; - } - - public static Client getClient() { - return client; - } -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/SingletonUtil.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/SingletonUtil.java deleted file mode 100644 index 6e30aa635..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/SingletonUtil.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.eclipse.rap.rwt; - -public class SingletonUtil { - public static T getSessionInstance(Class clss) { - return null; - } -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/AbstractEntryPoint.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/AbstractEntryPoint.java deleted file mode 100644 index 980a81854..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/AbstractEntryPoint.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.eclipse.rap.rwt.application; - -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Shell; - -public abstract class AbstractEntryPoint implements EntryPoint { - private Display display; - private Shell shell; - - protected Shell createShell(Display display) { - return new Shell(display); - } - - protected void createContents(Composite parent) { - - } - - public int createUI() { - display = new Display(); - shell = createShell(display); - shell.setLayout(new GridLayout(1, false)); - createContents(shell); - if (shell.getMaximized()) { - shell.layout(); - } else { - shell.pack(); - } - shell.open(); - while (!shell.isDisposed()) { - if (!display.readAndDispatch()) { - display.sleep(); - } - } - display.dispose(); - return 0; - } - - protected Shell getShell() { - return shell; - } -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/Application.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/Application.java deleted file mode 100644 index 6cb5f29d2..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/Application.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.eclipse.rap.rwt.application; - -import java.util.Map; - -import org.eclipse.rap.rwt.service.ResourceLoader; - -public interface Application { - public static enum OperationMode { - JEE_COMPATIBILITY, SWT_COMPATIBILITY, - } - - void setOperationMode(OperationMode operationMode); - - void addResource(String name, ResourceLoader resourceLoader); - - void setExceptionHandler(ExceptionHandler exceptionHandler); - - void addEntryPoint(String path, EntryPointFactory entryPointFactory, - Map properties); - - void addEntryPoint(String path, Class entryPoint, - Map properties); - - void addStyleSheet(String themeId, String styleSheetLocation, - ResourceLoader resourceLoader); - -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/ApplicationConfiguration.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/ApplicationConfiguration.java deleted file mode 100644 index 961ad70f6..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/ApplicationConfiguration.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.eclipse.rap.rwt.application; - -public interface ApplicationConfiguration { - void configure(Application application); -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/EntryPoint.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/EntryPoint.java deleted file mode 100644 index c0d559a2b..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/EntryPoint.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.eclipse.rap.rwt.application; - -public interface EntryPoint { - int createUI(); -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/EntryPointFactory.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/EntryPointFactory.java deleted file mode 100644 index d5b24d8fe..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/EntryPointFactory.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.eclipse.rap.rwt.application; - -public interface EntryPointFactory { - public EntryPoint create(); -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/ExceptionHandler.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/ExceptionHandler.java deleted file mode 100644 index 13daf2195..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/ExceptionHandler.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.eclipse.rap.rwt.application; - -public interface ExceptionHandler { - public void handleException(Throwable throwable); -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/Client.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/Client.java deleted file mode 100644 index 934feaea6..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/Client.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.eclipse.rap.rwt.client; - -import java.io.Serializable; - -import org.eclipse.rap.rwt.client.service.ClientService; - -public interface Client extends Serializable { - - /** - * Returns this client's implementation of a given service, if available. - * - * @param type the type of the requested service, must be a subtype of ClientService - * @return the requested service if provided by this client, otherwise null - * @see ClientService - */ - T getService( Class type ); - -} \ No newline at end of file diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/WebClient.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/WebClient.java deleted file mode 100644 index 1f19bdd7c..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/WebClient.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.eclipse.rap.rwt.client; - -public interface WebClient { - public final static String FAVICON = "rcp:FAVICON"; - public final static String PAGE_TITLE = "rcp:PAGE_TITLE"; - public final static String BODY_HTML = "rcp:BODY_HTML"; - public final static String THEME_ID = "rcp:THEME_ID"; - public final static String HEAD_HTML = "rcp:HEAD_HTML"; - public final static String PAGE_OVERFLOW = "rcp:PAGE_OVERFLOW"; -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigation.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigation.java deleted file mode 100644 index ffba4e43e..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigation.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.eclipse.rap.rwt.client.service; - -public interface BrowserNavigation extends ClientService { - void pushState(String state, String title); - - void addBrowserNavigationListener(BrowserNavigationListener listener); -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigationEvent.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigationEvent.java deleted file mode 100644 index 3e1b3eb72..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigationEvent.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.eclipse.rap.rwt.client.service; - -public class BrowserNavigationEvent { - private String state; - - public String getState() { - return state; - } - -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigationListener.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigationListener.java deleted file mode 100644 index 8319c03f7..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigationListener.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.eclipse.rap.rwt.client.service; - -public interface BrowserNavigationListener { - public void navigated(BrowserNavigationEvent event); -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/ClientService.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/ClientService.java deleted file mode 100644 index 9f479d139..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/ClientService.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.eclipse.rap.rwt.client.service; - -import java.io.Serializable; - -public interface ClientService extends Serializable { -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/JavaScriptExecutor.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/JavaScriptExecutor.java deleted file mode 100644 index 6c44c729c..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/JavaScriptExecutor.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.eclipse.rap.rwt.client.service; - -public interface JavaScriptExecutor extends ClientService { - public void execute( String code ); -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/UrlLauncher.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/UrlLauncher.java deleted file mode 100644 index 9dae811c7..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/UrlLauncher.java +++ /dev/null @@ -1,36 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012 EclipseSource and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * EclipseSource - initial API and implementation - ******************************************************************************/ -package org.eclipse.rap.rwt.client.service; - -/** - * The UrlLauncher service allows loading an URL in an external window, application or save dialog. - * - * @since 2.0 - * @noimplement This interface is not intended to be implemented by clients. - */ -public interface UrlLauncher extends ClientService { - - /** - * Opens the given URL. - * - * Any HTTP URL or relative URL will be opened in a new window. - * Modern browser may block any attempt to open new windows, but will usually prompt the user to - * accept or ignore. Even if accepted, the decision may be applied to only this attempt, or only - * to future attempts. It could also trigger a document reload, causing a session restart. - * - * Non-HTTP URLs like "mailto" will not create a new browser window, but require the client - * to have a matching protocol handler registered. - * - * @param url the URL to open - */ - void openURL( String url ); - -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ResourceLoader.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ResourceLoader.java deleted file mode 100644 index 7e7116cf3..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ResourceLoader.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.eclipse.rap.rwt.service; - -import java.io.IOException; -import java.io.InputStream; - -public interface ResourceLoader { - public InputStream getResourceAsStream(String resourceName) - throws IOException; -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ResourceManager.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ResourceManager.java deleted file mode 100644 index c3379ea66..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ResourceManager.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.eclipse.rap.rwt.service; - -import java.io.InputStream; - -public interface ResourceManager { - public void register(String name, InputStream in); - - boolean unregister(String name); - - public InputStream getRegisteredContent(String name); - - public String getLocation(String name); - - public boolean isRegistered(String name); -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ServerPushSession.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ServerPushSession.java deleted file mode 100644 index bed194f31..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ServerPushSession.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.eclipse.rap.rwt.service; - -/** Mock, does nothing as this is irrelevant for RCP. */ -public class ServerPushSession { - public void start() { - - } - - public void stop() { - - } -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/widgets/DropDown.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/widgets/DropDown.java deleted file mode 100644 index b2a2005e7..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/widgets/DropDown.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.eclipse.rap.rwt.widgets; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Widget; - -public class DropDown { - private boolean visible=false; - - public DropDown(Widget parent, int style) { - // FIXME implement a shell - } - - public DropDown(Widget parent) { - this(parent, SWT.NONE); - } - - public void setVisible(boolean visible) { - this.visible = visible; - } - - public boolean isVisible() { - return visible; - } - - public void setItems( String[] items ) { - - } - - public void setSelectionIndex( int selection ) { - - } - -} diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/widgets/FileUpload.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/widgets/FileUpload.java deleted file mode 100644 index cbf1449e0..000000000 --- a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/widgets/FileUpload.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.eclipse.rap.rwt.widgets; - -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.widgets.Composite; - -public class FileUpload extends Composite { - - public FileUpload(Composite parent, int style) { - super(parent, style); - } - - public void addSelectionListener(SelectionListener listener) { - - } - - public void submit(String url) { - - } - - public void setImage(Image image) { - - } - - public void setText(String text) { - - } - - public String getFileName() { - return null; - } - - public String[] getFileNames() { - return null; - } - -} diff --git a/swt/org.argeo.cms.e4/.classpath b/swt/org.argeo.cms.e4/.classpath new file mode 100644 index 000000000..e801ebfb4 --- /dev/null +++ b/swt/org.argeo.cms.e4/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/swt/org.argeo.cms.e4/.project b/swt/org.argeo.cms.e4/.project new file mode 100644 index 000000000..0c0406952 --- /dev/null +++ b/swt/org.argeo.cms.e4/.project @@ -0,0 +1,33 @@ + + + org.argeo.cms.e4 + + + + + + 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/swt/org.argeo.cms.e4/META-INF/.gitignore b/swt/org.argeo.cms.e4/META-INF/.gitignore new file mode 100644 index 000000000..4854a41b9 --- /dev/null +++ b/swt/org.argeo.cms.e4/META-INF/.gitignore @@ -0,0 +1 @@ +/MANIFEST.MF diff --git a/swt/org.argeo.cms.e4/OSGI-INF/defaultCallbackHandler.xml b/swt/org.argeo.cms.e4/OSGI-INF/defaultCallbackHandler.xml new file mode 100644 index 000000000..fcd3ae5cb --- /dev/null +++ b/swt/org.argeo.cms.e4/OSGI-INF/defaultCallbackHandler.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/swt/org.argeo.cms.e4/OSGI-INF/homeRepository.xml b/swt/org.argeo.cms.e4/OSGI-INF/homeRepository.xml new file mode 100644 index 000000000..65690f262 --- /dev/null +++ b/swt/org.argeo.cms.e4/OSGI-INF/homeRepository.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/swt/org.argeo.cms.e4/OSGI-INF/userAdminWrapper.xml b/swt/org.argeo.cms.e4/OSGI-INF/userAdminWrapper.xml new file mode 100644 index 000000000..cc7087b6e --- /dev/null +++ b/swt/org.argeo.cms.e4/OSGI-INF/userAdminWrapper.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/swt/org.argeo.cms.e4/bnd.bnd b/swt/org.argeo.cms.e4/bnd.bnd new file mode 100644 index 000000000..8026f9869 --- /dev/null +++ b/swt/org.argeo.cms.e4/bnd.bnd @@ -0,0 +1,19 @@ +Service-Component: OSGI-INF/homeRepository.xml,\ +OSGI-INF/userAdminWrapper.xml,\ +OSGI-INF/defaultCallbackHandler.xml +Bundle-ActivationPolicy: lazy + +Import-Package: \ +org.argeo.api.acr,\ +org.eclipse.swt,\ +org.eclipse.swt.widgets;version="0.0.0",\ +org.eclipse.e4.ui.model.application.ui,\ +org.eclipse.e4.ui.model.application,\ +javax.jcr.nodetype,\ +org.argeo.cms,\ +org.eclipse.core.commands.common,\ +org.eclipse.jface.window,\ +org.argeo.cms.swt.auth,\ +org.apache.jackrabbit.*;version="[2,3)",\ +javax.servlet.*;version="[3,5)",\ +* diff --git a/swt/org.argeo.cms.e4/build.properties b/swt/org.argeo.cms.e4/build.properties new file mode 100644 index 000000000..e46a7baee --- /dev/null +++ b/swt/org.argeo.cms.e4/build.properties @@ -0,0 +1,9 @@ +output.. = bin/ +bin.includes = META-INF/,\ + OSGI-INF/,\ + .,\ + OSGI-INF/homeRepository.xml,\ + OSGI-INF/userAdminWrapper.xml,\ + OSGI-INF/defaultCallbackHandler.xml,\ + e4xmi/cms-demo.e4xmi +source.. = src/ diff --git a/swt/org.argeo.cms.e4/e4xmi/cms-devops.e4xmi b/swt/org.argeo.cms.e4/e4xmi/cms-devops.e4xmi new file mode 100644 index 000000000..89bcc376d --- /dev/null +++ b/swt/org.argeo.cms.e4/e4xmi/cms-devops.e4xmi @@ -0,0 +1,129 @@ + + + + + shellMaximized + auth.cn=admin,ou=roles,ou=node + + + auth.cn=admin,ou=roles,ou=node + + + + + + + + + + + + + usersEditorArea + + + + + + + + + + + + + + + + + + + + + + + + + ViewMenu + + + + + + + + + + dataExplorer + + + + + + + + + + + + + + + + + + + + + + + + + auth.cn=admin,ou=roles,ou=node + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/swt/org.argeo.cms.e4/e4xmi/cms-ego.e4xmi b/swt/org.argeo.cms.e4/e4xmi/cms-ego.e4xmi new file mode 100644 index 000000000..ef659fcc7 --- /dev/null +++ b/swt/org.argeo.cms.e4/e4xmi/cms-ego.e4xmi @@ -0,0 +1,67 @@ + + + + + shellMaximized + auth.cn=user,ou=roles,ou=node + + + + + + + + + + + ViewMenu + + + + + + + + + + dataExplorer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/CmsE4Utils.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/CmsE4Utils.java new file mode 100644 index 000000000..a997de748 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/CmsE4Utils.java @@ -0,0 +1,77 @@ +package org.argeo.cms.e4; + +import java.util.List; + +import org.argeo.cms.swt.CmsException; +import org.eclipse.e4.ui.model.application.MApplication; +import org.eclipse.e4.ui.model.application.commands.MCommand; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.model.application.ui.menu.MDirectMenuItem; +import org.eclipse.e4.ui.model.application.ui.menu.MHandledMenuItem; +import org.eclipse.e4.ui.workbench.modeling.EModelService; +import org.eclipse.e4.ui.workbench.modeling.EPartService; +import org.eclipse.e4.ui.workbench.modeling.EPartService.PartState; +import org.osgi.framework.Bundle; +import org.osgi.framework.FrameworkUtil; + +/** Static utilities simplifying recurring Eclipse 4 patterns. */ +public class CmsE4Utils { + /** Open an editor based on its id. */ + public static void openEditor(EPartService partService, String editorId, String key, String state) { + for (MPart part : partService.getParts()) { + String id = part.getPersistedState().get(key); + if (id != null && state.equals(id)) { + partService.showPart(part, PartState.ACTIVATE); + return; + } + } + + // new part + MPart part = partService.createPart(editorId); + if (part == null) + throw new CmsException("No editor found with id " + editorId); + part.getPersistedState().put(key, state); + partService.showPart(part, PartState.ACTIVATE); + } + + /** Dynamically creates an handled menu item from a command ID. */ + public static MHandledMenuItem createHandledMenuItem(EModelService modelService, MApplication app, + String commandId) { + MCommand command = findCommand(modelService, app, commandId); + if (command == null) + return null; + MHandledMenuItem handledItem = modelService.createModelElement(MHandledMenuItem.class); + handledItem.setCommand(command); + return handledItem; + + } + + /** + * Finds a command by ID. + * + * @return the {@link MCommand} or null if not found. + */ + public static MCommand findCommand(EModelService modelService, MApplication app, String commandId) { + List cmds = modelService.findElements(app, null, MCommand.class, null); + for (MCommand cmd : cmds) { + if (cmd.getElementId().equals(commandId)) { + return cmd; + } + } + return null; + } + + /** Dynamically creates a direct menu item from a class. */ + public static MDirectMenuItem createDirectMenuItem(EModelService modelService, Class clss, String label) { + MDirectMenuItem dynamicItem = modelService.createModelElement(MDirectMenuItem.class); + dynamicItem.setLabel(label); + Bundle bundle = FrameworkUtil.getBundle(clss); + dynamicItem.setContributionURI("bundleclass://" + bundle.getSymbolicName() + "/" + clss.getName()); + return dynamicItem; + } + + /** Singleton. */ + private CmsE4Utils() { + } + +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/OsgiFilterContextFunction.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/OsgiFilterContextFunction.java new file mode 100644 index 000000000..1e3e75cec --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/OsgiFilterContextFunction.java @@ -0,0 +1,33 @@ +package org.argeo.cms.e4; + +import org.argeo.cms.swt.CmsException; +import org.eclipse.e4.core.contexts.ContextFunction; +import org.eclipse.e4.core.contexts.IEclipseContext; +import org.eclipse.e4.core.di.IInjector; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; + +/** An Eclipse 4 {@link ContextFunction} based on an OSGi filter. */ +public class OsgiFilterContextFunction extends ContextFunction { + + private BundleContext bc = FrameworkUtil.getBundle(OsgiFilterContextFunction.class).getBundleContext(); + + @Override + public Object compute(IEclipseContext context, String contextKey) { + ServiceReference[] srs; + try { + srs = bc.getServiceReferences((String) null, contextKey); + } catch (InvalidSyntaxException e) { + throw new CmsException("Context key " + contextKey + " must be a valid osgi filter", e); + } + if (srs == null || srs.length == 0) { + return IInjector.NOT_A_VALUE; + } else { + // return the first one + return bc.getService(srs[0]); + } + } + +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/PrivilegedJob.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/PrivilegedJob.java new file mode 100644 index 000000000..89055d2ff --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/PrivilegedJob.java @@ -0,0 +1,49 @@ +package org.argeo.cms.e4; + +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedAction; + +import javax.security.auth.Subject; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.jobs.Job; + +/** + * Propagate authentication to an eclipse job. Typically to execute a privileged + * action outside the UI thread + */ +public abstract class PrivilegedJob extends Job { + private final Subject subject; + + public PrivilegedJob(String jobName) { + this(jobName, AccessController.getContext()); + } + + public PrivilegedJob(String jobName, + AccessControlContext accessControlContext) { + super(jobName); + subject = Subject.getSubject(accessControlContext); + + // Must be called *before* the job is scheduled, + // it is required for the progress window to appear + setUser(true); + } + + @Override + protected IStatus run(final IProgressMonitor progressMonitor) { + PrivilegedAction privilegedAction = new PrivilegedAction() { + public IStatus run() { + return doRun(progressMonitor); + } + }; + return Subject.doAs(subject, privilegedAction); + } + + /** + * Implement here what should be executed with default context + * authentication + */ + protected abstract IStatus doRun(IProgressMonitor progressMonitor); +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/AuthAddon.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/AuthAddon.java new file mode 100644 index 000000000..e84b18c0b --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/AuthAddon.java @@ -0,0 +1,104 @@ +package org.argeo.cms.e4.addons; + +import java.security.AccessController; +import java.util.Iterator; + +import javax.annotation.PostConstruct; +import javax.security.auth.Subject; +import javax.servlet.http.HttpServletRequest; + +import org.argeo.api.cms.CmsLog; +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.swt.CmsException; +import org.eclipse.e4.ui.model.application.MApplication; +import org.eclipse.e4.ui.model.application.ui.MElementContainer; +import org.eclipse.e4.ui.model.application.ui.MUIElement; +import org.eclipse.e4.ui.model.application.ui.basic.MTrimBar; +import org.eclipse.e4.ui.model.application.ui.basic.MTrimmedWindow; +import org.eclipse.e4.ui.model.application.ui.basic.MWindow; + +public class AuthAddon { + private final static CmsLog log = CmsLog.getLog(AuthAddon.class); + + public final static String AUTH = "auth."; + + @PostConstruct + void init(MApplication application) { + Iterator windows = application.getChildren().iterator(); + boolean atLeastOneTopLevelWindowVisible = false; + windows: while (windows.hasNext()) { + MWindow window = windows.next(); + // main window + boolean windowVisible = process(window); + if (!windowVisible) { +// windows.remove(); + continue windows; + } + atLeastOneTopLevelWindowVisible = true; + // trim bars + if (window instanceof MTrimmedWindow) { + Iterator trimBars = ((MTrimmedWindow) window).getTrimBars().iterator(); + while (trimBars.hasNext()) { + MTrimBar trimBar = trimBars.next(); + if (!process(trimBar)) { + trimBars.remove(); + } + } + } + } + + if (!atLeastOneTopLevelWindowVisible) { + log.warn("No top-level window is authorized for user " + CurrentUser.getUsername() + ", logging out.."); + logout(); + } + } + + protected boolean process(MUIElement element) { + for (String tag : element.getTags()) { + if (tag.startsWith(AUTH)) { + String role = tag.substring(AUTH.length(), tag.length()); + if (!CurrentUser.isInRole(role)) { + element.setVisible(false); + element.setToBeRendered(false); + return false; + } + } + } + + // children + if (element instanceof MElementContainer) { + @SuppressWarnings("unchecked") + MElementContainer container = (MElementContainer) element; + Iterator children = container.getChildren().iterator(); + while (children.hasNext()) { + MUIElement child = children.next(); + boolean visible = process(child); + if (!visible) + children.remove(); + } + + for (Object child : container.getChildren()) { + if (child instanceof MUIElement) { + boolean visible = process((MUIElement) child); + if (!visible) + container.getChildren().remove(child); + } + } + } + + return true; + } + + protected void logout() { + Subject subject = Subject.getSubject(AccessController.getContext()); + try { + CurrentUser.logoutCmsSession(subject); + } catch (Exception e) { + throw new CmsException("Cannot log out", e); + } + HttpServletRequest request = org.argeo.eclipse.ui.specific.UiContext.getHttpRequest(); + if (request != null) + request.getSession().setMaxInactiveInterval(0); + } + +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/LocaleAddon.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/LocaleAddon.java new file mode 100644 index 000000000..5bc0d6936 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/LocaleAddon.java @@ -0,0 +1,51 @@ +package org.argeo.cms.e4.addons; + +import java.security.AccessController; +import java.util.List; +import java.util.Locale; +import java.util.Set; + +import javax.annotation.PostConstruct; +import javax.security.auth.Subject; + +import org.argeo.eclipse.ui.specific.UiContext; +import org.eclipse.e4.core.services.nls.ILocaleChangeService; +import org.eclipse.e4.ui.model.application.MApplication; +import org.eclipse.e4.ui.model.application.ui.basic.MWindow; +import org.eclipse.e4.ui.workbench.modeling.EModelService; +import org.eclipse.e4.ui.workbench.modeling.ElementMatcher; +import org.eclipse.swt.SWT; + +/** Integrate workbench with the locale provided at log in. */ +public class LocaleAddon { + private final static String STYLE_OVERRIDE = "styleOverride"; + + // Right to left languages + private final static String ARABIC = "ar"; + private final static String HEBREW = "he"; + + @PostConstruct + public void init(ILocaleChangeService localeChangeService, EModelService modelService, MApplication application) { + Subject subject = Subject.getSubject(AccessController.getContext()); + Set locales = subject.getPublicCredentials(Locale.class); + if (!locales.isEmpty()) { + Locale locale = locales.iterator().next(); + localeChangeService.changeApplicationLocale(locale); + UiContext.setLocale(locale); + + if (locale.getLanguage().equals(ARABIC) || locale.getLanguage().equals(HEBREW)) { + List windows = modelService.findElements(application, MWindow.class, EModelService.ANYWHERE, + new ElementMatcher(null, null, (String) null)); + for (MWindow window : windows) { + String currentStyle = window.getPersistedState().get(STYLE_OVERRIDE); + int style = 0; + if (currentStyle != null) { + style = Integer.parseInt(currentStyle); + } + style = style | SWT.RIGHT_TO_LEFT; + window.getPersistedState().put(STYLE_OVERRIDE, Integer.toString(style)); + } + } + } + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/package-info.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/package-info.java new file mode 100644 index 000000000..6367b42d5 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/package-info.java @@ -0,0 +1,2 @@ +/** Eclipse 4 addons to integrate with Argeo CMS. */ +package org.argeo.cms.e4.addons; \ No newline at end of file diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/files/NodeFsBrowserView.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/files/NodeFsBrowserView.java new file mode 100644 index 000000000..579d35d6a --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/files/NodeFsBrowserView.java @@ -0,0 +1,50 @@ +package org.argeo.cms.e4.files; + +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.spi.FileSystemProvider; + +import javax.annotation.PostConstruct; +import javax.inject.Inject; + +import org.argeo.cms.jcr.CmsJcrUtils; +import org.argeo.cms.swt.CmsException; +import org.argeo.eclipse.ui.fs.AdvancedFsBrowser; +import org.argeo.eclipse.ui.fs.SimpleFsBrowser; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; + +/** Browse the node file system. */ +public class NodeFsBrowserView { + // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + + // ".nodeFsBrowserView"; + + @Inject + FileSystemProvider nodeFileSystemProvider; + + @PostConstruct + public void createPartControl(Composite parent) { + try { + //URI uri = new URI("node://root:demo@localhost:7070/"); + URI uri = new URI("node:///"); + FileSystem fileSystem = nodeFileSystemProvider.getFileSystem(uri); + if (fileSystem == null) + fileSystem = nodeFileSystemProvider.newFileSystem(uri, null); + Path nodePath = fileSystem.getPath("/"); + + Path localPath = Paths.get(System.getProperty("user.home")); + + SimpleFsBrowser browser = new SimpleFsBrowser(parent, SWT.NO_FOCUS); + browser.setInput(nodePath, localPath); +// AdvancedFsBrowser browser = new AdvancedFsBrowser(); +// browser.createUi(parent, localPath); + } catch (Exception e) { + throw new CmsException("Cannot open file system browser", e); + } + } + + public void setFocus() { + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/files/package-info.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/files/package-info.java new file mode 100644 index 000000000..b481dd48a --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/files/package-info.java @@ -0,0 +1,2 @@ +/** Files browser perspective. */ +package org.argeo.cms.e4.files; \ No newline at end of file diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangeLanguage.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangeLanguage.java new file mode 100644 index 000000000..416df7df1 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangeLanguage.java @@ -0,0 +1,13 @@ +package org.argeo.cms.e4.handlers; + +import java.util.Locale; + +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.core.services.nls.ILocaleChangeService; + +public class ChangeLanguage { + @Execute + public void execute(ILocaleChangeService localeChangeService) { + localeChangeService.changeApplicationLocale(Locale.FRENCH); + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangePassword.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangePassword.java new file mode 100644 index 000000000..7ef8c59da --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangePassword.java @@ -0,0 +1,137 @@ +package org.argeo.cms.e4.handlers; + +import static org.argeo.cms.CmsMsg.changePassword; +import static org.argeo.cms.CmsMsg.currentPassword; +import static org.argeo.cms.CmsMsg.newPassword; +import static org.argeo.cms.CmsMsg.passwordChanged; +import static org.argeo.cms.CmsMsg.repeatNewPassword; + +import java.util.Arrays; + +import javax.inject.Inject; +import javax.naming.InvalidNameException; +import javax.naming.ldap.LdapName; + +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.security.CryptoKeyring; +import org.argeo.cms.swt.CmsException; +import org.argeo.cms.swt.dialogs.CmsMessageDialog; +import org.argeo.eclipse.ui.dialogs.ErrorFeedback; +import org.argeo.util.transaction.WorkTransaction; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.core.di.annotations.Optional; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.osgi.service.useradmin.User; +import org.osgi.service.useradmin.UserAdmin; + +/** Change the password of the logged-in user. */ +public class ChangePassword { + @Inject + private UserAdmin userAdmin; + @Inject + private WorkTransaction userTransaction; + @Inject + @Optional + private CryptoKeyring keyring = null; + + @Execute + public void execute() { + ChangePasswordDialog dialog = new ChangePasswordDialog(Display.getCurrent().getActiveShell(), userAdmin); + if (dialog.open() == Dialog.OK) { + new CmsMessageDialog(Display.getCurrent().getActiveShell(), passwordChanged.lead(), + CmsMessageDialog.INFORMATION).open(); + } + } + + protected void changePassword(char[] oldPassword, char[] newPassword) { + String name = CurrentUser.getUsername(); + LdapName dn; + try { + dn = new LdapName(name); + } catch (InvalidNameException e) { + throw new CmsException("Invalid user dn " + name, e); + } + User user = (User) userAdmin.getRole(dn.toString()); + if (!user.hasCredential(null, oldPassword)) + throw new CmsException("Invalid password"); + if (Arrays.equals(newPassword, new char[0])) + throw new CmsException("New password empty"); + try { + userTransaction.begin(); + user.getCredentials().put(null, newPassword); + if (keyring != null) { + keyring.changePassword(oldPassword, newPassword); + // TODO change secret keys in the CMS session + } + userTransaction.commit(); + } catch (Exception e) { + try { + userTransaction.rollback(); + } catch (Exception e1) { + e1.printStackTrace(); + } + if (e instanceof RuntimeException) + throw (RuntimeException) e; + else + throw new CmsException("Cannot change password", e); + } + } + + class ChangePasswordDialog extends CmsMessageDialog { + private Text oldPassword, newPassword1, newPassword2; + + public ChangePasswordDialog(Shell parentShell, UserAdmin securityService) { + super(parentShell, changePassword.lead(), CONFIRM); + } + +// protected Point getInitialSize() { +// return new Point(400, 450); +// } + + protected Control createDialogArea(Composite parent) { + Composite dialogarea = (Composite) super.createDialogArea(parent); + dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + Composite composite = new Composite(dialogarea, SWT.NONE); + composite.setLayout(new GridLayout(2, false)); + composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + oldPassword = createLP(composite, currentPassword.lead()); + newPassword1 = createLP(composite, newPassword.lead()); + newPassword2 = createLP(composite, repeatNewPassword.lead()); + +// parent.pack(); + oldPassword.setFocus(); + return composite; + } + + @Override + protected void okPressed() { + try { + if (!newPassword1.getText().equals(newPassword2.getText())) + throw new CmsException("New passwords are different"); + changePassword(oldPassword.getTextChars(), newPassword1.getTextChars()); + closeShell(OK); + } catch (Exception e) { + ErrorFeedback.show("Cannot change password", e); + } + } + + /** Creates label and password. */ + protected Text createLP(Composite parent, String label) { + new Label(parent, SWT.NONE).setText(label); + Text text = new Text(parent, SWT.SINGLE | SWT.LEAD | SWT.PASSWORD | SWT.BORDER); + text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + return text; + } + + } + +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/CloseAllParts.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/CloseAllParts.java new file mode 100644 index 000000000..d11c0412c --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/CloseAllParts.java @@ -0,0 +1,37 @@ +package org.argeo.cms.e4.handlers; + +import org.eclipse.e4.core.di.annotations.CanExecute; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.workbench.modeling.EPartService; + +public class CloseAllParts { + + @Execute + void execute(EPartService partService) { + for (MPart part : partService.getParts()) { + if (part.isCloseable()) { + if (part.isDirty()) { + if (partService.savePart(part, true)) { + partService.hidePart(part, true); + } + } else { + partService.hidePart(part, true); + } + } + } + } + + @CanExecute + boolean canExecute(EPartService partService) { + boolean atLeastOnePart = false; + for (MPart part : partService.getParts()) { + if (part.isVisible() && part.isCloseable()) { + atLeastOnePart = true; + break; + } + } + return atLeastOnePart; + } + +} \ No newline at end of file diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/CloseWorkbench.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/CloseWorkbench.java new file mode 100644 index 000000000..c2ae4bff7 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/CloseWorkbench.java @@ -0,0 +1,28 @@ +package org.argeo.cms.e4.handlers; + +import java.security.AccessController; + +import javax.security.auth.Subject; + +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.swt.CmsException; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.workbench.IWorkbench; + +public class CloseWorkbench { + @Execute + public void execute(IWorkbench workbench) { + logout(); + workbench.close(); + } + + protected void logout() { + Subject subject = Subject.getSubject(AccessController.getContext()); + try { + CurrentUser.logoutCmsSession(subject); + } catch (Exception e) { + throw new CmsException("Cannot log out", e); + } + } + +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/DoNothing.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/DoNothing.java new file mode 100644 index 000000000..358494c5b --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/DoNothing.java @@ -0,0 +1,10 @@ +package org.argeo.cms.e4.handlers; + +import org.eclipse.e4.core.di.annotations.Execute; + +public class DoNothing { + @Execute + public void execute() { + + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/LanguageMenuContribution.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/LanguageMenuContribution.java new file mode 100644 index 000000000..ac825bb0d --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/LanguageMenuContribution.java @@ -0,0 +1,29 @@ + +package org.argeo.cms.e4.handlers; + +import java.util.Date; +import java.util.List; + +import org.eclipse.e4.ui.di.AboutToHide; +import org.eclipse.e4.ui.di.AboutToShow; +import org.eclipse.e4.ui.model.application.ui.menu.MDirectMenuItem; +import org.eclipse.e4.ui.model.application.ui.menu.MMenuElement; +import org.eclipse.e4.ui.workbench.modeling.EModelService; + +public class LanguageMenuContribution { + @AboutToShow + public void aboutToShow(List items, EModelService modelService) { + MDirectMenuItem dynamicItem = modelService.createModelElement(MDirectMenuItem.class); + dynamicItem.setLabel("Dynamic Menu Item (" + new Date() + ")"); + //dynamicItem.setContributorURI("platform:/plugin/org.argeo.cms.e4"); + //dynamicItem.setContributionURI("bundleclass://org.argeo.cms.e4/" + ChangeLanguage.class.getName()); + dynamicItem.setEnabled(true); + dynamicItem.setContributionURI("bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.handlers.ChangeLanguage"); + items.add(dynamicItem); + } + + @AboutToHide + public void aboutToHide() { + + } +} \ No newline at end of file diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/OpenPerspective.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/OpenPerspective.java new file mode 100644 index 000000000..ac544b107 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/OpenPerspective.java @@ -0,0 +1,31 @@ +package org.argeo.cms.e4.handlers; + +import java.util.List; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.model.application.MApplication; +import org.eclipse.e4.ui.model.application.ui.advanced.MPerspective; +import org.eclipse.e4.ui.workbench.modeling.EModelService; +import org.eclipse.e4.ui.workbench.modeling.EPartService; + +public class OpenPerspective { + @Inject + MApplication application; + @Inject + EPartService partService; + @Inject + EModelService modelService; + + @Execute + public void execute(@Named("perspectiveId") String perspectiveId) { + List perspectives = modelService.findElements(application, perspectiveId, MPerspective.class, + null); + if (perspectives.size() == 0) + return; + MPerspective perspective = perspectives.get(0); + partService.switchPerspective(perspective); + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/SaveAllParts.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/SaveAllParts.java new file mode 100644 index 000000000..3b60abd7e --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/SaveAllParts.java @@ -0,0 +1,19 @@ +package org.argeo.cms.e4.handlers; + +import org.eclipse.e4.core.di.annotations.CanExecute; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.workbench.modeling.EPartService; + +public class SaveAllParts { + + @Execute + void execute(EPartService partService) { + partService.saveAll(false); + } + + @CanExecute + boolean canExecute(EPartService partService) { + return partService.getDirtyParts().size() > 0; + } + +} \ No newline at end of file diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/SavePart.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/SavePart.java new file mode 100644 index 000000000..73486f363 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/SavePart.java @@ -0,0 +1,18 @@ +package org.argeo.cms.e4.handlers; + +import org.eclipse.e4.core.di.annotations.CanExecute; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.workbench.modeling.EPartService; + +public class SavePart { + @Execute + void execute(EPartService partService, MPart part) { + partService.savePart(part, false); + } + + @CanExecute + boolean canExecute(MPart part) { + return part.isDirty(); + } +} \ No newline at end of file diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/package-info.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/package-info.java new file mode 100644 index 000000000..a44ca9056 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/package-info.java @@ -0,0 +1,2 @@ +/** Generic Eclipse 4 handlers. */ +package org.argeo.cms.e4.handlers; \ No newline at end of file diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/EclipseJcrMonitor.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/EclipseJcrMonitor.java new file mode 100644 index 000000000..e10738ee0 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/EclipseJcrMonitor.java @@ -0,0 +1,44 @@ +package org.argeo.cms.e4.jcr; + +import org.argeo.jcr.JcrMonitor; +import org.eclipse.core.runtime.IProgressMonitor; + +/** + * Wraps an Eclipse {@link IProgressMonitor} so that it can be passed to + * framework agnostic Argeo routines. + */ +public class EclipseJcrMonitor implements JcrMonitor { + private final IProgressMonitor progressMonitor; + + public EclipseJcrMonitor(IProgressMonitor progressMonitor) { + this.progressMonitor = progressMonitor; + } + + public void beginTask(String name, int totalWork) { + progressMonitor.beginTask(name, totalWork); + } + + public void done() { + progressMonitor.done(); + } + + public boolean isCanceled() { + return progressMonitor.isCanceled(); + } + + public void setCanceled(boolean value) { + progressMonitor.setCanceled(value); + } + + public void setTaskName(String name) { + progressMonitor.setTaskName(name); + } + + public void subTask(String name) { + progressMonitor.subTask(name); + } + + public void worked(int work) { + progressMonitor.worked(work); + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/GenericPropertyPage.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/GenericPropertyPage.java new file mode 100644 index 000000000..e17f17bb7 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/GenericPropertyPage.java @@ -0,0 +1,141 @@ +package org.argeo.cms.e4.jcr; + +import java.util.ArrayList; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.PropertyIterator; +import javax.jcr.RepositoryException; + +import org.argeo.cms.ui.jcr.PropertyLabelProvider; +import org.argeo.eclipse.ui.EclipseUiException; +import org.eclipse.jface.layout.TreeColumnLayout; +import org.eclipse.jface.viewers.ColumnWeightData; +import org.eclipse.jface.viewers.IBaseLabelProvider; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeColumn; + +/** + * Generic editor property page. Lists all properties of current node as a + * complex tree. TODO: enable editing + */ +public class GenericPropertyPage { + + // Main business Objects + private Node currentNode; + + public GenericPropertyPage(Node currentNode) { + this.currentNode = currentNode; + } + + protected void createFormContent(Composite parent) { + Composite innerBox = new Composite(parent, SWT.NONE); + // Composite innerBox = new Composite(body, SWT.NO_FOCUS); + FillLayout layout = new FillLayout(); + layout.marginHeight = 5; + layout.marginWidth = 5; + innerBox.setLayout(layout); + createComplexTree(innerBox); + // TODO TreeColumnLayout triggers a scroll issue with the form: + // The inside body is always to big and a scroll bar is shown + // Composite tableCmp = new Composite(body, SWT.NO_FOCUS); + // createComplexTree(tableCmp); + } + + private TreeViewer createComplexTree(Composite parent) { + int style = SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION; + Tree tree = new Tree(parent, style); + TreeColumnLayout tableColumnLayout = new TreeColumnLayout(); + + createColumn(tree, tableColumnLayout, "Property", SWT.LEFT, 200, 30); + createColumn(tree, tableColumnLayout, "Value(s)", SWT.LEFT, 300, 60); + createColumn(tree, tableColumnLayout, "Type", SWT.LEFT, 75, 10); + createColumn(tree, tableColumnLayout, "Attributes", SWT.LEFT, 75, 0); + // Do not apply the treeColumnLayout it does not work yet + // parent.setLayout(tableColumnLayout); + + tree.setLinesVisible(true); + tree.setHeaderVisible(true); + + TreeViewer treeViewer = new TreeViewer(tree); + treeViewer.setContentProvider(new TreeContentProvider()); + treeViewer.setLabelProvider((IBaseLabelProvider) new PropertyLabelProvider()); + treeViewer.setInput(currentNode); + treeViewer.expandAll(); + return treeViewer; + } + + private static TreeColumn createColumn(Tree parent, TreeColumnLayout tableColumnLayout, String name, int style, + int width, int weight) { + TreeColumn column = new TreeColumn(parent, style); + column.setText(name); + column.setWidth(width); + column.setMoveable(true); + column.setResizable(true); + tableColumnLayout.setColumnData(column, new ColumnWeightData(weight, width, true)); + return column; + } + + private class TreeContentProvider implements ITreeContentProvider { + private static final long serialVersionUID = -6162736530019406214L; + + public Object[] getElements(Object parent) { + Object[] props = null; + try { + + if (parent instanceof Node) { + Node node = (Node) parent; + PropertyIterator pi; + pi = node.getProperties(); + List propList = new ArrayList(); + while (pi.hasNext()) { + propList.add(pi.nextProperty()); + } + props = propList.toArray(); + } + } catch (RepositoryException e) { + throw new EclipseUiException("Unexpected exception while listing node properties", e); + } + return props; + } + + public Object getParent(Object child) { + return null; + } + + public Object[] getChildren(Object parent) { + if (parent instanceof Property) { + Property prop = (Property) parent; + try { + if (prop.isMultiple()) + return prop.getValues(); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot get multi-prop values on " + prop, e); + } + } + return null; + } + + public boolean hasChildren(Object parent) { + try { + return (parent instanceof Property && ((Property) parent).isMultiple()); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot check if property is multiple for " + parent, e); + } + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + + public void dispose() { + } + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrBrowserView.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrBrowserView.java new file mode 100644 index 000000000..0b77c0732 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrBrowserView.java @@ -0,0 +1,349 @@ +package org.argeo.cms.e4.jcr; + +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.inject.Inject; +import javax.jcr.Property; +import javax.jcr.PropertyType; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.RepositoryFactory; +import javax.jcr.Session; +import javax.jcr.Value; +import javax.jcr.observation.Event; +import javax.jcr.observation.EventListener; +import javax.jcr.observation.ObservationManager; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.cms.security.CryptoKeyring; +import org.argeo.cms.security.Keyring; +import org.argeo.cms.swt.CmsException; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ui.jcr.JcrBrowserUtils; +import org.argeo.cms.ui.jcr.NodeContentProvider; +import org.argeo.cms.ui.jcr.NodeLabelProvider; +import org.argeo.cms.ui.jcr.OsgiRepositoryRegister; +import org.argeo.cms.ui.jcr.PropertiesContentProvider; +import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; +import org.argeo.cms.ux.widgets.TreeParent; +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.eclipse.ui.jcr.AsyncUiEventListener; +import org.argeo.eclipse.ui.jcr.util.NodeViewerComparer; +import org.argeo.jcr.JcrUtils; +import org.eclipse.e4.core.contexts.IEclipseContext; +import org.eclipse.e4.core.di.annotations.Optional; +import org.eclipse.e4.ui.services.EMenuService; +import org.eclipse.e4.ui.workbench.modeling.EPartService; +import org.eclipse.e4.ui.workbench.modeling.ESelectionService; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.jface.viewers.IBaseLabelProvider; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TableViewerColumn; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.SashForm; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; + +/** + * Basic View to display a sash form to browse a JCR compliant multiple + * repository environment + */ +public class JcrBrowserView { + final static String ID = "org.argeo.cms.e4.jcrbrowser"; + final static String NODE_VIEWER_POPUP_MENU_ID = "org.argeo.cms.e4.popupmenu.nodeViewer"; + + private boolean sortChildNodes = true; + + /* DEPENDENCY INJECTION */ + @Inject + @Optional + private Keyring keyring; + @Inject + private RepositoryFactory repositoryFactory; + @Inject + private Repository nodeRepository; + + // Current user session on the home repository default workspace + private Session userSession; + + private OsgiRepositoryRegister repositoryRegister = new OsgiRepositoryRegister(); + + // This page widgets + private TreeViewer nodesViewer; + private NodeContentProvider nodeContentProvider; + private TableViewer propertiesViewer; + private EventListener resultsObserver; + + @PostConstruct + public void createPartControl(Composite parent, IEclipseContext context, EPartService partService, + ESelectionService selectionService, EMenuService menuService) { + repositoryRegister.init(); + + parent.setLayout(new FillLayout()); + SashForm sashForm = new SashForm(parent, SWT.VERTICAL); + // sashForm.setSashWidth(4); + // sashForm.setLayout(new FillLayout()); + + // Create the tree on top of the view + Composite top = new Composite(sashForm, SWT.NONE); + // GridLayout gl = new GridLayout(1, false); + top.setLayout(CmsSwtUtils.noSpaceGridLayout()); + + try { + this.userSession = this.nodeRepository.login(CmsConstants.HOME_WORKSPACE); + } catch (RepositoryException e) { + throw new CmsException("Cannot open user session", e); + } + + nodeContentProvider = new NodeContentProvider(userSession, keyring, repositoryRegister, repositoryFactory, + sortChildNodes); + + // nodes viewer + nodesViewer = createNodeViewer(top, nodeContentProvider); + + // context menu : it is completely defined in the plugin.xml file. + // MenuManager menuManager = new MenuManager(); + // Menu menu = menuManager.createContextMenu(nodesViewer.getTree()); + + // nodesViewer.getTree().setMenu(menu); + + nodesViewer.setInput(""); + + // Create the property viewer on the bottom + Composite bottom = new Composite(sashForm, SWT.NONE); + bottom.setLayout(CmsSwtUtils.noSpaceGridLayout()); + propertiesViewer = createPropertiesViewer(bottom); + + sashForm.setWeights(getWeights()); + nodesViewer.setComparer(new NodeViewerComparer()); + nodesViewer.addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + IStructuredSelection selection = (IStructuredSelection) event.getSelection(); + selectionService.setSelection(selection.toList()); + } + }); + nodesViewer.addDoubleClickListener(new JcrE4DClickListener(nodesViewer, partService)); + menuService.registerContextMenu(nodesViewer.getControl(), NODE_VIEWER_POPUP_MENU_ID); + // getSite().registerContextMenu(menuManager, nodesViewer); + // getSite().setSelectionProvider(nodesViewer); + } + + @PreDestroy + public void dispose() { + JcrUtils.logoutQuietly(userSession); + repositoryRegister.destroy(); + } + + public void refresh(Object obj) { + // Enable full refresh from a command when no element of the tree is + // selected + if (obj == null) { + Object[] elements = nodeContentProvider.getElements(null); + for (Object el : elements) { + if (el instanceof TreeParent) + JcrBrowserUtils.forceRefreshIfNeeded((TreeParent) el); + getNodeViewer().refresh(el); + } + } else + getNodeViewer().refresh(obj); + } + + /** + * To be overridden to adapt size of form and result frames. + */ + protected int[] getWeights() { + return new int[] { 70, 30 }; + } + + protected TreeViewer createNodeViewer(Composite parent, final ITreeContentProvider nodeContentProvider) { + + final TreeViewer tmpNodeViewer = new TreeViewer(parent, SWT.MULTI); + + tmpNodeViewer.getTree().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + tmpNodeViewer.setContentProvider(nodeContentProvider); + tmpNodeViewer.setLabelProvider((IBaseLabelProvider) new NodeLabelProvider()); + tmpNodeViewer.addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + if (!event.getSelection().isEmpty()) { + IStructuredSelection sel = (IStructuredSelection) event.getSelection(); + Object firstItem = sel.getFirstElement(); + if (firstItem instanceof SingleJcrNodeElem) + propertiesViewer.setInput(((SingleJcrNodeElem) firstItem).getNode()); + } else { + propertiesViewer.setInput(""); + } + } + }); + + resultsObserver = new TreeObserver(tmpNodeViewer.getTree().getDisplay()); + if (keyring != null) + try { + ObservationManager observationManager = userSession.getWorkspace().getObservationManager(); + observationManager.addEventListener(resultsObserver, Event.PROPERTY_ADDED | Event.PROPERTY_CHANGED, "/", + true, null, null, false); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot register listeners", e); + } + + // tmpNodeViewer.addDoubleClickListener(new JcrDClickListener(tmpNodeViewer)); + return tmpNodeViewer; + } + + protected TableViewer createPropertiesViewer(Composite parent) { + propertiesViewer = new TableViewer(parent, SWT.NONE); + propertiesViewer.getTable().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + propertiesViewer.getTable().setHeaderVisible(true); + propertiesViewer.setContentProvider(new PropertiesContentProvider()); + TableViewerColumn col = new TableViewerColumn(propertiesViewer, SWT.NONE); + col.getColumn().setText("Name"); + col.getColumn().setWidth(200); + col.setLabelProvider(new ColumnLabelProvider() { + private static final long serialVersionUID = -6684361063107478595L; + + public String getText(Object element) { + try { + return ((Property) element).getName(); + } catch (RepositoryException e) { + throw new EclipseUiException("Unexpected exception in label provider", e); + } + } + }); + col = new TableViewerColumn(propertiesViewer, SWT.NONE); + col.getColumn().setText("Value"); + col.getColumn().setWidth(400); + col.setLabelProvider(new ColumnLabelProvider() { + private static final long serialVersionUID = -8201994187693336657L; + + public String getText(Object element) { + try { + Property property = (Property) element; + if (property.getType() == PropertyType.BINARY) + return ""; + else if (property.isMultiple()) { + StringBuffer buf = new StringBuffer("["); + Value[] values = property.getValues(); + for (int i = 0; i < values.length; i++) { + if (i != 0) + buf.append(", "); + buf.append(values[i].getString()); + } + buf.append(']'); + return buf.toString(); + } else + return property.getValue().getString(); + } catch (RepositoryException e) { + throw new EclipseUiException("Unexpected exception in label provider", e); + } + } + }); + col = new TableViewerColumn(propertiesViewer, SWT.NONE); + col.getColumn().setText("Type"); + col.getColumn().setWidth(200); + col.setLabelProvider(new ColumnLabelProvider() { + private static final long serialVersionUID = -6009599998150286070L; + + public String getText(Object element) { + return JcrBrowserUtils.getPropertyTypeAsString((Property) element); + } + }); + propertiesViewer.setInput(""); + return propertiesViewer; + } + + protected TreeViewer getNodeViewer() { + return nodesViewer; + } + + /** + * Resets the tree content provider + * + * @param sortChildNodes if true the content provider will use a comparer to + * sort nodes that might slow down the display + */ + public void setSortChildNodes(boolean sortChildNodes) { + this.sortChildNodes = sortChildNodes; + ((NodeContentProvider) nodesViewer.getContentProvider()).setSortChildren(sortChildNodes); + nodesViewer.setInput(""); + } + + /** Notifies the current view that a node has been added */ + public void nodeAdded(TreeParent parentNode) { + // insure that Ui objects have been correctly created: + JcrBrowserUtils.forceRefreshIfNeeded(parentNode); + getNodeViewer().refresh(parentNode); + getNodeViewer().expandToLevel(parentNode, 1); + } + + /** Notifies the current view that a node has been removed */ + public void nodeRemoved(TreeParent parentNode) { + IStructuredSelection newSel = new StructuredSelection(parentNode); + getNodeViewer().setSelection(newSel, true); + // Force refresh + IStructuredSelection tmpSel = (IStructuredSelection) getNodeViewer().getSelection(); + getNodeViewer().refresh(tmpSel.getFirstElement()); + } + + class TreeObserver extends AsyncUiEventListener { + + public TreeObserver(Display display) { + super(display); + } + + @Override + protected Boolean willProcessInUiThread(List events) throws RepositoryException { + for (Event event : events) { + if (getLog().isTraceEnabled()) + getLog().debug("Received event " + event); + String path = event.getPath(); + int index = path.lastIndexOf('/'); + String propertyName = path.substring(index + 1); + if (getLog().isTraceEnabled()) + getLog().debug("Concerned property " + propertyName); + } + return false; + } + + protected void onEventInUiThread(List events) throws RepositoryException { + if (getLog().isTraceEnabled()) + getLog().trace("Refresh result list"); + nodesViewer.refresh(); + } + + } + + public boolean getSortChildNodes() { + return sortChildNodes; + } + + public void setFocus() { + getNodeViewer().getTree().setFocus(); + } + + /* DEPENDENCY INJECTION */ + // public void setRepositoryRegister(RepositoryRegister repositoryRegister) { + // this.repositoryRegister = repositoryRegister; + // } + + public void setKeyring(CryptoKeyring keyring) { + this.keyring = keyring; + } + + public void setRepositoryFactory(RepositoryFactory repositoryFactory) { + this.repositoryFactory = repositoryFactory; + } + + public void setNodeRepository(Repository nodeRepository) { + this.nodeRepository = nodeRepository; + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrE4DClickListener.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrE4DClickListener.java new file mode 100644 index 000000000..f4ee2e8da --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrE4DClickListener.java @@ -0,0 +1,36 @@ +package org.argeo.cms.e4.jcr; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.swt.CmsException; +import org.argeo.cms.ui.jcr.JcrDClickListener; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.workbench.modeling.EPartService; +import org.eclipse.e4.ui.workbench.modeling.EPartService.PartState; +import org.eclipse.jface.viewers.TreeViewer; + +public class JcrE4DClickListener extends JcrDClickListener { + EPartService partService; + + public JcrE4DClickListener(TreeViewer nodeViewer, EPartService partService) { + super(nodeViewer); + this.partService = partService; + } + + @Override + protected void openNode(Node node) { + MPart part = partService.createPart(JcrNodeEditor.DESCRIPTOR_ID); + try { + part.setLabel(node.getName()); + part.getPersistedState().put("nodeWorkspace", node.getSession().getWorkspace().getName()); + part.getPersistedState().put("nodePath", node.getPath()); + } catch (RepositoryException e) { + throw new CmsException("Cannot open " + node, e); + } + + // the provided part is be shown + partService.showPart(part, PartState.ACTIVATE); + } + +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrNodeEditor.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrNodeEditor.java new file mode 100644 index 000000000..ae2b325f5 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrNodeEditor.java @@ -0,0 +1,26 @@ +package org.argeo.cms.e4.jcr; + +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.jcr.Node; + +import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.workbench.modeling.ESelectionService; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Composite; + +public class JcrNodeEditor { + final static String DESCRIPTOR_ID = "org.argeo.cms.e4.partdescriptor.nodeEditor"; + + @PostConstruct + public void createUi(Composite parent, MPart part, ESelectionService selectionService) { + parent.setLayout(new FillLayout()); + List selection = (List) selectionService.getSelection(); + Node node = ((SingleJcrNodeElem) selection.get(0)).getNode(); + GenericPropertyPage propertyPage = new GenericPropertyPage(node); + propertyPage.createFormContent(parent); + } + +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/SimplePart.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/SimplePart.java new file mode 100644 index 000000000..17d8d2a23 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/SimplePart.java @@ -0,0 +1,19 @@ +package org.argeo.cms.e4.jcr; + +import javax.annotation.PostConstruct; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; + +public class SimplePart { + + @PostConstruct + void init(Composite parent) { + parent.setLayout(new GridLayout()); + Label label = new Label(parent, SWT.NONE); + label.setText("Hello e4 World"); + } + +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/AddFolderNode.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/AddFolderNode.java new file mode 100644 index 000000000..09fa760cd --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/AddFolderNode.java @@ -0,0 +1,67 @@ +package org.argeo.cms.e4.jcr.handlers; + +import java.util.List; + +import javax.inject.Named; +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.nodetype.NodeType; + +import org.argeo.cms.e4.jcr.JcrBrowserView; +import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; +import org.argeo.cms.ui.jcr.model.WorkspaceElem; +import org.argeo.cms.ux.widgets.TreeParent; +import org.argeo.eclipse.ui.dialogs.ErrorFeedback; +import org.argeo.eclipse.ui.dialogs.SingleValue; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.services.IServiceConstants; +import org.eclipse.e4.ui.workbench.modeling.ESelectionService; + +/** + * Adds a node of type nt:folder, only on {@link SingleJcrNodeElem} and + * {@link WorkspaceElem} TreeObject types. + * + * This handler assumes that a selection provider is available and picks only + * first selected item. It is UI's job to enable the command only when the + * selection contains one and only one element. Thus no parameter is passed + * through the command. + */ +public class AddFolderNode { + @Execute + public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) { + List selection = (List) selectionService.getSelection(); + JcrBrowserView view = (JcrBrowserView) part.getObject(); + + if (selection != null && selection.size() == 1) { + TreeParent treeParentNode = null; + Node jcrParentNode = null; + Object obj = selection.get(0); + + if (obj instanceof SingleJcrNodeElem) { + treeParentNode = (TreeParent) obj; + jcrParentNode = ((SingleJcrNodeElem) treeParentNode).getNode(); + } else if (obj instanceof WorkspaceElem) { + treeParentNode = (TreeParent) obj; + jcrParentNode = ((WorkspaceElem) treeParentNode).getRootNode(); + } else + return; + + String folderName = SingleValue.ask("Folder name", "Enter folder name"); + if (folderName != null) { + try { + jcrParentNode.addNode(folderName, NodeType.NT_FOLDER); + jcrParentNode.getSession().save(); + view.nodeAdded(treeParentNode); + } catch (RepositoryException e) { + ErrorFeedback.show("Cannot create folder " + folderName + " under " + treeParentNode, e); + } + } + } else { + // ErrorFeedback.show(WorkbenchUiPlugin + // .getMessage("errorUnvalidNtFolderNodeType")); + ErrorFeedback.show("Invalid NT folder node type"); + } + } + +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/AddRemoteRepository.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/AddRemoteRepository.java new file mode 100644 index 000000000..dc47f6edf --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/AddRemoteRepository.java @@ -0,0 +1,210 @@ +package org.argeo.cms.e4.jcr.handlers; + +import java.net.URI; +import java.util.Hashtable; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.jcr.Node; +import javax.jcr.Repository; +import javax.jcr.RepositoryFactory; +import javax.jcr.Session; +import javax.jcr.SimpleCredentials; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.cms.ArgeoNames; +import org.argeo.cms.ArgeoTypes; +import org.argeo.cms.e4.jcr.JcrBrowserView; +import org.argeo.cms.jcr.CmsJcrUtils; +import org.argeo.cms.security.Keyring; +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.eclipse.ui.dialogs.ErrorFeedback; +import org.argeo.jcr.JcrUtils; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.core.di.annotations.Optional; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.services.IServiceConstants; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IMessageProvider; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.dialogs.TitleAreaDialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +/** + * Connect to a remote repository and, if successful publish it as an OSGi + * service. + */ +public class AddRemoteRepository { + + @Inject + private RepositoryFactory repositoryFactory; + @Inject + private Repository nodeRepository; + @Inject + @Optional + private Keyring keyring; + + @Execute + public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part) { + JcrBrowserView view = (JcrBrowserView) part.getObject(); + RemoteRepositoryLoginDialog dlg = new RemoteRepositoryLoginDialog(Display.getDefault().getActiveShell()); + if (dlg.open() == Dialog.OK) { + view.refresh(null); + } + } + + // public void setRepositoryFactory(RepositoryFactory repositoryFactory) { + // this.repositoryFactory = repositoryFactory; + // } + // + // public void setKeyring(Keyring keyring) { + // this.keyring = keyring; + // } + // + // public void setNodeRepository(Repository nodeRepository) { + // this.nodeRepository = nodeRepository; + // } + + class RemoteRepositoryLoginDialog extends TitleAreaDialog { + private static final long serialVersionUID = 2234006887750103399L; + private Text name; + private Text uri; + private Text username; + private Text password; + private Button saveInKeyring; + + public RemoteRepositoryLoginDialog(Shell parentShell) { + super(parentShell); + } + + protected Point getInitialSize() { + return new Point(600, 400); + } + + protected Control createDialogArea(Composite parent) { + Composite dialogarea = (Composite) super.createDialogArea(parent); + dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + Composite composite = new Composite(dialogarea, SWT.NONE); + composite.setLayout(new GridLayout(2, false)); + composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + setMessage("Login to remote repository", IMessageProvider.NONE); + name = createLT(composite, "Name", "remoteRepository"); + uri = createLT(composite, "URI", "http://localhost:7070/jcr/node"); + username = createLT(composite, "User", ""); + password = createLP(composite, "Password"); + + saveInKeyring = createLC(composite, "Remember password", false); + parent.pack(); + return composite; + } + + @Override + protected void createButtonsForButtonBar(Composite parent) { + super.createButtonsForButtonBar(parent); + Button test = createButton(parent, 2, "Test", false); + test.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = -1829962269440419560L; + + public void widgetSelected(SelectionEvent arg0) { + testConnection(); + } + }); + } + + void testConnection() { + Session session = null; + try { + URI checkedUri = new URI(uri.getText()); + String checkedUriStr = checkedUri.toString(); + + Hashtable params = new Hashtable(); + params.put(CmsConstants.LABELED_URI, checkedUriStr); + Repository repository = repositoryFactory.getRepository(params); + if (username.getText().trim().equals("")) {// anonymous + // FIXME make it more generic + session = repository.login(CmsConstants.SYS_WORKSPACE); + } else { + // FIXME use getTextChars() when upgrading to 3.7 + // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=297412 + char[] pwd = password.getText().toCharArray(); + SimpleCredentials sc = new SimpleCredentials(username.getText(), pwd); + session = repository.login(sc, "main"); + MessageDialog.openInformation(getParentShell(), "Success", + "Connection to '" + uri.getText() + "' successful"); + } + } catch (Exception e) { + ErrorFeedback.show("Connection test failed for " + uri.getText(), e); + } finally { + JcrUtils.logoutQuietly(session); + } + } + + @Override + protected void okPressed() { + Session nodeSession = null; + try { + nodeSession = nodeRepository.login(); + Node home = CmsJcrUtils.getUserHome(nodeSession); + + Node remote = home.hasNode(ArgeoNames.ARGEO_REMOTE) ? home.getNode(ArgeoNames.ARGEO_REMOTE) + : home.addNode(ArgeoNames.ARGEO_REMOTE); + if (remote.hasNode(name.getText())) + throw new EclipseUiException("There is already a remote repository named " + name.getText()); + Node remoteRepository = remote.addNode(name.getText(), ArgeoTypes.ARGEO_REMOTE_REPOSITORY); + remoteRepository.setProperty(ArgeoNames.ARGEO_URI, uri.getText()); + remoteRepository.setProperty(ArgeoNames.ARGEO_USER_ID, username.getText()); + nodeSession.save(); + if (saveInKeyring.getSelection()) { + String pwdPath = remoteRepository.getPath() + '/' + ArgeoNames.ARGEO_PASSWORD; + keyring.set(pwdPath, password.getText().toCharArray()); + } + nodeSession.save(); + MessageDialog.openInformation(getParentShell(), "Repository Added", + "Remote repository '" + username.getText() + "@" + uri.getText() + "' added"); + + super.okPressed(); + } catch (Exception e) { + ErrorFeedback.show("Cannot add remote repository", e); + } finally { + JcrUtils.logoutQuietly(nodeSession); + } + } + + /** Creates label and text. */ + protected Text createLT(Composite parent, String label, String initial) { + new Label(parent, SWT.NONE).setText(label); + Text text = new Text(parent, SWT.SINGLE | SWT.LEAD | SWT.BORDER); + text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + text.setText(initial); + return text; + } + + /** Creates label and check. */ + protected Button createLC(Composite parent, String label, Boolean initial) { + new Label(parent, SWT.NONE).setText(label); + Button check = new Button(parent, SWT.CHECK); + check.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + check.setSelection(initial); + return check; + } + + protected Text createLP(Composite parent, String label) { + new Label(parent, SWT.NONE).setText(label); + Text text = new Text(parent, SWT.SINGLE | SWT.LEAD | SWT.BORDER | SWT.PASSWORD); + text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + return text; + } + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/DeleteNodes.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/DeleteNodes.java new file mode 100644 index 000000000..b8de06b46 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/DeleteNodes.java @@ -0,0 +1,95 @@ +package org.argeo.cms.e4.jcr.handlers; + +import java.util.List; + +import javax.inject.Named; +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.e4.jcr.JcrBrowserView; +import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; +import org.argeo.cms.ui.jcr.model.WorkspaceElem; +import org.argeo.cms.ux.widgets.TreeParent; +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.eclipse.ui.dialogs.ErrorFeedback; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.services.IServiceConstants; +import org.eclipse.e4.ui.workbench.modeling.ESelectionService; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.swt.widgets.Display; + +/** + * Delete the selected nodes: both in the JCR repository and in the UI view. + * Warning no check is done, except implementation dependent native checks, + * handle with care. + * + * This handler is still 'hard linked' to a GenericJcrBrowser view to enable + * correct tree refresh when a node is added. This must be corrected in future + * versions. + */ +public class DeleteNodes { + @Execute + public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) { + List selection = (List) selectionService.getSelection(); + if (selection == null) + return; + + JcrBrowserView view = (JcrBrowserView) part.getObject(); + + // confirmation + StringBuffer buf = new StringBuffer(""); + for (Object o : selection) { + SingleJcrNodeElem sjn = (SingleJcrNodeElem) o; + buf.append(sjn.getName()).append(' '); + } + Boolean doRemove = MessageDialog.openConfirm(Display.getCurrent().getActiveShell(), "Confirm deletion", + "Do you want to delete " + buf + "?"); + + // operation + if (doRemove) { + SingleJcrNodeElem ancestor = null; + WorkspaceElem rootAncestor = null; + try { + for (Object obj : selection) { + if (obj instanceof SingleJcrNodeElem) { + // Cache objects + SingleJcrNodeElem sjn = (SingleJcrNodeElem) obj; + TreeParent tp = (TreeParent) sjn.getParent(); + Node node = sjn.getNode(); + + // Jcr Remove + node.remove(); + node.getSession().save(); + // UI remove + tp.removeChild(sjn); + + // Check if the parent is the root node + if (tp instanceof WorkspaceElem) + rootAncestor = (WorkspaceElem) tp; + else + ancestor = getOlder(ancestor, (SingleJcrNodeElem) tp); + } + } + if (rootAncestor != null) + view.nodeRemoved(rootAncestor); + else if (ancestor != null) + view.nodeRemoved(ancestor); + } catch (Exception e) { + ErrorFeedback.show("Cannot delete selected node ", e); + } + } + } + + private SingleJcrNodeElem getOlder(SingleJcrNodeElem A, SingleJcrNodeElem B) { + try { + if (A == null) + return B == null ? null : B; + // Todo enhanced this method + else + return A.getNode().getDepth() <= B.getNode().getDepth() ? A : B; + } catch (RepositoryException re) { + throw new EclipseUiException("Cannot find ancestor", re); + } + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/Refresh.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/Refresh.java new file mode 100644 index 000000000..036e70ac1 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/Refresh.java @@ -0,0 +1,43 @@ +package org.argeo.cms.e4.jcr.handlers; + +import java.util.List; + +import javax.inject.Named; + +import org.argeo.cms.e4.jcr.JcrBrowserView; +import org.argeo.cms.ui.jcr.JcrBrowserUtils; +import org.argeo.cms.ux.widgets.TreeParent; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.services.IServiceConstants; +import org.eclipse.e4.ui.workbench.modeling.EPartService; +import org.eclipse.e4.ui.workbench.modeling.ESelectionService; + +/** + * Force the selected objects of the active view to be refreshed doing the + * following: + *
    + *
  1. The model objects are recomputed
  2. + *
  3. the view is refreshed
  4. + *
+ */ +public class Refresh { + + @Execute + public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, EPartService partService, + ESelectionService selectionService) { + + JcrBrowserView view = (JcrBrowserView) part.getObject(); + List selection = (List) selectionService.getSelection(); + + if (selection != null && !selection.isEmpty()) { + for (Object obj : selection) + if (obj instanceof TreeParent) { + TreeParent tp = (TreeParent) obj; + JcrBrowserUtils.forceRefreshIfNeeded(tp); + view.refresh(obj); + } + } else if (view instanceof JcrBrowserView) + view.refresh(null); // force full refresh + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/RenameNode.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/RenameNode.java new file mode 100644 index 000000000..97674abc2 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/RenameNode.java @@ -0,0 +1,59 @@ +package org.argeo.cms.e4.jcr.handlers; + +import java.util.List; + +import javax.inject.Named; +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.argeo.cms.e4.jcr.JcrBrowserView; +import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.eclipse.ui.dialogs.SingleValue; +import org.argeo.jcr.JcrUtils; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.services.IServiceConstants; +import org.eclipse.e4.ui.workbench.modeling.EPartService; +import org.eclipse.e4.ui.workbench.modeling.ESelectionService; + +/** + * Canonically call JCR Session#move(String, String) on the first element + * returned by HandlerUtil#getActiveWorkbenchWindow() + * (...getActivePage().getSelection()), if it is a {@link SingleJcrNodeElem}. + * The user must then fill a new name in and confirm + */ +public class RenameNode { + @Execute + public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, EPartService partService, + ESelectionService selectionService) { + List selection = (List) selectionService.getSelection(); + if (selection == null || selection.size() != 1) + return; + JcrBrowserView view = (JcrBrowserView) part.getObject(); + + Object element = selection.get(0); + if (element instanceof SingleJcrNodeElem) { + SingleJcrNodeElem sjn = (SingleJcrNodeElem) element; + Node node = sjn.getNode(); + Session session = null; + String newName = null; + String oldPath = null; + try { + newName = SingleValue.ask("New node name", "Please provide a new name for [" + node.getName() + "]"); + // TODO sanity check and user feedback + newName = JcrUtils.replaceInvalidChars(newName); + oldPath = node.getPath(); + session = node.getSession(); + session.move(oldPath, JcrUtils.parentPath(oldPath) + "/" + newName); + session.save(); + + // Manually refresh the browser view. Must be enhanced + view.refresh(sjn); + } catch (RepositoryException e) { + throw new EclipseUiException("Unable to rename " + node + " to " + newName, e); + } + } + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/package-info.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/package-info.java new file mode 100644 index 000000000..4e075e2b1 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/package-info.java @@ -0,0 +1,2 @@ +/** JCR browser handlers. */ +package org.argeo.cms.e4.jcr.handlers; \ No newline at end of file diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/package-info.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/package-info.java new file mode 100644 index 000000000..3e92fb04d --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/package-info.java @@ -0,0 +1,2 @@ +/** JCR browser perspective. */ +package org.argeo.cms.e4.jcr; \ No newline at end of file diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/AbstractOsgiComposite.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/AbstractOsgiComposite.java new file mode 100644 index 000000000..4fd1d68dc --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/AbstractOsgiComposite.java @@ -0,0 +1,41 @@ +package org.argeo.cms.e4.maintenance; + +import java.util.Collection; + +import org.argeo.api.cms.CmsLog; +import org.argeo.cms.swt.CmsSwtUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; + +abstract class AbstractOsgiComposite extends Composite { + private static final long serialVersionUID = -4097415973477517137L; + protected final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext(); + protected final CmsLog log = CmsLog.getLog(getClass()); + + public AbstractOsgiComposite(Composite parent, int style) { + super(parent, style); + parent.setLayout(CmsSwtUtils.noSpaceGridLayout()); + setLayout(CmsSwtUtils.noSpaceGridLayout()); + setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); + initUi(style); + } + + protected abstract void initUi(int style); + + protected T getService(Class clazz) { + return bc.getService(bc.getServiceReference(clazz)); + } + + protected Collection> getServiceReferences(Class clazz, String filter) { + try { + return bc.getServiceReferences(clazz, filter); + } catch (InvalidSyntaxException e) { + throw new IllegalArgumentException("Filter " + filter + " is invalid", e); + } + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/Browse.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/Browse.java new file mode 100644 index 000000000..a536da066 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/Browse.java @@ -0,0 +1,576 @@ +package org.argeo.cms.e4.maintenance; + +import static org.eclipse.swt.SWT.RIGHT; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.LinkedHashMap; + +import javax.jcr.ItemNotFoundException; +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.Property; +import javax.jcr.PropertyIterator; +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; +import javax.jcr.Value; + +import org.argeo.api.cms.ux.Cms2DSize; +import org.argeo.cms.swt.CmsException; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ui.CmsUiProvider; +import org.argeo.cms.ui.util.CmsLink; +import org.argeo.cms.ui.widgets.EditableImage; +import org.argeo.cms.ui.widgets.Img; +import org.argeo.jcr.JcrUtils; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.jface.viewers.ILazyContentProvider; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TableViewerColumn; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.events.ControlAdapter; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.swt.widgets.Text; + +public class Browse implements CmsUiProvider { + + // Some local constants to experiment. should be cleaned + private final static String BROWSE_PREFIX = "browse#"; + private final static int THUMBNAIL_WIDTH = 400; + private final static int COLUMN_WIDTH = 160; + private DateFormat timeFormatter = new SimpleDateFormat("dd-MM-yyyy', 'HH:mm"); + + // keep a cache of the opened nodes + // Key is the path + private LinkedHashMap browserCols = new LinkedHashMap(); + private Composite nodeDisplayParent; + private Composite colViewer; + private ScrolledComposite scrolledCmp; + private Text parentPathTxt; + private Text filterTxt; + private Node currEdited; + + private String initialPath; + + @Override + public Control createUi(Composite parent, Node context) throws RepositoryException { + if (context == null) + // return null; + throw new CmsException("Context cannot be null"); + GridLayout layout = CmsSwtUtils.noSpaceGridLayout(); + layout.numColumns = 2; + parent.setLayout(layout); + + // Left + Composite leftCmp = new Composite(parent, SWT.NO_FOCUS); + leftCmp.setLayoutData(CmsSwtUtils.fillAll()); + createBrowserPart(leftCmp, context); + + // Right + nodeDisplayParent = new Composite(parent, SWT.NO_FOCUS | SWT.BORDER); + GridData gd = new GridData(SWT.RIGHT, SWT.FILL, false, true); + gd.widthHint = THUMBNAIL_WIDTH; + nodeDisplayParent.setLayoutData(gd); + createNodeView(nodeDisplayParent, context); + + // INIT + setEdited(context); + initialPath = context.getPath(); + + // Workaround we don't yet manage the delete to display parent of the + // initial context node + + return null; + } + + private void createBrowserPart(Composite parent, Node context) throws RepositoryException { + GridLayout layout = CmsSwtUtils.noSpaceGridLayout(); + parent.setLayout(layout); + Composite filterCmp = new Composite(parent, SWT.NO_FOCUS); + filterCmp.setLayoutData(CmsSwtUtils.fillWidth()); + + // top filter + addFilterPanel(filterCmp); + + // scrolled composite + scrolledCmp = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.BORDER | SWT.NO_FOCUS); + scrolledCmp.setLayoutData(CmsSwtUtils.fillAll()); + scrolledCmp.setExpandVertical(true); + scrolledCmp.setExpandHorizontal(true); + scrolledCmp.setShowFocusedControl(true); + + colViewer = new Composite(scrolledCmp, SWT.NO_FOCUS); + scrolledCmp.setContent(colViewer); + scrolledCmp.addControlListener(new ControlAdapter() { + private static final long serialVersionUID = 6589392045145698201L; + + @Override + public void controlResized(ControlEvent e) { + Rectangle r = scrolledCmp.getClientArea(); + scrolledCmp.setMinSize(colViewer.computeSize(SWT.DEFAULT, r.height)); + } + }); + initExplorer(colViewer, context); + } + + private Control initExplorer(Composite parent, Node context) throws RepositoryException { + parent.setLayout(CmsSwtUtils.noSpaceGridLayout()); + createBrowserColumn(parent, context); + return null; + } + + private Control createBrowserColumn(Composite parent, Node context) throws RepositoryException { + // TODO style is not correctly managed. + FilterEntitiesVirtualTable table = new FilterEntitiesVirtualTable(parent, SWT.BORDER | SWT.NO_FOCUS, context); + // CmsUiUtils.style(table, ArgeoOrgStyle.browserColumn.style()); + table.filterList("*"); + table.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, false, true)); + browserCols.put(context.getPath(), table); + return null; + } + + public void addFilterPanel(Composite parent) { + + parent.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(2, false))); + + // Text Area for the filter + parentPathTxt = new Text(parent, SWT.NO_FOCUS); + parentPathTxt.setEditable(false); + filterTxt = new Text(parent, SWT.SEARCH | SWT.ICON_CANCEL); + filterTxt.setMessage("Filter current list"); + filterTxt.setLayoutData(CmsSwtUtils.fillWidth()); + filterTxt.addModifyListener(new ModifyListener() { + private static final long serialVersionUID = 7709303319740056286L; + + public void modifyText(ModifyEvent event) { + modifyFilter(false); + } + }); + + filterTxt.addKeyListener(new KeyListener() { + private static final long serialVersionUID = -4523394262771183968L; + + @Override + public void keyReleased(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + boolean shiftPressed = (e.stateMask & SWT.SHIFT) != 0; + // boolean altPressed = (e.stateMask & SWT.ALT) != 0; + FilterEntitiesVirtualTable currTable = null; + if (currEdited != null) { + FilterEntitiesVirtualTable table = browserCols.get(getPath(currEdited)); + if (table != null && !table.isDisposed()) + currTable = table; + } + + try { + if (e.keyCode == SWT.ARROW_DOWN) + currTable.setFocus(); + else if (e.keyCode == SWT.BS) { + if (filterTxt.getText().equals("") + && !(getPath(currEdited).equals("/") || getPath(currEdited).equals(initialPath))) { + setEdited(currEdited.getParent()); + e.doit = false; + filterTxt.setFocus(); + } + } else if (e.keyCode == SWT.TAB && !shiftPressed) { + if (currEdited.getNodes(filterTxt.getText() + "*").getSize() == 1) { + setEdited(currEdited.getNodes(filterTxt.getText() + "*").nextNode()); + } + filterTxt.setFocus(); + e.doit = false; + } + } catch (RepositoryException e1) { + throw new CmsException("Unexpected error in key management for " + currEdited + "with filter " + + filterTxt.getText(), e1); + } + + } + }); + } + + private void setEdited(Node node) { + try { + currEdited = node; + CmsSwtUtils.clear(nodeDisplayParent); + createNodeView(nodeDisplayParent, currEdited); + nodeDisplayParent.layout(); + refreshFilters(node); + refreshBrowser(node); + } catch (RepositoryException re) { + throw new CmsException("Unable to update browser for " + node, re); + } + } + + private void refreshFilters(Node node) throws RepositoryException { + String currNodePath = node.getPath(); + parentPathTxt.setText(currNodePath); + filterTxt.setText(""); + filterTxt.getParent().layout(); + } + + private void refreshBrowser(Node node) throws RepositoryException { + + // Retrieve + String currNodePath = node.getPath(); + String currParPath = ""; + if (!"/".equals(currNodePath)) + currParPath = JcrUtils.parentPath(currNodePath); + if ("".equals(currParPath)) + currParPath = "/"; + + Object[][] colMatrix = new Object[browserCols.size()][2]; + + int i = 0, j = -1, k = -1; + for (String path : browserCols.keySet()) { + colMatrix[i][0] = path; + colMatrix[i][1] = browserCols.get(path); + if (j >= 0 && k < 0 && !currNodePath.equals("/")) { + boolean leaveOpened = path.startsWith(currNodePath); + + // workaround for same name siblings + // fix me weird side effect when we go left or click on anb + // already selected, unfocused node + if (leaveOpened && (path.lastIndexOf("/") == 0 && currNodePath.lastIndexOf("/") == 0 + || JcrUtils.parentPath(path).equals(JcrUtils.parentPath(currNodePath)))) + leaveOpened = JcrUtils.lastPathElement(path).equals(JcrUtils.lastPathElement(currNodePath)); + + if (!leaveOpened) + k = i; + } + if (currParPath.equals(path)) + j = i; + i++; + } + + if (j >= 0 && k >= 0) + // remove useless cols + for (int l = i - 1; l >= k; l--) { + browserCols.remove(colMatrix[l][0]); + ((FilterEntitiesVirtualTable) colMatrix[l][1]).dispose(); + } + + // Remove disposed columns + // TODO investigate and fix the mechanism that leave them there after + // disposal + if (browserCols.containsKey(currNodePath)) { + FilterEntitiesVirtualTable currCol = browserCols.get(currNodePath); + if (currCol.isDisposed()) + browserCols.remove(currNodePath); + } + + if (!browserCols.containsKey(currNodePath)) + createBrowserColumn(colViewer, node); + + colViewer.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(browserCols.size(), false))); + // colViewer.pack(); + colViewer.layout(); + // also resize the scrolled composite + scrolledCmp.layout(); + scrolledCmp.getShowFocusedControl(); + // colViewer.getParent().layout(); + // if (JcrUtils.parentPath(currNodePath).equals(currBrowserKey)) { + // } else { + // } + } + + private void modifyFilter(boolean fromOutside) { + if (!fromOutside) + if (currEdited != null) { + String filter = filterTxt.getText() + "*"; + FilterEntitiesVirtualTable table = browserCols.get(getPath(currEdited)); + if (table != null && !table.isDisposed()) + table.filterList(filter); + } + + } + + private String getPath(Node node) { + try { + return node.getPath(); + } catch (RepositoryException e) { + throw new CmsException("Unable to get path for node " + node, e); + } + } + + private Cms2DSize imageWidth = new Cms2DSize(250, 0); + + /** + * Recreates the content of the box that displays information about the current + * selected node. + */ + private Control createNodeView(Composite parent, Node context) throws RepositoryException { + + parent.setLayout(new GridLayout(2, false)); + + if (isImg(context)) { + EditableImage image = new Img(parent, RIGHT, context, imageWidth); + image.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false, 2, 1)); + } + + // Name and primary type + Label contextL = new Label(parent, SWT.NONE); + CmsSwtUtils.markup(contextL); + contextL.setText("" + context.getName() + ""); + new Label(parent, SWT.NONE).setText(context.getPrimaryNodeType().getName()); + + // Children + for (NodeIterator nIt = context.getNodes(); nIt.hasNext();) { + Node child = nIt.nextNode(); + new CmsLink(child.getName(), BROWSE_PREFIX + child.getPath()).createUi(parent, context); + new Label(parent, SWT.NONE).setText(child.getPrimaryNodeType().getName()); + } + + // Properties + for (PropertyIterator pIt = context.getProperties(); pIt.hasNext();) { + Property property = pIt.nextProperty(); + Label label = new Label(parent, SWT.NONE); + label.setText(property.getName()); + label.setToolTipText(JcrUtils.getPropertyDefinitionAsString(property)); + new Label(parent, SWT.NONE).setText(getPropAsString(property)); + } + + return null; + } + + private boolean isImg(Node node) throws RepositoryException { + // TODO support images + return false; +// return node.hasNode(JCR_CONTENT) && node.isNodeType(CmsTypes.CMS_IMAGE); + } + + private String getPropAsString(Property property) throws RepositoryException { + String result = ""; + if (property.isMultiple()) { + result = getMultiAsString(property, ", "); + } else { + Value value = property.getValue(); + if (value.getType() == PropertyType.BINARY) + result = ""; + else if (value.getType() == PropertyType.DATE) + result = timeFormatter.format(value.getDate().getTime()); + else + result = value.getString(); + } + return result; + } + + private String getMultiAsString(Property property, String separator) throws RepositoryException { + if (separator == null) + separator = "; "; + Value[] values = property.getValues(); + StringBuilder builder = new StringBuilder(); + for (Value val : values) { + String currStr = val.getString(); + if (!"".equals(currStr.trim())) + builder.append(currStr).append(separator); + } + if (builder.lastIndexOf(separator) >= 0) + return builder.substring(0, builder.length() - separator.length()); + else + return builder.toString(); + } + + /** Almost canonical implementation of a table that display entities */ + private class FilterEntitiesVirtualTable extends Composite { + private static final long serialVersionUID = 8798147431706283824L; + + // Context + private Node context; + + // UI Objects + private TableViewer entityViewer; + + // enable management of multiple columns + Node getNode() { + return context; + } + + @Override + public boolean setFocus() { + if (entityViewer.getTable().isDisposed()) + return false; + if (entityViewer.getSelection().isEmpty()) { + Object first = entityViewer.getElementAt(0); + if (first != null) { + entityViewer.setSelection(new StructuredSelection(first), true); + } + } + return entityViewer.getTable().setFocus(); + } + + void filterList(String filter) { + try { + NodeIterator nit = context.getNodes(filter); + refreshFilteredList(nit); + } catch (RepositoryException e) { + throw new CmsException("Unable to filter " + getNode() + " children with filter " + filter, e); + } + + } + + public FilterEntitiesVirtualTable(Composite parent, int style, Node context) { + super(parent, SWT.NO_FOCUS); + this.context = context; + populate(); + } + + protected void populate() { + Composite parent = this; + GridLayout layout = CmsSwtUtils.noSpaceGridLayout(); + + this.setLayout(layout); + createTableViewer(parent); + } + + private void createTableViewer(final Composite parent) { + // the list + // We must limit the size of the table otherwise the full list is + // loaded + // before the layout happens + Composite listCmp = new Composite(parent, SWT.NO_FOCUS); + GridData gd = new GridData(SWT.LEFT, SWT.FILL, false, true); + gd.widthHint = COLUMN_WIDTH; + listCmp.setLayoutData(gd); + listCmp.setLayout(CmsSwtUtils.noSpaceGridLayout()); + + entityViewer = new TableViewer(listCmp, SWT.VIRTUAL | SWT.SINGLE); + Table table = entityViewer.getTable(); + + table.setLayoutData(CmsSwtUtils.fillAll()); + table.setLinesVisible(true); + table.setHeaderVisible(false); + CmsSwtUtils.markup(table); + + CmsSwtUtils.style(table, MaintenanceStyles.BROWSER_COLUMN); + + // first column + TableViewerColumn column = new TableViewerColumn(entityViewer, SWT.NONE); + TableColumn tcol = column.getColumn(); + tcol.setWidth(COLUMN_WIDTH); + tcol.setResizable(true); + column.setLabelProvider(new SimpleNameLP()); + + entityViewer.setContentProvider(new MyLazyCP(entityViewer)); + entityViewer.addSelectionChangedListener(new ISelectionChangedListener() { + + @Override + public void selectionChanged(SelectionChangedEvent event) { + IStructuredSelection selection = (IStructuredSelection) entityViewer.getSelection(); + if (selection.isEmpty()) + return; + else + setEdited((Node) selection.getFirstElement()); + + } + }); + + table.addKeyListener(new KeyListener() { + private static final long serialVersionUID = -330694313896036230L; + + @Override + public void keyReleased(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + + IStructuredSelection selection = (IStructuredSelection) entityViewer.getSelection(); + Node selected = null; + if (!selection.isEmpty()) + selected = ((Node) selection.getFirstElement()); + try { + if (e.keyCode == SWT.ARROW_RIGHT) { + if (selected != null) { + setEdited(selected); + browserCols.get(selected.getPath()).setFocus(); + } + } else if (e.keyCode == SWT.ARROW_LEFT) { + try { + selected = getNode().getParent(); + String newPath = selected.getPath(); // getNode().getParent() + setEdited(selected); + if (browserCols.containsKey(newPath)) + browserCols.get(newPath).setFocus(); + } catch (ItemNotFoundException ie) { + // root silent + } + } + } catch (RepositoryException ie) { + throw new CmsException("Error while managing arrow " + "events in the browser for " + selected, + ie); + } + } + }); + } + + private class MyLazyCP implements ILazyContentProvider { + private static final long serialVersionUID = 1L; + private TableViewer viewer; + private Object[] elements; + + public MyLazyCP(TableViewer viewer) { + this.viewer = viewer; + } + + public void dispose() { + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // IMPORTANT: don't forget this: an exception will be thrown if + // a selected object is not part of the results anymore. + viewer.setSelection(null); + this.elements = (Object[]) newInput; + } + + public void updateElement(int index) { + viewer.replace(elements[index], index); + } + } + + protected void refreshFilteredList(NodeIterator children) { + Object[] rows = JcrUtils.nodeIteratorToList(children).toArray(); + entityViewer.setInput(rows); + entityViewer.setItemCount(rows.length); + entityViewer.refresh(); + } + + public class SimpleNameLP extends ColumnLabelProvider { + private static final long serialVersionUID = 2465059387875338553L; + + @Override + public String getText(Object element) { + if (element instanceof Node) { + Node curr = ((Node) element); + try { + return curr.getName(); + } catch (RepositoryException e) { + throw new CmsException("Unable to get name for" + curr); + } + } + return super.getText(element); + } + } + } +} \ No newline at end of file diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/ConnectivityDeploymentUi.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/ConnectivityDeploymentUi.java new file mode 100644 index 000000000..97f3e6713 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/ConnectivityDeploymentUi.java @@ -0,0 +1,48 @@ +package org.argeo.cms.e4.maintenance; + +import org.argeo.cms.swt.CmsSwtUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.osgi.framework.ServiceReference; +import org.osgi.service.http.HttpService; +import org.osgi.service.useradmin.UserAdmin; + +class ConnectivityDeploymentUi extends AbstractOsgiComposite { + private static final long serialVersionUID = 590221539553514693L; + + public ConnectivityDeploymentUi(Composite parent, int style) { + super(parent, style); + } + + @Override + protected void initUi(int style) { + StringBuffer text = new StringBuffer(); + text.append("Provided Servers
"); + + ServiceReference userAdminRef = bc.getServiceReference(HttpService.class); + if (userAdminRef != null) { + // FIXME use constants + Object httpPort = userAdminRef.getProperty("http.port"); + Object httpsPort = userAdminRef.getProperty("https.port"); + if (httpPort != null) + text.append("http ").append(httpPort).append("
"); + if (httpsPort != null) + text.append("https ").append(httpsPort).append("
"); + + } + + text.append("
"); + text.append("Referenced Servers
"); + + Label label = new Label(this, SWT.NONE); + label.setData(new GridData(SWT.FILL, SWT.FILL, false, false)); + CmsSwtUtils.markup(label); + label.setText(text.toString()); + } + + protected boolean isDeployed() { + return bc.getServiceReference(UserAdmin.class) != null; + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DataDeploymentUi.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DataDeploymentUi.java new file mode 100644 index 000000000..ef95bde64 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DataDeploymentUi.java @@ -0,0 +1,139 @@ +package org.argeo.cms.e4.maintenance; + +import java.io.File; +import java.io.IOException; +import java.nio.file.FileStore; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collection; + +import org.apache.jackrabbit.core.RepositoryContext; +import org.apache.jackrabbit.core.config.RepositoryConfig; +import org.argeo.api.cms.CmsConstants; +import org.argeo.cms.swt.CmsSwtUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.osgi.framework.ServiceReference; + +class DataDeploymentUi extends AbstractOsgiComposite { + private static final long serialVersionUID = 590221539553514693L; + + public DataDeploymentUi(Composite parent, int style) { + super(parent, style); + } + + @Override + protected void initUi(int style) { + if (isDeployed()) { + initCurrentUi(this); + } else { + initNewUi(this); + } + } + + private void initNewUi(Composite parent) { +// try { +// ConfigurationAdmin confAdmin = bc.getService(bc.getServiceReference(ConfigurationAdmin.class)); +// Configuration[] confs = confAdmin.listConfigurations( +// "(" + ConfigurationAdmin.SERVICE_FACTORYPID + "=" + NodeConstants.NODE_REPOS_FACTORY_PID + ")"); +// if (confs == null || confs.length == 0) { +// Group buttonGroup = new Group(parent, SWT.NONE); +// buttonGroup.setText("Repository Type"); +// buttonGroup.setLayout(new GridLayout(2, true)); +// buttonGroup.setLayoutData(new GridData(GridData.FILL_VERTICAL)); +// +// SelectionListener selectionListener = new SelectionAdapter() { +// private static final long serialVersionUID = 6247064348421088092L; +// +// public void widgetSelected(SelectionEvent event) { +// Button radio = (Button) event.widget; +// if (!radio.getSelection()) +// return; +// log.debug(event); +// JackrabbitType nodeType = (JackrabbitType) radio.getData(); +// if (log.isDebugEnabled()) +// log.debug(" selected = " + nodeType.name()); +// }; +// }; +// +// for (JackrabbitType nodeType : JackrabbitType.values()) { +// Button radio = new Button(buttonGroup, SWT.RADIO); +// radio.setText(nodeType.name()); +// radio.setData(nodeType); +// if (nodeType.equals(JackrabbitType.localfs)) +// radio.setSelection(true); +// radio.addSelectionListener(selectionListener); +// } +// +// } else if (confs.length == 1) { +// +// } else { +// throw new CmsException("Multiple repos not yet supported"); +// } +// } catch (Exception e) { +// throw new CmsException("Cannot initialize UI", e); +// } + + } + + private void initCurrentUi(Composite parent) { + parent.setLayout(new GridLayout()); + Collection> contexts = getServiceReferences(RepositoryContext.class, + "(" + CmsConstants.CN + "=*)"); + StringBuffer text = new StringBuffer(); + text.append("Jackrabbit Repositories
"); + for (ServiceReference sr : contexts) { + RepositoryContext repositoryContext = bc.getService(sr); + String alias = sr.getProperty(CmsConstants.CN).toString(); + String rootNodeId = repositoryContext.getRootNodeId().toString(); + RepositoryConfig repositoryConfig = repositoryContext.getRepositoryConfig(); + Path repoHomePath = new File(repositoryConfig.getHomeDir()).toPath().toAbsolutePath(); + // TODO check data store + + text.append("" + alias + "
"); + text.append("rootNodeId: " + rootNodeId + "
"); + try { + FileStore fileStore = Files.getFileStore(repoHomePath); + text.append("partition: " + fileStore.toString() + "
"); + text.append( + percentUsed(fileStore) + " used (" + humanReadable(fileStore.getUsableSpace()) + " free)
"); + } catch (IOException e) { + log.error("Cannot check fileStore for " + repoHomePath, e); + } + } + Label label = new Label(parent, SWT.NONE); + label.setData(new GridData(SWT.FILL, SWT.FILL, false, false)); + CmsSwtUtils.markup(label); + label.setText("" + text.toString() + ""); + } + + private String humanReadable(long bytes) { + long mb = bytes / (1024 * 1024); + return mb >= 2048 ? Long.toString(mb / 1024) + " GB" : Long.toString(mb) + " MB"; + } + + private String percentUsed(FileStore fs) throws IOException { + long used = fs.getTotalSpace() - fs.getUnallocatedSpace(); + long percent = used * 100 / fs.getTotalSpace(); + if (log.isTraceEnabled()) { + // output identical to `df -B 1`) + log.trace(fs.getTotalSpace() + "," + used + "," + fs.getUsableSpace()); + } + String span; + if (percent < 80) + span = ""; + else if (percent < 95) + span = ""; + else + span = ""; + return span + percent + "%"; + } + + protected boolean isDeployed() { + return bc.getServiceReference(RepositoryContext.class) != null; + } + +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DeploymentEntryPoint.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DeploymentEntryPoint.java new file mode 100644 index 000000000..e713f53e1 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DeploymentEntryPoint.java @@ -0,0 +1,96 @@ +package org.argeo.cms.e4.maintenance; + +import java.util.GregorianCalendar; +import java.util.TimeZone; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.api.cms.CmsContext; +import org.argeo.api.cms.CmsDeployment; +import org.argeo.api.cms.CmsState; +import org.argeo.cms.swt.CmsSwtUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceReference; + +class DeploymentEntryPoint { + private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext(); + + protected void createContents(Composite parent) { + // FIXME manage authentication if needed + // if (!CurrentUser.roles().contains(AuthConstants.ROLE_ADMIN)) + // return; + + // parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + if (isDesktop()) { + parent.setLayout(new GridLayout(2, true)); + } else { + // TODO add scrolling + parent.setLayout(new GridLayout(1, true)); + } + + initHighLevelSummary(parent); + + Group securityGroup = createHighLevelGroup(parent, "Security"); + securityGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); + new SecurityDeploymentUi(securityGroup, SWT.NONE); + + Group dataGroup = createHighLevelGroup(parent, "Data"); + dataGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); + new DataDeploymentUi(dataGroup, SWT.NONE); + + Group logGroup = createHighLevelGroup(parent, "Notifications"); + logGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true)); + new LogDeploymentUi(logGroup, SWT.NONE); + + Group connectivityGroup = createHighLevelGroup(parent, "Connectivity"); + new ConnectivityDeploymentUi(connectivityGroup, SWT.NONE); + connectivityGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true)); + + } + + private void initHighLevelSummary(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false); + if (isDesktop()) + gridData.horizontalSpan = 3; + composite.setLayoutData(gridData); + composite.setLayout(new FillLayout()); + + ServiceReference nodeStateRef = bc.getServiceReference(CmsState.class); + if (nodeStateRef == null) + throw new IllegalStateException("No CMS state available"); + CmsState nodeState = bc.getService(nodeStateRef); + ServiceReference nodeDeploymentRef = bc.getServiceReference(CmsContext.class); + Label label = new Label(composite, SWT.WRAP); + CmsSwtUtils.markup(label); + if (nodeDeploymentRef == null) { + label.setText("Not yet deployed on
" + nodeState.getHostname() + "
, please configure below."); + } else { + Object stateUuid = nodeStateRef.getProperty(CmsConstants.CN); + CmsContext nodeDeployment = bc.getService(nodeDeploymentRef); + GregorianCalendar calendar = new GregorianCalendar(); + calendar.setTimeInMillis(nodeDeployment.getAvailableSince()); + calendar.setTimeZone(TimeZone.getDefault()); + label.setText("[" + "" + nodeState.getHostname() + "]# " + "Deployment state " + stateUuid + + ", available since " + calendar.getTime() + ""); + } + } + + private static Group createHighLevelGroup(Composite parent, String text) { + Group group = new Group(parent, SWT.NONE); + group.setText(text); + CmsSwtUtils.markup(group); + return group; + } + + private boolean isDesktop() { + return true; + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/LogDeploymentUi.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/LogDeploymentUi.java new file mode 100644 index 000000000..fa5d3dae3 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/LogDeploymentUi.java @@ -0,0 +1,73 @@ +package org.argeo.cms.e4.maintenance; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Enumeration; +import java.util.GregorianCalendar; +import java.util.TimeZone; + +import org.argeo.cms.swt.CmsSwtUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Text; +import org.osgi.service.log.LogEntry; +import org.osgi.service.log.LogListener; +import org.osgi.service.log.LogReaderService; + +class LogDeploymentUi extends AbstractOsgiComposite implements LogListener { + private static final long serialVersionUID = 590221539553514693L; + + private DateFormat dateFormat = new SimpleDateFormat("MMdd HH:mm"); + + private Display display; + private Text logDisplay; + + public LogDeploymentUi(Composite parent, int style) { + super(parent, style); + } + + @Override + protected void initUi(int style) { + LogReaderService logReader = getService(LogReaderService.class); + // FIXME use server push + // logReader.addLogListener(this); + this.display = getDisplay(); + this.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + logDisplay = new Text(this, SWT.WRAP | SWT.MULTI | SWT.READ_ONLY); + logDisplay.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + CmsSwtUtils.markup(logDisplay); + Enumeration logEntries = (Enumeration) logReader.getLog(); + while (logEntries.hasMoreElements()) + logDisplay.append(printEntry(logEntries.nextElement())); + } + + private String printEntry(LogEntry entry) { + StringBuilder sb = new StringBuilder(); + GregorianCalendar calendar = new GregorianCalendar(TimeZone.getDefault()); + calendar.setTimeInMillis(entry.getTime()); + sb.append(dateFormat.format(calendar.getTime())).append(' '); + sb.append(entry.getMessage()); + sb.append('\n'); + return sb.toString(); + } + + @Override + public void logged(LogEntry entry) { + if (display.isDisposed()) + return; + display.asyncExec(() -> { + if (logDisplay.isDisposed()) + return; + logDisplay.append(printEntry(entry)); + }); + display.wake(); + } + + // @Override + // public void dispose() { + // super.dispose(); + // getService(LogReaderService.class).removeLogListener(this); + // } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/MaintenanceStyles.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/MaintenanceStyles.java new file mode 100644 index 000000000..df1be51ad --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/MaintenanceStyles.java @@ -0,0 +1,10 @@ +package org.argeo.cms.e4.maintenance; + +/** Specific styles used by the various maintenance pages . */ +public interface MaintenanceStyles { + // General + public final static String PREFIX = "maintenance_"; + + // Browser + public final static String BROWSER_COLUMN = "browser_column"; + } diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/NonAdminPage.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/NonAdminPage.java new file mode 100644 index 000000000..cb38ce899 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/NonAdminPage.java @@ -0,0 +1,30 @@ +package org.argeo.cms.e4.maintenance; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ui.CmsUiProvider; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; + +public class NonAdminPage implements CmsUiProvider{ + + @Override + public Control createUi(Composite parent, Node context) + throws RepositoryException { + Composite body = new Composite(parent, SWT.NO_FOCUS); + body.setLayoutData(CmsSwtUtils.fillAll()); + body.setLayout(new GridLayout()); + Label label = new Label(body, SWT.NONE); + label.setText("You should be an admin to perform maintenance operations. " + + "Are you sure you are logged in?"); + label.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true)); + return null; + } + +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/SecurityDeploymentUi.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/SecurityDeploymentUi.java new file mode 100644 index 000000000..3492c5499 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/SecurityDeploymentUi.java @@ -0,0 +1,85 @@ +package org.argeo.cms.e4.maintenance; + +import java.net.URI; + +import org.argeo.cms.swt.CmsSwtUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.UserAdmin; + +class SecurityDeploymentUi extends AbstractOsgiComposite { + private static final long serialVersionUID = 590221539553514693L; + + public SecurityDeploymentUi(Composite parent, int style) { + super(parent, style); + } + + @Override + protected void initUi(int style) { + if (isDeployed()) { + initCurrentUi(this); + } else { + initNewUi(this); + } + } + + private void initNewUi(Composite parent) { + new Label(parent, SWT.NONE).setText("Security is not configured"); + } + + private void initCurrentUi(Composite parent) { + ServiceReference userAdminRef = bc.getServiceReference(UserAdmin.class); + UserAdmin userAdmin = bc.getService(userAdminRef); + StringBuffer text = new StringBuffer(); + text.append("Domains
"); + domains: for (String key : userAdminRef.getPropertyKeys()) { + if (!key.startsWith("/")) + continue domains; + URI uri; + try { + uri = new URI(key); + } catch (Exception e) { + // ignore non URI keys + continue domains; + } + + String rootDn = uri.getPath().substring(1, uri.getPath().length()); + // FIXME make reading query options more robust, using utils + boolean readOnly = uri.getQuery().equals("readOnly=true"); + if (readOnly) + text.append(""); + else + text.append(""); + + text.append(rootDn); + text.append("
"); + try { + Role[] roles = userAdmin.getRoles("(dn=*," + rootDn + ")"); + long userCount = 0; + long groupCount = 0; + for (Role role : roles) { + if (role.getType() == Role.USER) + userCount++; + else + groupCount++; + } + text.append(" " + userCount + " users, " + groupCount +" groups.
"); + } catch (InvalidSyntaxException e) { + log.error("Invalid syntax", e); + } + } + Label label = new Label(parent, SWT.NONE); + label.setData(new GridData(SWT.FILL, SWT.FILL, false, false)); + CmsSwtUtils.markup(label); + label.setText(text.toString()); + } + + protected boolean isDeployed() { + return bc.getServiceReference(UserAdmin.class) != null; + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/package-info.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/package-info.java new file mode 100644 index 000000000..e4d2ad4c3 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/package-info.java @@ -0,0 +1,2 @@ +/** Maintenance perspective. */ +package org.argeo.cms.e4.maintenance; \ No newline at end of file diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/BundleNode.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/BundleNode.java new file mode 100644 index 000000000..e9536830f --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/BundleNode.java @@ -0,0 +1,46 @@ +package org.argeo.cms.e4.monitoring; + +import org.argeo.cms.ux.widgets.TreeParent; +import org.osgi.framework.Bundle; +import org.osgi.framework.ServiceReference; + +/** A tree element representing a {@link Bundle} */ +class BundleNode extends TreeParent { + private final Bundle bundle; + + public BundleNode(Bundle bundle) { + this(bundle, false); + } + + @SuppressWarnings("rawtypes") + public BundleNode(Bundle bundle, boolean hasChildren) { + super(bundle.getSymbolicName()); + this.bundle = bundle; + + if (hasChildren) { + // REFERENCES + ServiceReference[] usedServices = bundle.getServicesInUse(); + if (usedServices != null) { + for (ServiceReference sr : usedServices) { + if (sr != null) + addChild(new ServiceReferenceNode(sr, false)); + } + } + + // SERVICES + ServiceReference[] registeredServices = bundle + .getRegisteredServices(); + if (registeredServices != null) { + for (ServiceReference sr : registeredServices) { + if (sr != null) + addChild(new ServiceReferenceNode(sr, true)); + } + } + } + + } + + Bundle getBundle() { + return bundle; + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/BundlesView.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/BundlesView.java new file mode 100644 index 000000000..c639255b6 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/BundlesView.java @@ -0,0 +1,114 @@ +//package org.argeo.eclipse.ui.workbench.osgi; +//public class BundlesView {} + +package org.argeo.cms.e4.monitoring; + +import javax.annotation.PostConstruct; + +import org.argeo.eclipse.ui.ColumnViewerComparator; +import org.argeo.eclipse.ui.specific.EclipseUiSpecificUtils; +import org.eclipse.e4.ui.di.Focus; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TableViewerColumn; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; + +/** + * Overview of the bundles as a table. Equivalent to Equinox 'ss' console + * command. + */ +public class BundlesView { + private final static BundleContext bc = FrameworkUtil.getBundle(BundlesView.class).getBundleContext(); + private TableViewer viewer; + + @PostConstruct + public void createPartControl(Composite parent) { + viewer = new TableViewer(parent); + viewer.setContentProvider(new BundleContentProvider()); + viewer.getTable().setHeaderVisible(true); + + EclipseUiSpecificUtils.enableToolTipSupport(viewer); + + // ID + TableViewerColumn column = new TableViewerColumn(viewer, SWT.NONE); + column.getColumn().setWidth(30); + column.getColumn().setText("ID"); + column.getColumn().setAlignment(SWT.RIGHT); + column.setLabelProvider(new ColumnLabelProvider() { + private static final long serialVersionUID = -3122136344359358605L; + + public String getText(Object element) { + return Long.toString(((Bundle) element).getBundleId()); + } + }); + new ColumnViewerComparator(column); + + // State + column = new TableViewerColumn(viewer, SWT.NONE); + column.getColumn().setWidth(18); + column.getColumn().setText("State"); + column.setLabelProvider(new StateLabelProvider()); + new ColumnViewerComparator(column); + + // Symbolic name + column = new TableViewerColumn(viewer, SWT.NONE); + column.getColumn().setWidth(250); + column.getColumn().setText("Symbolic Name"); + column.setLabelProvider(new ColumnLabelProvider() { + private static final long serialVersionUID = -4280840684440451080L; + + public String getText(Object element) { + return ((Bundle) element).getSymbolicName(); + } + }); + new ColumnViewerComparator(column); + + // Version + column = new TableViewerColumn(viewer, SWT.NONE); + column.getColumn().setWidth(250); + column.getColumn().setText("Version"); + column.setLabelProvider(new ColumnLabelProvider() { + private static final long serialVersionUID = 6871926308708629989L; + + public String getText(Object element) { + Bundle bundle = (org.osgi.framework.Bundle) element; + return bundle.getVersion().toString(); + } + }); + new ColumnViewerComparator(column); + + viewer.setInput(bc); + + } + + @Focus + public void setFocus() { + if (viewer != null) + viewer.getControl().setFocus(); + } + + /** Content provider managing the array of bundles */ + private static class BundleContentProvider implements IStructuredContentProvider { + private static final long serialVersionUID = -8533792785725875977L; + + public Object[] getElements(Object inputElement) { + if (inputElement instanceof BundleContext) { + BundleContext bc = (BundleContext) inputElement; + return bc.getBundles(); + } + return null; + } + + public void dispose() { + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/CmsSessionsView.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/CmsSessionsView.java new file mode 100644 index 000000000..95b1eb2cb --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/CmsSessionsView.java @@ -0,0 +1,173 @@ +//package org.argeo.eclipse.ui.workbench.osgi; +//public class BundlesView {} + +package org.argeo.cms.e4.monitoring; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.annotation.PostConstruct; + +import org.argeo.api.cms.CmsSession; +import org.argeo.cms.auth.RoleNameUtils; +import org.argeo.eclipse.ui.ColumnViewerComparator; +import org.argeo.eclipse.ui.specific.EclipseUiSpecificUtils; +import org.argeo.util.LangUtils; +import org.eclipse.e4.ui.di.Focus; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TableViewerColumn; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; + +/** + * Overview of the active CMS sessions. + */ +public class CmsSessionsView { + private final static BundleContext bc = FrameworkUtil.getBundle(CmsSessionsView.class).getBundleContext(); + + private TableViewer viewer; + + @PostConstruct + public void createPartControl(Composite parent) { + viewer = new TableViewer(parent); + viewer.setContentProvider(new CmsSessionContentProvider()); + viewer.getTable().setHeaderVisible(true); + + EclipseUiSpecificUtils.enableToolTipSupport(viewer); + + int longColWidth = 150; + int smallColWidth = 100; + + // Display name + TableViewerColumn column = new TableViewerColumn(viewer, SWT.NONE); + column.getColumn().setWidth(longColWidth); + column.getColumn().setText("User"); + column.setLabelProvider(new ColumnLabelProvider() { + private static final long serialVersionUID = -5234573509093747505L; + + public String getText(Object element) { + return ((CmsSession) element).getDisplayName(); + } + + public String getToolTipText(Object element) { + return ((CmsSession) element).getUserDn().toString(); + } + }); + new ColumnViewerComparator(column); + + // Creation time + column = new TableViewerColumn(viewer, SWT.NONE); + column.getColumn().setWidth(smallColWidth); + column.getColumn().setText("Since"); + column.setLabelProvider(new ColumnLabelProvider() { + private static final long serialVersionUID = -5234573509093747505L; + + public String getText(Object element) { + return LangUtils.since(((CmsSession) element).getCreationTime()); + } + + public String getToolTipText(Object element) { + return ((CmsSession) element).getCreationTime().toString(); + } + }); + new ColumnViewerComparator(column); + + // Username + column = new TableViewerColumn(viewer, SWT.NONE); + column.getColumn().setWidth(smallColWidth); + column.getColumn().setText("Username"); + column.setLabelProvider(new ColumnLabelProvider() { + private static final long serialVersionUID = -5234573509093747505L; + + public String getText(Object element) { + String userDn = ((CmsSession) element).getUserDn(); + return RoleNameUtils.getLastRdnValue(userDn); + } + + public String getToolTipText(Object element) { + return ((CmsSession) element).getUserDn().toString(); + } + }); + new ColumnViewerComparator(column); + + // UUID + column = new TableViewerColumn(viewer, SWT.NONE); + column.getColumn().setWidth(smallColWidth); + column.getColumn().setText("UUID"); + column.setLabelProvider(new ColumnLabelProvider() { + private static final long serialVersionUID = -5234573509093747505L; + + public String getText(Object element) { + return ((CmsSession) element).getUuid().toString(); + } + + public String getToolTipText(Object element) { + return getText(element); + } + }); + new ColumnViewerComparator(column); + + // Local ID + column = new TableViewerColumn(viewer, SWT.NONE); + column.getColumn().setWidth(smallColWidth); + column.getColumn().setText("Local ID"); + column.setLabelProvider(new ColumnLabelProvider() { + private static final long serialVersionUID = -5234573509093747505L; + + public String getText(Object element) { + return ((CmsSession) element).getLocalId(); + } + + public String getToolTipText(Object element) { + return getText(element); + } + }); + new ColumnViewerComparator(column); + + viewer.setInput(bc); + + } + + @Focus + public void setFocus() { + if (viewer != null) + viewer.getControl().setFocus(); + } + + /** Content provider managing the array of bundles */ + private static class CmsSessionContentProvider implements IStructuredContentProvider { + private static final long serialVersionUID = -8533792785725875977L; + + public Object[] getElements(Object inputElement) { + if (inputElement instanceof BundleContext) { + BundleContext bc = (BundleContext) inputElement; + Collection> srs; + try { + srs = bc.getServiceReferences(CmsSession.class, null); + } catch (InvalidSyntaxException e) { + throw new IllegalArgumentException("Cannot retrieve CMS sessions", e); + } + List res = new ArrayList<>(); + for (ServiceReference sr : srs) { + res.add(bc.getService(sr)); + } + return res.toArray(); + } + return null; + } + + public void dispose() { + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/ModulesView.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/ModulesView.java new file mode 100644 index 000000000..6317882c4 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/ModulesView.java @@ -0,0 +1,91 @@ +package org.argeo.cms.e4.monitoring; + +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.PostConstruct; + +import org.argeo.cms.ux.widgets.TreeParent; +import org.eclipse.e4.ui.di.Focus; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; + +/** The OSGi runtime from a module perspective. */ +public class ModulesView { + private final static BundleContext bc = FrameworkUtil.getBundle(ModulesView.class).getBundleContext(); + private TreeViewer viewer; + + @PostConstruct + public void createPartControl(Composite parent) { + viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); + viewer.setContentProvider(new ModulesContentProvider()); + viewer.setLabelProvider(new ModulesLabelProvider()); + viewer.setInput(bc); + } + + @Focus + public void setFocus() { + viewer.getTree().setFocus(); + } + + private class ModulesContentProvider implements ITreeContentProvider { + private static final long serialVersionUID = 3819934804640641721L; + + public Object[] getElements(Object inputElement) { + return getChildren(inputElement); + } + + public Object[] getChildren(Object parentElement) { + if (parentElement instanceof BundleContext) { + BundleContext bundleContext = (BundleContext) parentElement; + Bundle[] bundles = bundleContext.getBundles(); + + List modules = new ArrayList(); + for (Bundle bundle : bundles) { + if (bundle.getState() == Bundle.ACTIVE) + modules.add(new BundleNode(bundle, true)); + } + return modules.toArray(); + } else if (parentElement instanceof TreeParent) { + return ((TreeParent) parentElement).getChildren(); + } else { + return null; + } + } + + public Object getParent(Object element) { + // TODO Auto-generated method stub + return null; + } + + public boolean hasChildren(Object element) { + if (element instanceof TreeParent) { + return ((TreeParent) element).hasChildren(); + } + return false; + } + + public void dispose() { + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + } + + private class ModulesLabelProvider extends StateLabelProvider { + private static final long serialVersionUID = 5290046145534824722L; + + @Override + public String getText(Object element) { + if (element instanceof BundleNode) + return element.toString() + " [" + ((BundleNode) element).getBundle().getBundleId() + "]"; + return element.toString(); + } + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/OsgiConfigurationsView.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/OsgiConfigurationsView.java new file mode 100644 index 000000000..5db8bd151 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/OsgiConfigurationsView.java @@ -0,0 +1,163 @@ +package org.argeo.cms.e4.monitoring; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Dictionary; +import java.util.List; + +import javax.annotation.PostConstruct; + +import org.argeo.cms.swt.CmsException; +import org.argeo.util.LangUtils; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.TreeViewerColumn; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Composite; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.service.cm.Configuration; +import org.osgi.service.cm.ConfigurationAdmin; + +public class OsgiConfigurationsView { + private final static BundleContext bc = FrameworkUtil.getBundle(OsgiConfigurationsView.class).getBundleContext(); + + @PostConstruct + public void createPartControl(Composite parent) { + ConfigurationAdmin configurationAdmin = bc.getService(bc.getServiceReference(ConfigurationAdmin.class)); + + TreeViewer viewer = new TreeViewer(parent); + // viewer.getTree().setHeaderVisible(true); + + TreeViewerColumn tvc = new TreeViewerColumn(viewer, SWT.NONE); + tvc.getColumn().setWidth(400); + tvc.setLabelProvider(new ColumnLabelProvider() { + private static final long serialVersionUID = 835407996597566763L; + + @Override + public String getText(Object element) { + if (element instanceof Configuration) { + return ((Configuration) element).getPid(); + } else if (element instanceof Prop) { + return ((Prop) element).key; + } + return super.getText(element); + } + + @Override + public Image getImage(Object element) { + if (element instanceof Configuration) + return OsgiExplorerImages.CONFIGURATION; + return null; + } + + }); + + tvc = new TreeViewerColumn(viewer, SWT.NONE); + tvc.getColumn().setWidth(400); + tvc.setLabelProvider(new ColumnLabelProvider() { + private static final long serialVersionUID = 6999659261190014687L; + + @Override + public String getText(Object element) { + if (element instanceof Configuration) { + // return ((Configuration) element).getFactoryPid(); + return null; + } else if (element instanceof Prop) { + return ((Prop) element).value.toString(); + } + return super.getText(element); + } + }); + + viewer.setContentProvider(new ConfigurationsContentProvider()); + viewer.setInput(configurationAdmin); + } + + static class ConfigurationsContentProvider implements ITreeContentProvider { + private static final long serialVersionUID = -4892768279440981042L; + private ConfigurationComparator configurationComparator = new ConfigurationComparator(); + + @Override + public void dispose() { + } + + @Override + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + + @Override + public Object[] getElements(Object inputElement) { + ConfigurationAdmin configurationAdmin = (ConfigurationAdmin) inputElement; + try { + Configuration[] configurations = configurationAdmin.listConfigurations(null); + Arrays.sort(configurations, configurationComparator); + return configurations; + } catch (IOException | InvalidSyntaxException e) { + throw new CmsException("Cannot list configurations", e); + } + } + + @Override + public Object[] getChildren(Object parentElement) { + if (parentElement instanceof Configuration) { + List res = new ArrayList<>(); + Configuration configuration = (Configuration) parentElement; + Dictionary props = configuration.getProperties(); + keys: for (String key : LangUtils.keys(props)) { + if (Constants.SERVICE_PID.equals(key)) + continue keys; + if (ConfigurationAdmin.SERVICE_FACTORYPID.equals(key)) + continue keys; + res.add(new Prop(configuration, key, props.get(key))); + } + return res.toArray(new Prop[res.size()]); + } + return null; + } + + @Override + public Object getParent(Object element) { + if (element instanceof Prop) + return ((Prop) element).configuration; + return null; + } + + @Override + public boolean hasChildren(Object element) { + if (element instanceof Configuration) + return true; + return false; + } + + } + + static class Prop { + final Configuration configuration; + final String key; + final Object value; + + public Prop(Configuration configuration, String key, Object value) { + this.configuration = configuration; + this.key = key; + this.value = value; + } + + } + + static class ConfigurationComparator implements Comparator { + + @Override + public int compare(Configuration o1, Configuration o2) { + return o1.getPid().compareTo(o2.getPid()); + } + + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/OsgiExplorerImages.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/OsgiExplorerImages.java new file mode 100644 index 000000000..7217fe612 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/OsgiExplorerImages.java @@ -0,0 +1,15 @@ +package org.argeo.cms.e4.monitoring; + +import org.argeo.cms.ui.theme.CmsImages; +import org.eclipse.swt.graphics.Image; + +/** Shared icons. */ +public class OsgiExplorerImages extends CmsImages { + public final static Image INSTALLED = createIcon("installed.gif"); + public final static Image RESOLVED = createIcon("resolved.gif"); + public final static Image STARTING = createIcon("starting.gif"); + public final static Image ACTIVE = createIcon("active.gif"); + public final static Image SERVICE_PUBLISHED = createIcon("service_published.gif"); + public final static Image SERVICE_REFERENCED = createIcon("service_referenced.gif"); + public final static Image CONFIGURATION = createIcon("node.gif"); +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/ServiceReferenceNode.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/ServiceReferenceNode.java new file mode 100644 index 000000000..1c60811d2 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/ServiceReferenceNode.java @@ -0,0 +1,46 @@ +package org.argeo.cms.e4.monitoring; + +import org.argeo.cms.ux.widgets.TreeParent; +import org.osgi.framework.Bundle; +import org.osgi.framework.ServiceReference; + +/** A tree element representing a {@link ServiceReference} */ +@SuppressWarnings({ "rawtypes" }) +class ServiceReferenceNode extends TreeParent { + private final ServiceReference serviceReference; + private final boolean published; + + public ServiceReferenceNode(ServiceReference serviceReference, + boolean published) { + super(serviceReference.toString()); + this.serviceReference = serviceReference; + this.published = published; + + if (isPublished()) { + Bundle[] usedBundles = serviceReference.getUsingBundles(); + if (usedBundles != null) { + for (Bundle b : usedBundles) { + if (b != null) + addChild(new BundleNode(b)); + } + } + } else { + Bundle provider = serviceReference.getBundle(); + addChild(new BundleNode(provider)); + } + + for (String key : serviceReference.getPropertyKeys()) { + addChild(new TreeParent(key + "=" + + serviceReference.getProperty(key))); + } + + } + + public ServiceReference getServiceReference() { + return serviceReference; + } + + public boolean isPublished() { + return published; + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/StateLabelProvider.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/StateLabelProvider.java new file mode 100644 index 000000000..5cb5b6563 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/StateLabelProvider.java @@ -0,0 +1,82 @@ +package org.argeo.cms.e4.monitoring; + +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.swt.graphics.Image; +import org.osgi.framework.Bundle; +import org.osgi.framework.Constants; + +/** Label provider showing the sate of bundles */ +class StateLabelProvider extends ColumnLabelProvider { + private static final long serialVersionUID = -7885583135316000733L; + + @Override + public Image getImage(Object element) { + int state; + if (element instanceof Bundle) + state = ((Bundle) element).getState(); + else if (element instanceof BundleNode) + state = ((BundleNode) element).getBundle().getState(); + else if (element instanceof ServiceReferenceNode) + if (((ServiceReferenceNode) element).isPublished()) + return OsgiExplorerImages.SERVICE_PUBLISHED; + else + return OsgiExplorerImages.SERVICE_REFERENCED; + else + return null; + + switch (state) { + case Bundle.UNINSTALLED: + return OsgiExplorerImages.INSTALLED; + case Bundle.INSTALLED: + return OsgiExplorerImages.INSTALLED; + case Bundle.RESOLVED: + return OsgiExplorerImages.RESOLVED; + case Bundle.STARTING: + return OsgiExplorerImages.STARTING; + case Bundle.STOPPING: + return OsgiExplorerImages.STARTING; + case Bundle.ACTIVE: + return OsgiExplorerImages.ACTIVE; + default: + return null; + } + } + + @Override + public String getText(Object element) { + return null; + } + + @Override + public String getToolTipText(Object element) { + Bundle bundle = (Bundle) element; + Integer state = bundle.getState(); + switch (state) { + case Bundle.UNINSTALLED: + return "UNINSTALLED"; + case Bundle.INSTALLED: + return "INSTALLED"; + case Bundle.RESOLVED: + return "RESOLVED"; + case Bundle.STARTING: + String activationPolicy = bundle.getHeaders() + .get(Constants.BUNDLE_ACTIVATIONPOLICY).toString(); + + // .get("Bundle-ActivationPolicy").toString(); + // FIXME constant triggers the compilation failure + if (activationPolicy != null + && activationPolicy.equals(Constants.ACTIVATION_LAZY)) + // && activationPolicy.equals("lazy")) + // FIXME constant triggers the compilation failure + // && activationPolicy.equals(Constants.ACTIVATION_LAZY)) + return "<>"; + return "STARTING"; + case Bundle.STOPPING: + return "STOPPING"; + case Bundle.ACTIVE: + return "ACTIVE"; + default: + return null; + } + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/package-info.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/package-info.java new file mode 100644 index 000000000..873bf3118 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/package-info.java @@ -0,0 +1,2 @@ +/** Monitoring perspective. */ +package org.argeo.cms.e4.monitoring; \ No newline at end of file diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/package-info.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/package-info.java new file mode 100644 index 000000000..233119c0d --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/package-info.java @@ -0,0 +1,2 @@ +/** Eclipse 4 user interfaces. */ +package org.argeo.cms.e4; \ No newline at end of file diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/parts/EgoDashboard.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/parts/EgoDashboard.java new file mode 100644 index 000000000..f2a73f210 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/parts/EgoDashboard.java @@ -0,0 +1,41 @@ +package org.argeo.cms.e4.parts; + +import java.time.ZonedDateTime; + +import javax.annotation.PostConstruct; + +import org.argeo.api.cms.CmsSession; +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.swt.CmsSwtUtils; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; + +/** A canonical view of the logged in user. */ +public class EgoDashboard { +// private BundleContext bc = FrameworkUtil.getBundle(EgoDashboard.class).getBundleContext(); + + @PostConstruct + public void createPartControl(Composite p) { + p.setLayout(new GridLayout()); + String username = CurrentUser.getUsername(); + + CmsSwtUtils.lbl(p, "" + CurrentUser.getDisplayName() + ""); + CmsSwtUtils.txt(p, username); + CmsSwtUtils.lbl(p, "Roles:"); + roles: for (String role : CurrentUser.roles()) { + if (username.equals(role)) + continue roles; + CmsSwtUtils.txt(p, role); + } + +// Subject subject = Subject.getSubject(AccessController.getContext()); +// if (subject != null) { + CmsSession cmsSession = CurrentUser.getCmsSession(); + ZonedDateTime loggedIndSince = cmsSession.getCreationTime(); + CmsSwtUtils.lbl(p, "Session:"); + CmsSwtUtils.txt(p, cmsSession.getUuid().toString()); + CmsSwtUtils.lbl(p, "Logged in since:"); + CmsSwtUtils.txt(p, loggedIndSince.toString()); +// } + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/AbstractRoleEditor.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/AbstractRoleEditor.java new file mode 100644 index 000000000..137f76242 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/AbstractRoleEditor.java @@ -0,0 +1,287 @@ +package org.argeo.cms.e4.users; + +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.inject.Inject; + +import org.argeo.cms.auth.UserAdminUtils; +import org.argeo.cms.ui.eclipse.forms.AbstractFormPart; +import org.argeo.cms.ui.eclipse.forms.IManagedForm; +import org.argeo.cms.ui.eclipse.forms.ManagedForm; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.util.naming.LdapAttrs; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.e4.ui.di.Persist; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.osgi.service.useradmin.Authorization; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.User; +import org.osgi.service.useradmin.UserAdmin; +import org.osgi.service.useradmin.UserAdminEvent; + +/** Editor for a user, might be a user or a group. */ +public abstract class AbstractRoleEditor { + + // public final static String USER_EDITOR_ID = WorkbenchUiPlugin.PLUGIN_ID + + // ".userEditor"; + // public final static String GROUP_EDITOR_ID = WorkbenchUiPlugin.PLUGIN_ID + + // ".groupEditor"; + + /* DEPENDENCY INJECTION */ + @Inject + protected UserAdminWrapper userAdminWrapper; + + @Inject + private MPart mPart; + + // @Inject + // Composite parent; + + private UserAdmin userAdmin; + + // Context + private User user; + private String username; + + private NameChangeListener listener; + + private ManagedForm managedForm; + + // public void init(IEditorSite site, IEditorInput input) throws + // PartInitException { + @PostConstruct + public void init(Composite parent) { + this.userAdmin = userAdminWrapper.getUserAdmin(); + username = mPart.getPersistedState().get(LdapAttrs.uid.name()); + user = (User) userAdmin.getRole(username); + + listener = new NameChangeListener(Display.getCurrent()); + userAdminWrapper.addListener(listener); + updateEditorTitle(null); + + managedForm = new ManagedForm(parent) { + + @Override + public void staleStateChanged() { + refresh(); + } + }; + ScrolledComposite scrolled = managedForm.getForm(); + Composite body = new Composite(scrolled, SWT.NONE); + scrolled.setContent(body); + createUi(body); + managedForm.refresh(); + } + + abstract void createUi(Composite parent); + + /** + * returns the list of all authorizations for the given user or of the current + * displayed user if parameter is null + */ + protected List getFlatGroups(User aUser) { + Authorization currAuth; + if (aUser == null) + currAuth = userAdmin.getAuthorization(this.user); + else + currAuth = userAdmin.getAuthorization(aUser); + + String[] roles = currAuth.getRoles(); + + List groups = new ArrayList(); + for (String roleStr : roles) { + User currRole = (User) userAdmin.getRole(roleStr); + if (currRole != null && !groups.contains(currRole)) + groups.add(currRole); + } + return groups; + } + + protected IManagedForm getManagedForm() { + return managedForm; + } + + /** Exposes the user (or group) that is displayed by the current editor */ + protected User getDisplayedUser() { + return user; + } + + private void setDisplayedUser(User user) { + this.user = user; + } + + void updateEditorTitle(String title) { + if (title == null) { + String commonName = UserAdminUtils.getProperty(user, LdapAttrs.cn.name()); + title = "".equals(commonName) ? user.getName() : commonName; + } + setPartName(title); + } + + protected void setPartName(String name) { + mPart.setLabel(name); + } + + // protected void addPages() { + // try { + // if (user.getType() == Role.GROUP) + // addPage(new GroupMainPage(this, userAdminWrapper, repository, nodeInstance)); + // else + // addPage(new UserMainPage(this, userAdminWrapper)); + // } catch (Exception e) { + // throw new CmsException("Cannot add pages", e); + // } + // } + + @Persist + public void doSave(IProgressMonitor monitor) { + userAdminWrapper.beginTransactionIfNeeded(); + commitPages(true); + userAdminWrapper.commitOrNotifyTransactionStateChange(); + // firePropertyChange(PROP_DIRTY); + userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_REMOVED, user)); + } + + protected void commitPages(boolean b) { + managedForm.commit(b); + } + + @PreDestroy + public void dispose() { + userAdminWrapper.removeListener(listener); + managedForm.dispose(); + } + + // CONTROLERS FOR THIS EDITOR AND ITS PAGES + + class NameChangeListener extends UiUserAdminListener { + public NameChangeListener(Display display) { + super(display); + } + + @Override + public void roleChangedToUiThread(UserAdminEvent event) { + Role changedRole = event.getRole(); + if (changedRole == null || changedRole.equals(user)) { + updateEditorTitle(null); + User reloadedUser = (User) userAdminWrapper.getUserAdmin().getRole(user.getName()); + setDisplayedUser(reloadedUser); + } + } + } + + class MainInfoListener extends UiUserAdminListener { + private final AbstractFormPart part; + + public MainInfoListener(Display display, AbstractFormPart part) { + super(display); + this.part = part; + } + + @Override + public void roleChangedToUiThread(UserAdminEvent event) { + // Rollback + if (event.getRole() == null) + part.markStale(); + } + } + + class GroupChangeListener extends UiUserAdminListener { + private final AbstractFormPart part; + + public GroupChangeListener(Display display, AbstractFormPart part) { + super(display); + this.part = part; + } + + @Override + public void roleChangedToUiThread(UserAdminEvent event) { + // always mark as stale + part.markStale(); + } + } + + /** Registers a listener that will notify this part */ + class FormPartML implements ModifyListener { + private static final long serialVersionUID = 6299808129505381333L; + private AbstractFormPart formPart; + + public FormPartML(AbstractFormPart generalPart) { + this.formPart = generalPart; + } + + public void modifyText(ModifyEvent e) { + // Discard event when the control does not have the focus, typically + // to avoid all editors being marked as dirty during a Rollback + if (((Control) e.widget).isFocusControl()) + formPart.markDirty(); + } + } + + /* DEPENDENCY INJECTION */ + public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { + this.userAdminWrapper = userAdminWrapper; + } + + /** Creates label and multiline text. */ + Text createLMT(Composite parent, String label, String value) { + Label lbl = new Label(parent, SWT.NONE); + lbl.setText(label); + lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false)); + Text text = new Text(parent, SWT.NONE); + text.setText(value); + text.setLayoutData(new GridData(SWT.LEAD, SWT.FILL, true, true)); + return text; + } + + /** Creates label and password. */ + Text createLP(Composite parent, String label, String value) { + Label lbl = new Label(parent, SWT.NONE); + lbl.setText(label); + lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false)); + Text text = new Text(parent, SWT.PASSWORD | SWT.BORDER); + text.setText(value); + text.setLayoutData(new GridData(SWT.LEAD, SWT.FILL, true, false)); + return text; + } + + /** Creates label and text. */ + Text createLT(Composite parent, String label, String value) { + Label lbl = new Label(parent, SWT.NONE); + lbl.setText(label); + lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false)); + lbl.setFont(EclipseUiUtils.getBoldFont(parent)); + Text text = new Text(parent, SWT.BORDER); + text.setText(value); + text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + // CmsUiUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT); + return text; + } + + Text createReadOnlyLT(Composite parent, String label, String value) { + Label lbl = new Label(parent, SWT.NONE); + lbl.setText(label); + lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false)); + lbl.setFont(EclipseUiUtils.getBoldFont(parent)); + Text text = new Text(parent, SWT.NONE); + text.setText(value); + text.setLayoutData(new GridData(SWT.LEAD, SWT.FILL, true, false)); + text.setEditable(false); + // CmsUiUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT); + return text; + } + +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/CmsWorkbenchStyles.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/CmsWorkbenchStyles.java new file mode 100644 index 000000000..07df312e1 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/CmsWorkbenchStyles.java @@ -0,0 +1,8 @@ +package org.argeo.cms.e4.users; + +/** Centralize the declaration of Workbench specific CSS Styles */ +interface CmsWorkbenchStyles { + + // Specific People layouting + String WORKBENCH_FORM_TEXT = "workbench_form_text"; +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupEditor.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupEditor.java new file mode 100644 index 000000000..d54f8bc38 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupEditor.java @@ -0,0 +1,566 @@ +package org.argeo.cms.e4.users; + +import static org.argeo.api.cms.CmsContext.WORKGROUP; +import static org.argeo.cms.auth.UserAdminUtils.setProperty; +import static org.argeo.util.naming.LdapAttrs.businessCategory; +import static org.argeo.util.naming.LdapAttrs.description; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.annotation.PreDestroy; +import javax.inject.Inject; +import javax.jcr.Node; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.api.cms.CmsContext; +import org.argeo.cms.auth.UserAdminUtils; +import org.argeo.cms.e4.users.providers.CommonNameLP; +import org.argeo.cms.e4.users.providers.MailLP; +import org.argeo.cms.e4.users.providers.RoleIconLP; +import org.argeo.cms.e4.users.providers.UserFilter; +import org.argeo.cms.jcr.CmsJcrUtils; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ui.eclipse.forms.AbstractFormPart; +import org.argeo.cms.ui.eclipse.forms.IManagedForm; +import org.argeo.eclipse.ui.ColumnDefinition; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.parts.LdifUsersTable; +import org.argeo.jcr.JcrException; +import org.argeo.jcr.JcrUtils; +import org.argeo.util.naming.LdapAttrs; +import org.argeo.util.transaction.WorkTransaction; +import org.eclipse.e4.ui.workbench.modeling.EPartService; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.ToolBarManager; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.ViewerDropAdapter; +import org.eclipse.swt.SWT; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.DropTargetEvent; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.dnd.TransferData; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Link; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.ToolBar; +import org.osgi.service.useradmin.Group; +import org.osgi.service.useradmin.Role; +//import org.eclipse.ui.forms.AbstractFormPart; +//import org.eclipse.ui.forms.IManagedForm; +//import org.eclipse.ui.forms.SectionPart; +//import org.eclipse.ui.forms.editor.FormEditor; +//import org.eclipse.ui.forms.editor.FormPage; +//import org.eclipse.ui.forms.widgets.FormToolkit; +//import org.eclipse.ui.forms.widgets.ScrolledForm; +//import org.eclipse.ui.forms.widgets.Section; +import org.osgi.service.useradmin.User; +import org.osgi.service.useradmin.UserAdmin; +import org.osgi.service.useradmin.UserAdminEvent; + +/** Display/edit main properties of a given group */ +public class GroupEditor extends AbstractRoleEditor { + // final static String ID = "GroupEditor.mainPage"; + + @Inject + private EPartService partService; + + // private final UserEditor editor; + @Inject + private Repository repository; + @Inject + private CmsContext nodeInstance; + // private final UserAdminWrapper userAdminWrapper; + private Session groupsSession; + + // public GroupMainPage(FormEditor editor, UserAdminWrapper userAdminWrapper, + // Repository repository, + // NodeInstance nodeInstance) { + // super(editor, ID, "Main"); + // try { + // session = repository.login(); + // } catch (RepositoryException e) { + // throw new CmsException("Cannot retrieve session of in MainGroupPage + // constructor", e); + // } + // this.editor = (UserEditor) editor; + // this.userAdminWrapper = userAdminWrapper; + // this.nodeInstance = nodeInstance; + // } + + // protected void createFormContent(final IManagedForm mf) { + // ScrolledForm form = mf.getForm(); + // Composite body = form.getBody(); + // GridLayout mainLayout = new GridLayout(); + // body.setLayout(mainLayout); + // Group group = (Group) editor.getDisplayedUser(); + // appendOverviewPart(body, group); + // appendMembersPart(body, group); + // } + + @Override + protected void createUi(Composite parent) { + try { + groupsSession = repository.login(CmsConstants.SRV_WORKSPACE); + } catch (RepositoryException e) { + throw new JcrException("Cannot retrieve session", e); + } + // ScrolledForm form = mf.getForm(); + // Composite body = form.getBody(); + // Composite body = new Composite(parent, SWT.NONE); + Composite body = parent; + GridLayout mainLayout = new GridLayout(); + body.setLayout(mainLayout); + Group group = (Group) getDisplayedUser(); + appendOverviewPart(body, group); + appendMembersPart(body, group); + } + + @PreDestroy + public void dispose() { + JcrUtils.logoutQuietly(groupsSession); + super.dispose(); + } + + /** Creates the general section */ + protected void appendOverviewPart(final Composite parent, final Group group) { + Composite body = new Composite(parent, SWT.NONE); + // GridLayout layout = new GridLayout(5, false); + GridLayout layout = new GridLayout(2, false); + body.setLayout(layout); + body.setLayoutData(CmsSwtUtils.fillWidth()); + + String cn = UserAdminUtils.getProperty(group, LdapAttrs.cn.name()); + createReadOnlyLT(body, "Name", cn); + createReadOnlyLT(body, "DN", group.getName()); + createReadOnlyLT(body, "Domain", UserAdminUtils.getDomainName(group)); + + // Description + Label descLbl = new Label(body, SWT.LEAD); + descLbl.setFont(EclipseUiUtils.getBoldFont(body)); + descLbl.setText("Description"); + descLbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, true, false, 2, 1)); + final Text descTxt = new Text(body, SWT.LEAD | SWT.MULTI | SWT.WRAP | SWT.BORDER); + GridData gd = EclipseUiUtils.fillWidth(); + gd.heightHint = 50; + gd.horizontalSpan = 2; + descTxt.setLayoutData(gd); + + // Mark as workgroup + Link markAsWorkgroupLk = new Link(body, SWT.NONE); + markAsWorkgroupLk.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1)); + + // create form part (controller) + final AbstractFormPart part = new AbstractFormPart() { + + private MainInfoListener listener; + + @Override + public void initialize(IManagedForm form) { + super.initialize(form); + listener = new MainInfoListener(parent.getDisplay(), this); + userAdminWrapper.addListener(listener); + } + + @Override + public void dispose() { + userAdminWrapper.removeListener(listener); + super.dispose(); + } + + public void commit(boolean onSave) { + // group.getProperties().put(LdapAttrs.description.name(), descTxt.getText()); + setProperty(group, description, descTxt.getText()); + super.commit(onSave); + } + + @Override + public void refresh() { + // dnTxt.setText(group.getName()); + // cnTxt.setText(UserAdminUtils.getProperty(group, LdapAttrs.cn.name())); + descTxt.setText(UserAdminUtils.getProperty(group, LdapAttrs.description.name())); + Node workgroupHome = CmsJcrUtils.getGroupHome(groupsSession, cn); + if (workgroupHome == null) + markAsWorkgroupLk.setText("Mark as workgroup"); + else + markAsWorkgroupLk.setText("Configured as workgroup"); + parent.layout(true, true); + super.refresh(); + } + }; + + markAsWorkgroupLk.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = -6439340898096365078L; + + @Override + public void widgetSelected(SelectionEvent e) { + + boolean confirmed = MessageDialog.openConfirm(parent.getShell(), "Mark as workgroup", + "Are you sure you want to mark " + cn + " as being a workgroup? "); + if (confirmed) { + Node workgroupHome = CmsJcrUtils.getGroupHome(groupsSession, cn); + if (workgroupHome != null) + return; // already marked as workgroup, do nothing + else { + // improve transaction management + userAdminWrapper.beginTransactionIfNeeded(); + nodeInstance.createWorkgroup(group.getName()); + setProperty(group, businessCategory, WORKGROUP); + userAdminWrapper.commitOrNotifyTransactionStateChange(); + userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, group)); + part.refresh(); + } + } + } + }); + + ModifyListener defaultListener = new FormPartML(part); + descTxt.addModifyListener(defaultListener); + getManagedForm().addPart(part); + } + + /** Filtered table with members. Has drag and drop ability */ + protected void appendMembersPart(Composite parent, Group group) { + // Section section = tk.createSection(parent, Section.TITLE_BAR); + // section.setText("Members"); + // section.setLayoutData(EclipseUiUtils.fillAll()); + + Composite body = new Composite(parent, SWT.BORDER); + body.setLayout(new GridLayout()); + // section.setClient(body); + body.setLayoutData(EclipseUiUtils.fillAll()); + + // Define the displayed columns + List columnDefs = new ArrayList(); + columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 0, 24)); + columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Name", 150)); + columnDefs.add(new ColumnDefinition(new MailLP(), "Mail", 150)); + // columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", + // 240)); + + // Create and configure the table + LdifUsersTable userViewerCmp = new MyUserTableViewer(body, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL, + userAdminWrapper.getUserAdmin()); + + userViewerCmp.setColumnDefinitions(columnDefs); + userViewerCmp.populate(true, false); + userViewerCmp.setLayoutData(EclipseUiUtils.fillAll()); + + // Controllers + TableViewer userViewer = userViewerCmp.getTableViewer(); + userViewer.addDoubleClickListener(new UserTableDefaultDClickListener(partService)); + int operations = DND.DROP_COPY | DND.DROP_MOVE; + Transfer[] tt = new Transfer[] { TextTransfer.getInstance() }; + userViewer.addDropSupport(operations, tt, + new GroupDropListener(userAdminWrapper, userViewerCmp, (Group) getDisplayedUser())); + + AbstractFormPart part = new GroupMembersPart(userViewerCmp); + getManagedForm().addPart(part); + + // remove button + // addRemoveAbility(toolBarManager, userViewerCmp.getTableViewer(), group); + Action action = new RemoveMembershipAction(userViewer, group, "Remove selected items from this group", + SecurityAdminImages.ICON_REMOVE_DESC); + + ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT); + ToolBar toolBar = toolBarManager.createControl(body); + toolBar.setLayoutData(CmsSwtUtils.fillWidth()); + + toolBarManager.add(action); + toolBarManager.update(true); + + } + + // private LdifUsersTable createMemberPart(Composite parent, Group group) { + // + // // Define the displayed columns + // List columnDefs = new ArrayList(); + // columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 0, 24)); + // columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Name", 150)); + // columnDefs.add(new ColumnDefinition(new MailLP(), "Mail", 150)); + // // columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished + // Name", + // // 240)); + // + // // Create and configure the table + // LdifUsersTable userViewerCmp = new MyUserTableViewer(parent, SWT.MULTI | + // SWT.H_SCROLL | SWT.V_SCROLL, + // userAdminWrapper.getUserAdmin()); + // + // userViewerCmp.setColumnDefinitions(columnDefs); + // userViewerCmp.populate(true, false); + // userViewerCmp.setLayoutData(EclipseUiUtils.fillAll()); + // + // // Controllers + // TableViewer userViewer = userViewerCmp.getTableViewer(); + // userViewer.addDoubleClickListener(new + // UserTableDefaultDClickListener(partService)); + // int operations = DND.DROP_COPY | DND.DROP_MOVE; + // Transfer[] tt = new Transfer[] { TextTransfer.getInstance() }; + // userViewer.addDropSupport(operations, tt, + // new GroupDropListener(userAdminWrapper, userViewerCmp, (Group) + // getDisplayedUser())); + // + // // userViewerCmp.refresh(); + // return userViewerCmp; + // } + + // Local viewers + private class MyUserTableViewer extends LdifUsersTable { + private static final long serialVersionUID = 8467999509931900367L; + + private final UserFilter userFilter; + + public MyUserTableViewer(Composite parent, int style, UserAdmin userAdmin) { + super(parent, style, true); + userFilter = new UserFilter(); + + } + + @Override + protected List listFilteredElements(String filter) { + // reload user and set it in the editor + Group group = (Group) getDisplayedUser(); + Role[] roles = group.getMembers(); + List users = new ArrayList(); + userFilter.setSearchText(filter); + // userFilter.setShowSystemRole(true); + for (Role role : roles) + // if (role.getType() == Role.GROUP) + if (userFilter.select(null, null, role)) + users.add((User) role); + return users; + } + } + + // private void addRemoveAbility(ToolBarManager toolBarManager, TableViewer + // userViewer, Group group) { + // // Section section = sectionPart.getSection(); + // // ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT); + // // ToolBar toolbar = toolBarManager.createControl(parent); + // // ToolBar toolbar = toolBarManager.getControl(); + // // final Cursor handCursor = new Cursor(toolbar.getDisplay(), + // SWT.CURSOR_HAND); + // // toolbar.setCursor(handCursor); + // // toolbar.addDisposeListener(new DisposeListener() { + // // private static final long serialVersionUID = 3882131405820522925L; + // // + // // public void widgetDisposed(DisposeEvent e) { + // // if ((handCursor != null) && (handCursor.isDisposed() == false)) { + // // handCursor.dispose(); + // // } + // // } + // // }); + // + // Action action = new RemoveMembershipAction(userViewer, group, "Remove + // selected items from this group", + // SecurityAdminImages.ICON_REMOVE_DESC); + // toolBarManager.add(action); + // toolBarManager.update(true); + // // section.setTextClient(toolbar); + // } + + private class RemoveMembershipAction extends Action { + private static final long serialVersionUID = -1337713097184522588L; + + private final TableViewer userViewer; + private final Group group; + + RemoveMembershipAction(TableViewer userViewer, Group group, String name, ImageDescriptor img) { + super(name, img); + this.userViewer = userViewer; + this.group = group; + } + + @Override + public void run() { + ISelection selection = userViewer.getSelection(); + if (selection.isEmpty()) + return; + + @SuppressWarnings("unchecked") + Iterator it = ((IStructuredSelection) selection).iterator(); + List users = new ArrayList(); + while (it.hasNext()) { + User currUser = it.next(); + users.add(currUser); + } + + userAdminWrapper.beginTransactionIfNeeded(); + for (User user : users) { + group.removeMember(user); + } + userAdminWrapper.commitOrNotifyTransactionStateChange(); + userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, group)); + } + } + + // LOCAL CONTROLLERS + private class GroupMembersPart extends AbstractFormPart { + private final LdifUsersTable userViewer; + // private final Group group; + + private GroupChangeListener listener; + + public GroupMembersPart(LdifUsersTable userViewer) { + // super(section); + this.userViewer = userViewer; + // this.group = group; + } + + @Override + public void initialize(IManagedForm form) { + super.initialize(form); + listener = new GroupChangeListener(userViewer.getDisplay(), GroupMembersPart.this); + userAdminWrapper.addListener(listener); + } + + @Override + public void dispose() { + userAdminWrapper.removeListener(listener); + super.dispose(); + } + + @Override + public void refresh() { + userViewer.refresh(); + super.refresh(); + } + } + + /** + * Defines this table as being a potential target to add group membership + * (roles) to this group + */ + private class GroupDropListener extends ViewerDropAdapter { + private static final long serialVersionUID = 2893468717831451621L; + + private final UserAdminWrapper userAdminWrapper; + // private final LdifUsersTable myUserViewerCmp; + private final Group myGroup; + + public GroupDropListener(UserAdminWrapper userAdminWrapper, LdifUsersTable userTableViewerCmp, Group group) { + super(userTableViewerCmp.getTableViewer()); + this.userAdminWrapper = userAdminWrapper; + this.myGroup = group; + // this.myUserViewerCmp = userTableViewerCmp; + } + + @Override + public boolean validateDrop(Object target, int operation, TransferData transferType) { + // Target is always OK in a list only view + // TODO check if not a string + boolean validDrop = true; + return validDrop; + } + + @Override + public void drop(DropTargetEvent event) { + // TODO Is there an opportunity to perform the check before? + String newUserName = (String) event.data; + UserAdmin myUserAdmin = userAdminWrapper.getUserAdmin(); + Role role = myUserAdmin.getRole(newUserName); + if (role.getType() == Role.GROUP) { + Group newGroup = (Group) role; + Shell shell = getViewer().getControl().getShell(); + // Sanity checks + if (myGroup == newGroup) { // Equality + MessageDialog.openError(shell, "Forbidden addition ", "A group cannot be a member of itself."); + return; + } + + // Cycle + String myName = myGroup.getName(); + List myMemberships = getFlatGroups(myGroup); + if (myMemberships.contains(newGroup)) { + MessageDialog.openError(shell, "Forbidden addition: cycle", + "Cannot add " + newUserName + " to group " + myName + ". This would create a cycle"); + return; + } + + // Already member + List newGroupMemberships = getFlatGroups(newGroup); + if (newGroupMemberships.contains(myGroup)) { + MessageDialog.openError(shell, "Forbidden addition", + "Cannot add " + newUserName + " to group " + myName + ", this membership already exists"); + return; + } + userAdminWrapper.beginTransactionIfNeeded(); + myGroup.addMember(newGroup); + userAdminWrapper.commitOrNotifyTransactionStateChange(); + userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, myGroup)); + } else if (role.getType() == Role.USER) { + // TODO check if the group is already member of this group + WorkTransaction transaction = userAdminWrapper.beginTransactionIfNeeded(); + User user = (User) role; + myGroup.addMember(user); + if (UserAdminWrapper.COMMIT_ON_SAVE) + try { + transaction.commit(); + } catch (Exception e) { + throw new IllegalStateException( + "Cannot commit transaction " + "after user group membership update", e); + } + userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, myGroup)); + } + super.drop(event); + } + + @Override + public boolean performDrop(Object data) { + // myUserViewerCmp.refresh(); + return true; + } + } + + // LOCAL HELPERS + // private Composite addSection(FormToolkit tk, Composite parent) { + // Section section = tk.createSection(parent, SWT.NO_FOCUS); + // section.setLayoutData(EclipseUiUtils.fillWidth()); + // Composite body = tk.createComposite(section, SWT.WRAP); + // body.setLayoutData(EclipseUiUtils.fillAll()); + // section.setClient(body); + // return body; + // } + + /** Creates label and text. */ + // private Text createLT(Composite parent, String label, String value) { + // FormToolkit toolkit = getManagedForm().getToolkit(); + // Label lbl = toolkit.createLabel(parent, label); + // lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false)); + // lbl.setFont(EclipseUiUtils.getBoldFont(parent)); + // Text text = toolkit.createText(parent, value, SWT.BORDER); + // text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + // CmsUiUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT); + // return text; + // } + // + // Text createReadOnlyLT(Composite parent, String label, String value) { + // FormToolkit toolkit = getManagedForm().getToolkit(); + // Label lbl = toolkit.createLabel(parent, label); + // lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false)); + // lbl.setFont(EclipseUiUtils.getBoldFont(parent)); + // Text text = toolkit.createText(parent, value, SWT.NONE); + // text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + // text.setEditable(false); + // CmsUiUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT); + // return text; + // } + +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupsView.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupsView.java new file mode 100644 index 000000000..3bf48918d --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupsView.java @@ -0,0 +1,251 @@ +package org.argeo.cms.e4.users; + +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.inject.Inject; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.api.cms.CmsLog; +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.e4.users.providers.CommonNameLP; +import org.argeo.cms.e4.users.providers.DomainNameLP; +import org.argeo.cms.e4.users.providers.RoleIconLP; +import org.argeo.cms.e4.users.providers.UserDragListener; +import org.argeo.cms.swt.CmsException; +//import org.argeo.cms.ui.workbench.WorkbenchUiPlugin; +//import org.argeo.cms.ui.workbench.internal.useradmin.UiUserAdminListener; +//import org.argeo.cms.ui.workbench.internal.useradmin.UserAdminWrapper; +//import org.argeo.cms.ui.workbench.internal.useradmin.providers.CommonNameLP; +//import org.argeo.cms.ui.workbench.internal.useradmin.providers.DomainNameLP; +//import org.argeo.cms.ui.workbench.internal.useradmin.providers.RoleIconLP; +//import org.argeo.cms.ui.workbench.internal.useradmin.providers.UserDragListener; +//import org.argeo.cms.ui.workbench.internal.useradmin.providers.UserTableDefaultDClickListener; +import org.argeo.eclipse.ui.ColumnDefinition; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.parts.LdifUsersTable; +import org.argeo.util.naming.LdapAttrs; +import org.argeo.util.naming.LdapObjs; +import org.eclipse.e4.ui.di.Focus; +import org.eclipse.e4.ui.workbench.modeling.EPartService; +import org.eclipse.e4.ui.workbench.modeling.ESelectionService; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +//import org.eclipse.ui.part.ViewPart; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.User; +import org.osgi.service.useradmin.UserAdminEvent; +import org.osgi.service.useradmin.UserAdminListener; + +/** List all groups with filter */ +public class GroupsView { + private final static CmsLog log = CmsLog.getLog(GroupsView.class); + // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".groupsView"; + + @Inject + private EPartService partService; + @Inject + private UserAdminWrapper userAdminWrapper; + + // UI Objects + private LdifUsersTable groupTableViewerCmp; + private TableViewer userViewer; + private List columnDefs = new ArrayList(); + + private UserAdminListener listener; + + @PostConstruct + public void createPartControl(Composite parent, ESelectionService selectionService) { + parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); + + // boolean isAdmin = CurrentUser.isInRole(NodeConstants.ROLE_ADMIN); + + // Define the displayed columns + columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 19)); + columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Name", 150)); + columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 100)); + // Only show technical DN to admin + // if (isAdmin) + // columnDefs.add(new ColumnDefinition(new UserNameLP(), + // "Distinguished Name", 300)); + + // Create and configure the table + groupTableViewerCmp = new MyUserTableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); + + groupTableViewerCmp.setColumnDefinitions(columnDefs); + // if (isAdmin) + // groupTableViewerCmp.populateWithStaticFilters(false, false); + // else + groupTableViewerCmp.populate(true, false); + + groupTableViewerCmp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + // Links + userViewer = groupTableViewerCmp.getTableViewer(); + userViewer.addDoubleClickListener(new UserTableDefaultDClickListener(partService)); + // getViewSite().setSelectionProvider(userViewer); + userViewer.addSelectionChangedListener(new ISelectionChangedListener() { + + @Override + public void selectionChanged(SelectionChangedEvent event) { + IStructuredSelection selection = (IStructuredSelection) event.getSelection(); + selectionService.setSelection(selection.toList()); + } + }); + + // Really? + groupTableViewerCmp.refresh(); + + // Drag and drop + int operations = DND.DROP_COPY | DND.DROP_MOVE; + Transfer[] tt = new Transfer[] { TextTransfer.getInstance() }; + userViewer.addDragSupport(operations, tt, new UserDragListener(userViewer)); + + // // Register a useradmin listener + // listener = new UserAdminListener() { + // @Override + // public void roleChanged(UserAdminEvent event) { + // if (userViewer != null && !userViewer.getTable().isDisposed()) + // refresh(); + // } + // }; + // userAdminWrapper.addListener(listener); + // } + + // Register a useradmin listener + listener = new MyUiUAListener(parent.getDisplay()); + userAdminWrapper.addListener(listener); + } + + private class MyUiUAListener extends UiUserAdminListener { + public MyUiUAListener(Display display) { + super(display); + } + + @Override + public void roleChangedToUiThread(UserAdminEvent event) { + if (userViewer != null && !userViewer.getTable().isDisposed()) + refresh(); + } + } + + private class MyUserTableViewer extends LdifUsersTable { + private static final long serialVersionUID = 8467999509931900367L; + + private boolean showSystemRoles = true; + + private final String[] knownProps = { LdapAttrs.uid.name(), LdapAttrs.cn.name(), LdapAttrs.DN }; + + public MyUserTableViewer(Composite parent, int style) { + super(parent, style); + showSystemRoles = CurrentUser.isInRole(CmsConstants.ROLE_ADMIN); + } + + protected void populateStaticFilters(Composite staticFilterCmp) { + staticFilterCmp.setLayout(new GridLayout()); + final Button showSystemRoleBtn = new Button(staticFilterCmp, SWT.CHECK); + showSystemRoleBtn.setText("Show system roles"); + showSystemRoles = CurrentUser.isInRole(CmsConstants.ROLE_ADMIN); + showSystemRoleBtn.setSelection(showSystemRoles); + + showSystemRoleBtn.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = -7033424592697691676L; + + @Override + public void widgetSelected(SelectionEvent e) { + showSystemRoles = showSystemRoleBtn.getSelection(); + refresh(); + } + + }); + } + + @Override + protected List listFilteredElements(String filter) { + Role[] roles; + try { + StringBuilder builder = new StringBuilder(); + StringBuilder tmpBuilder = new StringBuilder(); + if (EclipseUiUtils.notEmpty(filter)) + for (String prop : knownProps) { + tmpBuilder.append("("); + tmpBuilder.append(prop); + tmpBuilder.append("=*"); + tmpBuilder.append(filter); + tmpBuilder.append("*)"); + } + if (tmpBuilder.length() > 1) { + builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=") + .append(LdapObjs.groupOfNames.name()).append(")"); + // hide tokens + builder.append("(!(").append(LdapAttrs.DN).append("=*").append(CmsConstants.TOKENS_BASEDN) + .append("))"); + + if (!showSystemRoles) + builder.append("(!(").append(LdapAttrs.DN).append("=*").append(CmsConstants.ROLES_BASEDN) + .append("))"); + builder.append("(|"); + builder.append(tmpBuilder.toString()); + builder.append("))"); + } else { + if (!showSystemRoles) + builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=") + .append(LdapObjs.groupOfNames.name()).append(")(!(").append(LdapAttrs.DN).append("=*") + .append(CmsConstants.ROLES_BASEDN).append("))(!(").append(LdapAttrs.DN).append("=*") + .append(CmsConstants.TOKENS_BASEDN).append(")))"); + else + builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=") + .append(LdapObjs.groupOfNames.name()).append(")(!(").append(LdapAttrs.DN).append("=*") + .append(CmsConstants.TOKENS_BASEDN).append(")))"); + + } + roles = userAdminWrapper.getUserAdmin().getRoles(builder.toString()); + } catch (InvalidSyntaxException e) { + throw new CmsException("Unable to get roles with filter: " + filter, e); + } + List users = new ArrayList(); + for (Role role : roles) + if (!users.contains(role)) + users.add((User) role); + else + log.warn("Duplicated role: " + role); + + return users; + } + } + + public void refresh() { + groupTableViewerCmp.refresh(); + } + + @PreDestroy + public void dispose() { + userAdminWrapper.removeListener(listener); + } + + @Focus + public void setFocus() { + groupTableViewerCmp.setFocus(); + } + + /* DEPENDENCY INJECTION */ + public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { + this.userAdminWrapper = userAdminWrapper; + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/SecurityAdminImages.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/SecurityAdminImages.java new file mode 100644 index 000000000..7bbe3c727 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/SecurityAdminImages.java @@ -0,0 +1,19 @@ +package org.argeo.cms.e4.users; + +import org.argeo.cms.ui.theme.CmsImages; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.graphics.Image; + +/** Shared icons that must be declared programmatically . */ +public class SecurityAdminImages extends CmsImages { + private final static String PREFIX = "icons/"; + + public final static ImageDescriptor ICON_REMOVE_DESC = createDesc(PREFIX + "delete.png"); + public final static ImageDescriptor ICON_USER_DESC = createDesc(PREFIX + "person.png"); + + public final static Image ICON_USER = ICON_USER_DESC.createImage(); + public final static Image ICON_GROUP = createImg(PREFIX + "group.png"); + public final static Image ICON_WORKGROUP = createImg(PREFIX + "workgroup.png"); + public final static Image ICON_ROLE = createImg(PREFIX + "role.gif"); + +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UiAdminUtils.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UiAdminUtils.java new file mode 100644 index 000000000..fb48a47c3 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UiAdminUtils.java @@ -0,0 +1,34 @@ +package org.argeo.cms.e4.users; + +import org.argeo.util.transaction.WorkTransaction; + +/** First effort to centralize back end methods used by the user admin UI */ +public class UiAdminUtils { + /* + * INTERNAL METHODS: Below methods are meant to stay here and are not part + * of a potential generic backend to manage the useradmin + */ + /** Easily notify the ActiveWindow that the transaction had a state change */ + public final static void notifyTransactionStateChange( + WorkTransaction userTransaction) { +// try { +// IWorkbenchWindow aww = PlatformUI.getWorkbench() +// .getActiveWorkbenchWindow(); +// ISourceProviderService sourceProviderService = (ISourceProviderService) aww +// .getService(ISourceProviderService.class); +// UserTransactionProvider esp = (UserTransactionProvider) sourceProviderService +// .getSourceProvider(UserTransactionProvider.TRANSACTION_STATE); +// esp.fireTransactionStateChange(); +// } catch (Exception e) { +// throw new CmsException("Unable to begin transaction", e); +// } + } + + /** + * Email addresses must match this regexp pattern ({@value #EMAIL_PATTERN}. + * Thanks to this tip. + */ + public final static String EMAIL_PATTERN = "^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$"; +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UiUserAdminListener.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UiUserAdminListener.java new file mode 100644 index 000000000..eb64aba0e --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UiUserAdminListener.java @@ -0,0 +1,27 @@ +package org.argeo.cms.e4.users; + +import org.eclipse.swt.widgets.Display; +import org.osgi.service.useradmin.UserAdminEvent; +import org.osgi.service.useradmin.UserAdminListener; + +/** Convenience class to insure the call to refresh is done in the UI thread */ +public abstract class UiUserAdminListener implements UserAdminListener { + + private final Display display; + + public UiUserAdminListener(Display display) { + this.display = display; + } + + @Override + public void roleChanged(final UserAdminEvent event) { + display.asyncExec(new Runnable() { + @Override + public void run() { + roleChangedToUiThread(event); + } + }); + } + + public abstract void roleChangedToUiThread(UserAdminEvent event); +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java new file mode 100644 index 000000000..d120ae9a2 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java @@ -0,0 +1,153 @@ +package org.argeo.cms.e4.users; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.cms.swt.CmsException; +import org.argeo.osgi.useradmin.UserDirectory; +import org.argeo.util.directory.DirectoryConf; +import org.argeo.util.transaction.WorkTransaction; +import org.osgi.service.useradmin.UserAdmin; +import org.osgi.service.useradmin.UserAdminEvent; +import org.osgi.service.useradmin.UserAdminListener; + +/** Centralise interaction with the UserAdmin in this bundle */ +public class UserAdminWrapper { + + private UserAdmin userAdmin; + // private ServiceReference userAdminServiceReference; +// private Set uris; + private Map> userDirectories = Collections + .synchronizedMap(new LinkedHashMap<>()); + private WorkTransaction userTransaction; + + // First effort to simplify UX while managing users and groups + public final static boolean COMMIT_ON_SAVE = true; + + // Registered listeners + List listeners = new ArrayList(); + + /** + * Starts a transaction if necessary. Should always been called together with + * {@link UserAdminWrapper#commitOrNotifyTransactionStateChange()} once the + * security model changes have been performed. + */ + public WorkTransaction beginTransactionIfNeeded() { + try { + // UserTransaction userTransaction = getUserTransaction(); + if (userTransaction.isNoTransactionStatus()) { + userTransaction.begin(); + // UiAdminUtils.notifyTransactionStateChange(userTransaction); + } + return userTransaction; + } catch (Exception e) { + throw new CmsException("Unable to begin transaction", e); + } + } + + /** + * Depending on the current application configuration, it will either commit the + * current transaction or throw a notification that the transaction state has + * changed (In the later case, it must be called from the UI thread). + */ + public void commitOrNotifyTransactionStateChange() { + try { + // UserTransaction userTransaction = getUserTransaction(); + if (userTransaction.isNoTransactionStatus()) + return; + + if (UserAdminWrapper.COMMIT_ON_SAVE) + userTransaction.commit(); + else + UiAdminUtils.notifyTransactionStateChange(userTransaction); + } catch (Exception e) { + throw new CmsException("Unable to clean transaction", e); + } + } + + // TODO implement safer mechanism + public void addListener(UserAdminListener userAdminListener) { + if (!listeners.contains(userAdminListener)) + listeners.add(userAdminListener); + } + + public void removeListener(UserAdminListener userAdminListener) { + if (listeners.contains(userAdminListener)) + listeners.remove(userAdminListener); + } + + public void notifyListeners(UserAdminEvent event) { + for (UserAdminListener listener : listeners) + listener.roleChanged(event); + } + + public Map getKnownBaseDns(boolean onlyWritable) { + Map dns = new HashMap(); + for (UserDirectory userDirectory : userDirectories.keySet()) { + Boolean readOnly = userDirectory.isReadOnly(); + String baseDn = userDirectory.getContext(); + + if (onlyWritable && readOnly) + continue; + if (baseDn.equalsIgnoreCase(CmsConstants.ROLES_BASEDN)) + continue; + if (baseDn.equalsIgnoreCase(CmsConstants.TOKENS_BASEDN)) + continue; + dns.put(baseDn, DirectoryConf.propertiesAsUri(userDirectories.get(userDirectory)).toString()); + + } +// for (String uri : uris) { +// if (!uri.startsWith("/")) +// continue; +// Dictionary props = UserAdminConf.uriAsProperties(uri); +// String readOnly = UserAdminConf.readOnly.getValue(props); +// String baseDn = UserAdminConf.baseDn.getValue(props); +// +// if (onlyWritable && "true".equals(readOnly)) +// continue; +// if (baseDn.equalsIgnoreCase(NodeConstants.ROLES_BASEDN)) +// continue; +// if (baseDn.equalsIgnoreCase(NodeConstants.TOKENS_BASEDN)) +// continue; +// dns.put(baseDn, uri); +// } + return dns; + } + + public UserAdmin getUserAdmin() { + return userAdmin; + } + + public WorkTransaction getUserTransaction() { + return userTransaction; + } + + /* DEPENDENCY INJECTION */ + public void setUserAdmin(UserAdmin userAdmin, Map properties) { + this.userAdmin = userAdmin; +// this.uris = Collections.unmodifiableSortedSet(new TreeSet<>(properties.keySet())); + } + + public void setUserTransaction(WorkTransaction userTransaction) { + this.userTransaction = userTransaction; + } + + public void addUserDirectory(UserDirectory userDirectory, Map properties) { + userDirectories.put(userDirectory, new Hashtable<>(properties)); + } + + public void removeUserDirectory(UserDirectory userDirectory, Map properties) { + userDirectories.remove(userDirectory); + } + + // public void setUserAdminServiceReference( + // ServiceReference userAdminServiceReference) { + // this.userAdminServiceReference = userAdminServiceReference; + // } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserBatchUpdateWizard.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserBatchUpdateWizard.java new file mode 100644 index 000000000..4fc59d30d --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserBatchUpdateWizard.java @@ -0,0 +1,606 @@ +package org.argeo.cms.e4.users; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.api.cms.CmsLog; +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.auth.UserAdminUtils; +import org.argeo.cms.e4.users.providers.CommonNameLP; +import org.argeo.cms.e4.users.providers.DomainNameLP; +import org.argeo.cms.e4.users.providers.MailLP; +import org.argeo.cms.e4.users.providers.UserNameLP; +import org.argeo.cms.swt.CmsException; +import org.argeo.eclipse.ui.ColumnDefinition; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.parts.LdifUsersTable; +import org.argeo.util.naming.LdapAttrs; +import org.argeo.util.naming.LdapObjs; +import org.argeo.util.transaction.WorkTransaction; +import org.eclipse.jface.dialogs.IPageChangeProvider; +import org.eclipse.jface.dialogs.IPageChangedListener; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.dialogs.PageChangedEvent; +import org.eclipse.jface.wizard.IWizardContainer; +import org.eclipse.jface.wizard.Wizard; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Text; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.User; +import org.osgi.service.useradmin.UserAdminEvent; + +/** Wizard to update users */ +public class UserBatchUpdateWizard extends Wizard { + + private final static CmsLog log = CmsLog.getLog(UserBatchUpdateWizard.class); + private UserAdminWrapper userAdminWrapper; + + // pages + private ChooseCommandWizardPage chooseCommandPage; + private ChooseUsersWizardPage userListPage; + private ValidateAndLaunchWizardPage validatePage; + + // Various implemented commands keys + private final static String CMD_UPDATE_PASSWORD = "resetPassword"; + private final static String CMD_UPDATE_EMAIL = "resetEmail"; + private final static String CMD_GROUP_MEMBERSHIP = "groupMembership"; + + private final Map commands = new HashMap() { + private static final long serialVersionUID = 1L; + { + put("Reset password(s)", CMD_UPDATE_PASSWORD); + put("Reset email(s)", CMD_UPDATE_EMAIL); + // TODO implement role / group management + // put("Add/Remove from group", CMD_GROUP_MEMBERSHIP); + } + }; + + public UserBatchUpdateWizard(UserAdminWrapper userAdminWrapper) { + this.userAdminWrapper = userAdminWrapper; + } + + @Override + public void addPages() { + chooseCommandPage = new ChooseCommandWizardPage(); + addPage(chooseCommandPage); + userListPage = new ChooseUsersWizardPage(); + addPage(userListPage); + validatePage = new ValidateAndLaunchWizardPage(); + addPage(validatePage); + } + + @Override + public boolean performFinish() { + if (!canFinish()) + return false; + WorkTransaction ut = userAdminWrapper.getUserTransaction(); + if (!ut.isNoTransactionStatus() && !MessageDialog.openConfirm(getShell(), "Existing Transaction", + "A user transaction is already existing, " + "are you sure you want to proceed ?")) + return false; + + // We cannot use jobs, user modifications are still meant to be done in + // the UIThread + // UpdateJob job = null; + // if (job != null) + // job.schedule(); + + if (CMD_UPDATE_PASSWORD.equals(chooseCommandPage.getCommand())) { + char[] newValue = chooseCommandPage.getPwdValue(); + if (newValue == null) + throw new CmsException("Password cannot be null or an empty string"); + ResetPassword job = new ResetPassword(userAdminWrapper, userListPage.getSelectedUsers(), newValue); + job.doUpdate(); + } else if (CMD_UPDATE_EMAIL.equals(chooseCommandPage.getCommand())) { + String newValue = chooseCommandPage.getEmailValue(); + if (newValue == null) + throw new CmsException("Password cannot be null or an empty string"); + ResetEmail job = new ResetEmail(userAdminWrapper, userListPage.getSelectedUsers(), newValue); + job.doUpdate(); + } + return true; + } + + public boolean canFinish() { + if (this.getContainer().getCurrentPage() == validatePage) + return true; + return false; + } + + private class ResetPassword { + private char[] newPwd; + private UserAdminWrapper userAdminWrapper; + private List usersToUpdate; + + public ResetPassword(UserAdminWrapper userAdminWrapper, List usersToUpdate, char[] newPwd) { + this.newPwd = newPwd; + this.usersToUpdate = usersToUpdate; + this.userAdminWrapper = userAdminWrapper; + } + + @SuppressWarnings("unchecked") + protected void doUpdate() { + userAdminWrapper.beginTransactionIfNeeded(); + try { + for (User user : usersToUpdate) { + // the char array is emptied after being used. + user.getCredentials().put(null, newPwd.clone()); + } + userAdminWrapper.commitOrNotifyTransactionStateChange(); + } catch (Exception e) { + throw new CmsException("Cannot perform batch update on users", e); + } finally { + WorkTransaction ut = userAdminWrapper.getUserTransaction(); + if (!ut.isNoTransactionStatus()) + ut.rollback(); + } + } + } + + private class ResetEmail { + private String newEmail; + private UserAdminWrapper userAdminWrapper; + private List usersToUpdate; + + public ResetEmail(UserAdminWrapper userAdminWrapper, List usersToUpdate, String newEmail) { + this.newEmail = newEmail; + this.usersToUpdate = usersToUpdate; + this.userAdminWrapper = userAdminWrapper; + } + + @SuppressWarnings("unchecked") + protected void doUpdate() { + userAdminWrapper.beginTransactionIfNeeded(); + try { + for (User user : usersToUpdate) { + // the char array is emptied after being used. + user.getProperties().put(LdapAttrs.mail.name(), newEmail); + } + + userAdminWrapper.commitOrNotifyTransactionStateChange(); + if (!usersToUpdate.isEmpty()) + userAdminWrapper.notifyListeners( + new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, usersToUpdate.get(0))); + } catch (Exception e) { + throw new CmsException("Cannot perform batch update on users", e); + } finally { + WorkTransaction ut = userAdminWrapper.getUserTransaction(); + if (!ut.isNoTransactionStatus()) + ut.rollback(); + } + } + } + + // @SuppressWarnings("unused") + // private class AddToGroup extends UpdateJob { + // private String groupID; + // private Session session; + // + // public AddToGroup(Session session, List nodesToUpdate, + // String groupID) { + // super(session, nodesToUpdate); + // this.session = session; + // this.groupID = groupID; + // } + // + // protected void doUpdate(Node node) { + // log.info("Add/Remove to group actions are not yet implemented"); + // // TODO implement this + // // try { + // // throw new CmsException("Not yet implemented"); + // // } catch (RepositoryException re) { + // // throw new CmsException( + // // "Unable to update boolean value for node " + node, re); + // // } + // } + // } + + // /** + // * Base privileged job that will be run asynchronously to perform the + // batch + // * update + // */ + // private abstract class UpdateJob extends PrivilegedJob { + // + // private final UserAdminWrapper userAdminWrapper; + // private final List usersToUpdate; + // + // protected abstract void doUpdate(User user); + // + // public UpdateJob(UserAdminWrapper userAdminWrapper, + // List usersToUpdate) { + // super("Perform update"); + // this.usersToUpdate = usersToUpdate; + // this.userAdminWrapper = userAdminWrapper; + // } + // + // @Override + // protected IStatus doRun(IProgressMonitor progressMonitor) { + // try { + // JcrMonitor monitor = new EclipseJcrMonitor(progressMonitor); + // int total = usersToUpdate.size(); + // monitor.beginTask("Performing change", total); + // userAdminWrapper.beginTransactionIfNeeded(); + // for (User user : usersToUpdate) { + // doUpdate(user); + // monitor.worked(1); + // } + // userAdminWrapper.getUserTransaction().commit(); + // } catch (Exception e) { + // throw new CmsException( + // "Cannot perform batch update on users", e); + // } finally { + // UserTransaction ut = userAdminWrapper.getUserTransaction(); + // try { + // if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION) + // ut.rollback(); + // } catch (IllegalStateException | SecurityException + // | SystemException e) { + // log.error("Unable to rollback session in 'finally', " + // + "the system might be in a dirty state"); + // e.printStackTrace(); + // } + // } + // return Status.OK_STATUS; + // } + // } + + // PAGES + /** + * Displays a combo box that enables user to choose which action to perform + */ + private class ChooseCommandWizardPage extends WizardPage { + private static final long serialVersionUID = -8069434295293996633L; + private Combo chooseCommandCmb; + private Button trueChk; + private Text valueTxt; + private Text pwdTxt; + private Text pwd2Txt; + + public ChooseCommandWizardPage() { + super("Choose a command to run."); + setTitle("Choose a command to run."); + } + + @Override + public void createControl(Composite parent) { + GridLayout gl = new GridLayout(); + Composite container = new Composite(parent, SWT.NO_FOCUS); + container.setLayout(gl); + + chooseCommandCmb = new Combo(container, SWT.READ_ONLY); + chooseCommandCmb.setLayoutData(EclipseUiUtils.fillWidth()); + String[] values = commands.keySet().toArray(new String[0]); + chooseCommandCmb.setItems(values); + + final Composite bottomPart = new Composite(container, SWT.NO_FOCUS); + bottomPart.setLayoutData(EclipseUiUtils.fillAll()); + bottomPart.setLayout(EclipseUiUtils.noSpaceGridLayout()); + + chooseCommandCmb.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = 1L; + + @Override + public void widgetSelected(SelectionEvent e) { + if (getCommand().equals(CMD_UPDATE_PASSWORD)) + populatePasswordCmp(bottomPart); + else if (getCommand().equals(CMD_UPDATE_EMAIL)) + populateEmailCmp(bottomPart); + else if (getCommand().equals(CMD_GROUP_MEMBERSHIP)) + populateGroupCmp(bottomPart); + else + populateBooleanFlagCmp(bottomPart); + checkPageComplete(); + bottomPart.layout(true, true); + } + }); + setControl(container); + } + + private void populateBooleanFlagCmp(Composite parent) { + EclipseUiUtils.clear(parent); + trueChk = new Button(parent, SWT.CHECK); + trueChk.setText("Do it. (It will to the contrary if unchecked)"); + trueChk.setSelection(true); + trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false)); + } + + private void populatePasswordCmp(Composite parent) { + EclipseUiUtils.clear(parent); + Composite body = new Composite(parent, SWT.NO_FOCUS); + + ModifyListener ml = new ModifyListener() { + private static final long serialVersionUID = -1558726363536729634L; + + @Override + public void modifyText(ModifyEvent event) { + checkPageComplete(); + } + }; + + body.setLayout(new GridLayout(2, false)); + body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + pwdTxt = EclipseUiUtils.createGridLP(body, "New password", ml); + pwd2Txt = EclipseUiUtils.createGridLP(body, "Repeat password", ml); + } + + private void populateEmailCmp(Composite parent) { + EclipseUiUtils.clear(parent); + Composite body = new Composite(parent, SWT.NO_FOCUS); + + ModifyListener ml = new ModifyListener() { + private static final long serialVersionUID = 2147704227294268317L; + + @Override + public void modifyText(ModifyEvent event) { + checkPageComplete(); + } + }; + + body.setLayout(new GridLayout(2, false)); + body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + valueTxt = EclipseUiUtils.createGridLT(body, "New e-mail", ml); + } + + private void checkPageComplete() { + String errorMsg = null; + if (chooseCommandCmb.getSelectionIndex() < 0) + errorMsg = "Please select an action"; + else if (CMD_UPDATE_EMAIL.equals(getCommand())) { + if (!valueTxt.getText().matches(UiAdminUtils.EMAIL_PATTERN)) + errorMsg = "Not a valid e-mail address"; + } else if (CMD_UPDATE_PASSWORD.equals(getCommand())) { + if (EclipseUiUtils.isEmpty(pwdTxt.getText()) || pwdTxt.getText().length() < 4) + errorMsg = "Please enter a password that is at least 4 character long"; + else if (!pwdTxt.getText().equals(pwd2Txt.getText())) + errorMsg = "Passwords are different"; + } + if (EclipseUiUtils.notEmpty(errorMsg)) { + setMessage(errorMsg, WizardPage.ERROR); + setPageComplete(false); + } else { + setMessage("Page complete, you can proceed to user choice", WizardPage.INFORMATION); + setPageComplete(true); + } + + getContainer().updateButtons(); + } + + private void populateGroupCmp(Composite parent) { + EclipseUiUtils.clear(parent); + trueChk = new Button(parent, SWT.CHECK); + trueChk.setText("Add to group. (It will remove user(s) from the " + "corresponding group if unchecked)"); + trueChk.setSelection(true); + trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false)); + } + + protected String getCommand() { + return commands.get(chooseCommandCmb.getItem(chooseCommandCmb.getSelectionIndex())); + } + + protected String getCommandLbl() { + return chooseCommandCmb.getItem(chooseCommandCmb.getSelectionIndex()); + } + + @SuppressWarnings("unused") + protected boolean getBoleanValue() { + // FIXME this is not consistent and will lead to errors. + if ("argeo:enabled".equals(getCommand())) + return trueChk.getSelection(); + else + return !trueChk.getSelection(); + } + + @SuppressWarnings("unused") + protected String getStringValue() { + String value = null; + if (valueTxt != null) { + value = valueTxt.getText(); + if ("".equals(value.trim())) + value = null; + } + return value; + } + + protected char[] getPwdValue() { + // We do not directly reset the password text fields: There is no + // need to over secure this process: setting a pwd to multi users + // at the same time is anyhow a bad practice and should be used only + // in test environment or for temporary access + if (pwdTxt == null || pwdTxt.isDisposed()) + return null; + else + return pwdTxt.getText().toCharArray(); + } + + protected String getEmailValue() { + // We do not directly reset the password text fields: There is no + // need to over secure this process: setting a pwd to multi users + // at the same time is anyhow a bad practice and should be used only + // in test environment or for temporary access + if (valueTxt == null || valueTxt.isDisposed()) + return null; + else + return valueTxt.getText(); + } + } + + /** + * Displays a list of users with a check box to be able to choose some of them + */ + private class ChooseUsersWizardPage extends WizardPage implements IPageChangedListener { + private static final long serialVersionUID = 7651807402211214274L; + private ChooseUserTableViewer userTableCmp; + + public ChooseUsersWizardPage() { + super("Choose Users"); + setTitle("Select users who will be impacted"); + } + + @Override + public void createControl(Composite parent) { + Composite pageCmp = new Composite(parent, SWT.NONE); + pageCmp.setLayout(EclipseUiUtils.noSpaceGridLayout()); + + // Define the displayed columns + List columnDefs = new ArrayList(); + columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name", 150)); + columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150)); + columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200)); + + // Only show technical DN to admin + if (CurrentUser.isInRole(CmsConstants.ROLE_ADMIN)) + columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", 300)); + + userTableCmp = new ChooseUserTableViewer(pageCmp, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); + userTableCmp.setLayoutData(EclipseUiUtils.fillAll()); + userTableCmp.setColumnDefinitions(columnDefs); + userTableCmp.populate(true, true); + userTableCmp.refresh(); + + setControl(pageCmp); + + // Add listener to update message when shown + final IWizardContainer wContainer = this.getContainer(); + if (wContainer instanceof IPageChangeProvider) { + ((IPageChangeProvider) wContainer).addPageChangedListener(this); + } + + } + + @Override + public void pageChanged(PageChangedEvent event) { + if (event.getSelectedPage() == this) { + String msg = "Chosen batch action: " + chooseCommandPage.getCommandLbl(); + ((WizardPage) event.getSelectedPage()).setMessage(msg); + } + } + + protected List getSelectedUsers() { + return userTableCmp.getSelectedUsers(); + } + + private class ChooseUserTableViewer extends LdifUsersTable { + private static final long serialVersionUID = 5080437561015853124L; + private final String[] knownProps = { LdapAttrs.uid.name(), LdapAttrs.DN, LdapAttrs.cn.name(), + LdapAttrs.givenName.name(), LdapAttrs.sn.name(), LdapAttrs.mail.name() }; + + public ChooseUserTableViewer(Composite parent, int style) { + super(parent, style); + } + + @Override + protected List listFilteredElements(String filter) { + Role[] roles; + + try { + StringBuilder builder = new StringBuilder(); + + StringBuilder tmpBuilder = new StringBuilder(); + if (EclipseUiUtils.notEmpty(filter)) + for (String prop : knownProps) { + tmpBuilder.append("("); + tmpBuilder.append(prop); + tmpBuilder.append("=*"); + tmpBuilder.append(filter); + tmpBuilder.append("*)"); + } + if (tmpBuilder.length() > 1) { + builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=") + .append(LdapObjs.inetOrgPerson.name()).append(")(|"); + builder.append(tmpBuilder.toString()); + builder.append("))"); + } else + builder.append("(").append(LdapAttrs.objectClass.name()).append("=") + .append(LdapObjs.inetOrgPerson.name()).append(")"); + roles = userAdminWrapper.getUserAdmin().getRoles(builder.toString()); + } catch (InvalidSyntaxException e) { + throw new CmsException("Unable to get roles with filter: " + filter, e); + } + List users = new ArrayList(); + for (Role role : roles) + // Prevent current logged in user to perform batch on + // himself + if (!UserAdminUtils.isCurrentUser((User) role)) + users.add((User) role); + return users; + } + } + } + + /** Summary of input data before launching the process */ + private class ValidateAndLaunchWizardPage extends WizardPage implements IPageChangedListener { + private static final long serialVersionUID = 7098918351451743853L; + private ChosenUsersTableViewer userTableCmp; + + public ValidateAndLaunchWizardPage() { + super("Validate and launch"); + setTitle("Validate and launch"); + } + + @Override + public void createControl(Composite parent) { + Composite pageCmp = new Composite(parent, SWT.NO_FOCUS); + pageCmp.setLayout(EclipseUiUtils.noSpaceGridLayout()); + + List columnDefs = new ArrayList(); + columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name", 150)); + columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150)); + columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200)); + // Only show technical DN to admin + if (CurrentUser.isInRole(CmsConstants.ROLE_ADMIN)) + columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", 300)); + userTableCmp = new ChosenUsersTableViewer(pageCmp, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); + userTableCmp.setLayoutData(EclipseUiUtils.fillAll()); + userTableCmp.setColumnDefinitions(columnDefs); + userTableCmp.populate(false, false); + userTableCmp.refresh(); + setControl(pageCmp); + // Add listener to update message when shown + final IWizardContainer wContainer = this.getContainer(); + if (wContainer instanceof IPageChangeProvider) { + ((IPageChangeProvider) wContainer).addPageChangedListener(this); + } + } + + @Override + public void pageChanged(PageChangedEvent event) { + if (event.getSelectedPage() == this) { + @SuppressWarnings({ "unchecked", "rawtypes" }) + Object[] values = ((ArrayList) userListPage.getSelectedUsers()) + .toArray(new Object[userListPage.getSelectedUsers().size()]); + userTableCmp.getTableViewer().setInput(values); + String msg = "Following batch action: [" + chooseCommandPage.getCommandLbl() + + "] will be perfomed on the users listed below.\n"; + // + "Are you sure you want to proceed?"; + setMessage(msg); + } + } + + private class ChosenUsersTableViewer extends LdifUsersTable { + private static final long serialVersionUID = 7814764735794270541L; + + public ChosenUsersTableViewer(Composite parent, int style) { + super(parent, style); + } + + @Override + protected List listFilteredElements(String filter) { + return userListPage.getSelectedUsers(); + } + } + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserEditor.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserEditor.java new file mode 100644 index 000000000..66f442082 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserEditor.java @@ -0,0 +1,535 @@ +package org.argeo.cms.e4.users; + +import static org.argeo.cms.auth.UserAdminUtils.getProperty; +import static org.argeo.util.naming.LdapAttrs.cn; +import static org.argeo.util.naming.LdapAttrs.givenName; +import static org.argeo.util.naming.LdapAttrs.mail; +import static org.argeo.util.naming.LdapAttrs.sn; +import static org.argeo.util.naming.LdapAttrs.uid; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.inject.Inject; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.auth.UserAdminUtils; +import org.argeo.cms.e4.users.providers.CommonNameLP; +import org.argeo.cms.e4.users.providers.DomainNameLP; +import org.argeo.cms.e4.users.providers.RoleIconLP; +import org.argeo.cms.e4.users.providers.UserFilter; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ui.eclipse.forms.AbstractFormPart; +//import org.argeo.cms.ui.eclipse.forms.FormToolkit; +import org.argeo.cms.ui.eclipse.forms.IManagedForm; +import org.argeo.eclipse.ui.ColumnDefinition; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.parts.LdifUsersTable; +import org.argeo.util.naming.LdapAttrs; +import org.eclipse.e4.ui.workbench.modeling.EPartService; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.ToolBarManager; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.dialogs.TrayDialog; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerDropAdapter; +import org.eclipse.swt.SWT; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.DropTargetEvent; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.dnd.TransferData; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Link; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.ToolBar; +import org.osgi.service.useradmin.Group; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.User; +import org.osgi.service.useradmin.UserAdmin; +import org.osgi.service.useradmin.UserAdminEvent; + +/** Display/edit the properties of a given user */ +public class UserEditor extends AbstractRoleEditor { + // final static String ID = "UserEditor.mainPage"; + + @Inject + private EPartService partService; + + // private final UserEditor editor; + // private UserAdminWrapper userAdminWrapper; + + // Local configuration + // private final int PRE_TITLE_INDENT = 10; + + // public UserMainPage(FormEditor editor, UserAdminWrapper userAdminWrapper) { + // super(editor, ID, "Main"); + // this.editor = (UserEditor) editor; + // this.userAdminWrapper = userAdminWrapper; + // } + + // protected void createFormContent(final IManagedForm mf) { + // ScrolledForm form = mf.getForm(); + // Composite body = form.getBody(); + // GridLayout mainLayout = new GridLayout(); + // // mainLayout.marginRight = 10; + // body.setLayout(mainLayout); + // User user = editor.getDisplayedUser(); + // appendOverviewPart(body, user); + // // Remove to ability to force the password for his own user. The user + // // must then use the change pwd feature + // appendMemberOfPart(body, user); + // } + + @Override + protected void createUi(Composite body) { + // Composite body = new Composite(parent, SWT.BORDER); + GridLayout mainLayout = new GridLayout(); + // mainLayout.marginRight = 10; + body.setLayout(mainLayout); + // body.getParent().setLayout(new GridLayout()); + // body.setLayoutData(CmsUiUtils.fillAll()); + User user = getDisplayedUser(); + appendOverviewPart(body, user); + // Remove to ability to force the password for his own user. The user + // must then use the change pwd feature + appendMemberOfPart(body, user); + } + + /** Creates the general section */ + private void appendOverviewPart(final Composite parent, final User user) { + // FormToolkit tk = getManagedForm().getToolkit(); + + // Section section = tk.createSection(parent, SWT.NO_FOCUS); + // GridData gd = EclipseUiUtils.fillWidth(); + // // gd.verticalAlignment = PRE_TITLE_INDENT; + // section.setLayoutData(gd); + Composite body = new Composite(parent, SWT.NONE); + body.setLayoutData(EclipseUiUtils.fillWidth()); + // section.setClient(body); + // body.setLayout(new GridLayout(6, false)); + body.setLayout(new GridLayout(2, false)); + + Text commonName = createReadOnlyLT(body, "Name", getProperty(user, cn)); + Text distinguishedName = createReadOnlyLT(body, "Login", getProperty(user, uid)); + Text firstName = createLT(body, "First name", getProperty(user, givenName)); + Text lastName = createLT(body, "Last name", getProperty(user, sn)); + Text email = createLT(body, "Email", getProperty(user, mail)); + + Link resetPwdLk = new Link(body, SWT.NONE); + if (!UserAdminUtils.isCurrentUser(user)) { + resetPwdLk.setText("Reset password"); + } + resetPwdLk.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1)); + + // create form part (controller) + AbstractFormPart part = new AbstractFormPart() { + private MainInfoListener listener; + + @Override + public void initialize(IManagedForm form) { + super.initialize(form); + listener = new MainInfoListener(parent.getDisplay(), this); + userAdminWrapper.addListener(listener); + } + + @Override + public void dispose() { + userAdminWrapper.removeListener(listener); + super.dispose(); + } + + @SuppressWarnings("unchecked") + public void commit(boolean onSave) { + // TODO Sanity checks (mail validity...) + user.getProperties().put(LdapAttrs.givenName.name(), firstName.getText()); + user.getProperties().put(LdapAttrs.sn.name(), lastName.getText()); + user.getProperties().put(LdapAttrs.cn.name(), commonName.getText()); + user.getProperties().put(LdapAttrs.mail.name(), email.getText()); + super.commit(onSave); + } + + @Override + public void refresh() { + distinguishedName.setText(UserAdminUtils.getProperty(user, LdapAttrs.uid.name())); + commonName.setText(UserAdminUtils.getProperty(user, LdapAttrs.cn.name())); + firstName.setText(UserAdminUtils.getProperty(user, LdapAttrs.givenName.name())); + lastName.setText(UserAdminUtils.getProperty(user, LdapAttrs.sn.name())); + email.setText(UserAdminUtils.getProperty(user, LdapAttrs.mail.name())); + refreshFormTitle(user); + super.refresh(); + } + }; + + // Improve this: automatically generate CN when first or last name + // changes + ModifyListener cnML = new ModifyListener() { + private static final long serialVersionUID = 4298649222869835486L; + + @Override + public void modifyText(ModifyEvent event) { + String first = firstName.getText(); + String last = lastName.getText(); + String cn = first.trim() + " " + last.trim() + " "; + cn = cn.trim(); + commonName.setText(cn); + // getManagedForm().getForm().setText(cn); + updateEditorTitle(cn); + } + }; + firstName.addModifyListener(cnML); + lastName.addModifyListener(cnML); + + ModifyListener defaultListener = new FormPartML(part); + firstName.addModifyListener(defaultListener); + lastName.addModifyListener(defaultListener); + email.addModifyListener(defaultListener); + + if (!UserAdminUtils.isCurrentUser(user)) + resetPwdLk.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = 5881800534589073787L; + + @Override + public void widgetSelected(SelectionEvent e) { + new ChangePasswordDialog(user, "Reset password").open(); + } + }); + + getManagedForm().addPart(part); + } + + private class ChangePasswordDialog extends TrayDialog { + private static final long serialVersionUID = 2843538207460082349L; + + private User user; + private Text password1; + private Text password2; + private String title; + // private FormToolkit tk; + + public ChangePasswordDialog(User user, String title) { + super(Display.getDefault().getActiveShell()); + // this.tk = tk; + this.user = user; + this.title = title; + } + + protected Control createDialogArea(Composite parent) { + Composite dialogarea = (Composite) super.createDialogArea(parent); + dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + Composite body = new Composite(dialogarea, SWT.NO_FOCUS); + body.setLayoutData(EclipseUiUtils.fillAll()); + GridLayout layout = new GridLayout(2, false); + body.setLayout(layout); + + password1 = createLP(body, "New password", ""); + password2 = createLP(body, "Repeat password", ""); + parent.pack(); + return body; + } + + @SuppressWarnings("unchecked") + @Override + protected void okPressed() { + String msg = null; + + if (password1.getText().equals("")) + msg = "Password cannot be empty"; + else if (password1.getText().equals(password2.getText())) { + char[] newPassword = password1.getText().toCharArray(); + // userAdminWrapper.beginTransactionIfNeeded(); + userAdminWrapper.beginTransactionIfNeeded(); + user.getCredentials().put(null, newPassword); + userAdminWrapper.commitOrNotifyTransactionStateChange(); + super.okPressed(); + } else { + msg = "Passwords are not equals"; + } + + if (EclipseUiUtils.notEmpty(msg)) + MessageDialog.openError(getParentShell(), "Cannot reset pasword", msg); + } + + protected void configureShell(Shell shell) { + super.configureShell(shell); + shell.setText(title); + } + } + + private LdifUsersTable appendMemberOfPart(final Composite parent, User user) { + // Section section = addSection(tk, parent, "Roles"); + // Composite body = (Composite) section.getClient(); + // Composite body= parent; + Composite body = new Composite(parent, SWT.BORDER); + body.setLayout(new GridLayout()); + body.setLayoutData(CmsSwtUtils.fillAll()); + + // boolean isAdmin = CurrentUser.isInRole(NodeConstants.ROLE_ADMIN); + + // Displayed columns + List columnDefs = new ArrayList(); + columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 0, 24)); + columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Name", 150)); + columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 100)); + // Only show technical DN to administrators + // if (isAdmin) + // columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", + // 300)); + + // Create and configure the table + final LdifUsersTable userViewerCmp = new MyUserTableViewer(body, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL, user); + + userViewerCmp.setColumnDefinitions(columnDefs); + // if (isAdmin) + // userViewerCmp.populateWithStaticFilters(false, false); + // else + userViewerCmp.populate(true, false); + GridData gd = EclipseUiUtils.fillAll(); + gd.heightHint = 500; + userViewerCmp.setLayoutData(gd); + + // Controllers + TableViewer userViewer = userViewerCmp.getTableViewer(); + userViewer.addDoubleClickListener(new UserTableDefaultDClickListener(partService)); + int operations = DND.DROP_COPY | DND.DROP_MOVE; + Transfer[] tt = new Transfer[] { TextTransfer.getInstance() }; + GroupDropListener dropL = new GroupDropListener(userAdminWrapper, userViewer, user); + userViewer.addDropSupport(operations, tt, dropL); + + AbstractFormPart part = new AbstractFormPart() { + + private GroupChangeListener listener; + + @Override + public void initialize(IManagedForm form) { + super.initialize(form); + listener = new GroupChangeListener(parent.getDisplay(), this); + userAdminWrapper.addListener(listener); + } + + public void commit(boolean onSave) { + super.commit(onSave); + } + + @Override + public void dispose() { + userAdminWrapper.removeListener(listener); + super.dispose(); + } + + @Override + public void refresh() { + userViewerCmp.refresh(); + super.refresh(); + } + }; + getManagedForm().addPart(part); + // addRemoveAbitily(body, userViewer, user); + // userViewerCmp.refresh(); + String tooltip = "Remove " + UserAdminUtils.getUserLocalId(user.getName()) + " from the below selected groups"; + Action action = new RemoveMembershipAction(userViewer, user, tooltip, SecurityAdminImages.ICON_REMOVE_DESC); + ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT); + ToolBar toolBar = toolBarManager.createControl(body); + toolBar.setLayoutData(CmsSwtUtils.fillWidth()); + toolBarManager.add(action); + toolBarManager.update(true); + return userViewerCmp; + } + + private class MyUserTableViewer extends LdifUsersTable { + private static final long serialVersionUID = 2653790051461237329L; + + private Button showSystemRoleBtn; + + private final User user; + private final UserFilter userFilter; + + public MyUserTableViewer(Composite parent, int style, User user) { + super(parent, style, true); + this.user = user; + userFilter = new UserFilter(); + } + + protected void populateStaticFilters(Composite staticFilterCmp) { + staticFilterCmp.setLayout(new GridLayout()); + showSystemRoleBtn = new Button(staticFilterCmp, SWT.CHECK); + showSystemRoleBtn.setText("Show system roles"); + boolean showSysRole = CurrentUser.isInRole(CmsConstants.ROLE_ADMIN); + showSystemRoleBtn.setSelection(showSysRole); + userFilter.setShowSystemRole(showSysRole); + showSystemRoleBtn.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = -7033424592697691676L; + + @Override + public void widgetSelected(SelectionEvent e) { + userFilter.setShowSystemRole(showSystemRoleBtn.getSelection()); + refresh(); + } + }); + } + + @Override + protected List listFilteredElements(String filter) { + List users = (List) getFlatGroups(null); + List filteredUsers = new ArrayList(); + if (users.contains(user)) + users.remove(user); + userFilter.setSearchText(filter); + for (User user : users) + if (userFilter.select(null, null, user)) + filteredUsers.add(user); + return filteredUsers; + } + } + + // private void addRemoveAbility(Composite parent, TableViewer userViewer, User + // user) { + // // Section section = sectionPart.getSection(); + // ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT); + // ToolBar toolbar = toolBarManager.createControl(parent); + // final Cursor handCursor = new Cursor(Display.getCurrent(), SWT.CURSOR_HAND); + // toolbar.setCursor(handCursor); + // toolbar.addDisposeListener(new DisposeListener() { + // private static final long serialVersionUID = 3882131405820522925L; + // + // public void widgetDisposed(DisposeEvent e) { + // if ((handCursor != null) && (handCursor.isDisposed() == false)) { + // handCursor.dispose(); + // } + // } + // }); + // + // String tooltip = "Remove " + UserAdminUtils.getUserLocalId(user.getName()) + + // " from the below selected groups"; + // Action action = new RemoveMembershipAction(userViewer, user, tooltip, + // SecurityAdminImages.ICON_REMOVE_DESC); + // toolBarManager.add(action); + // toolBarManager.update(true); + // // section.setTextClient(toolbar); + // } + + private class RemoveMembershipAction extends Action { + private static final long serialVersionUID = -1337713097184522588L; + + private final TableViewer userViewer; + private final User user; + + RemoveMembershipAction(TableViewer userViewer, User user, String name, ImageDescriptor img) { + super(name, img); + this.userViewer = userViewer; + this.user = user; + } + + @Override + public void run() { + ISelection selection = userViewer.getSelection(); + if (selection.isEmpty()) + return; + + @SuppressWarnings("unchecked") + Iterator it = ((IStructuredSelection) selection).iterator(); + List groups = new ArrayList(); + while (it.hasNext()) { + Group currGroup = it.next(); + groups.add(currGroup); + } + + userAdminWrapper.beginTransactionIfNeeded(); + for (Group group : groups) { + group.removeMember(user); + } + userAdminWrapper.commitOrNotifyTransactionStateChange(); + for (Group group : groups) { + userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, group)); + } + } + } + + /** + * Defines the table as being a potential target to add group memberships + * (roles) to this user + */ + private class GroupDropListener extends ViewerDropAdapter { + private static final long serialVersionUID = 2893468717831451621L; + + private final UserAdminWrapper myUserAdminWrapper; + private final User myUser; + + public GroupDropListener(UserAdminWrapper userAdminWrapper, Viewer userViewer, User user) { + super(userViewer); + this.myUserAdminWrapper = userAdminWrapper; + this.myUser = user; + } + + @Override + public boolean validateDrop(Object target, int operation, TransferData transferType) { + // Target is always OK in a list only view + // TODO check if not a string + boolean validDrop = true; + return validDrop; + } + + @Override + public void drop(DropTargetEvent event) { + String name = (String) event.data; + UserAdmin myUserAdmin = myUserAdminWrapper.getUserAdmin(); + Role role = myUserAdmin.getRole(name); + // TODO this check should be done before. + if (role.getType() == Role.GROUP) { + // TODO check if the user is already member of this group + + myUserAdminWrapper.beginTransactionIfNeeded(); + Group group = (Group) role; + group.addMember(myUser); + userAdminWrapper.commitOrNotifyTransactionStateChange(); + myUserAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, group)); + } + super.drop(event); + } + + @Override + public boolean performDrop(Object data) { + // userTableViewerCmp.refresh(); + return true; + } + } + + // LOCAL HELPERS + private void refreshFormTitle(User group) { + // getManagedForm().getForm().setText(UserAdminUtils.getProperty(group, + // LdapAttrs.cn.name())); + } + + /** Appends a section with a title */ + // private Section addSection(FormToolkit tk, Composite parent, String title) { + // Section section = tk.createSection(parent, Section.TITLE_BAR); + // GridData gd = EclipseUiUtils.fillWidth(); + // gd.verticalAlignment = PRE_TITLE_INDENT; + // section.setLayoutData(gd); + // section.setText(title); + // // section.getMenu().setVisible(true); + // + // Composite body = tk.createComposite(section, SWT.WRAP); + // body.setLayoutData(EclipseUiUtils.fillAll()); + // section.setClient(body); + // + // return section; + // } + +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserTableDefaultDClickListener.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserTableDefaultDClickListener.java new file mode 100644 index 000000000..c6d024ebc --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserTableDefaultDClickListener.java @@ -0,0 +1,39 @@ +package org.argeo.cms.e4.users; + +import org.argeo.cms.e4.CmsE4Utils; +import org.argeo.util.naming.LdapAttrs; +import org.eclipse.e4.ui.workbench.modeling.EPartService; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.osgi.service.useradmin.Group; +import org.osgi.service.useradmin.User; + +/** + * Default double click listener for the various user tables, will open the + * clicked item in the editor + */ +public class UserTableDefaultDClickListener implements IDoubleClickListener { + private final EPartService partService; + + public UserTableDefaultDClickListener(EPartService partService) { + this.partService = partService; + } + + public void doubleClick(DoubleClickEvent evt) { + if (evt.getSelection().isEmpty()) + return; + Object obj = ((IStructuredSelection) evt.getSelection()).getFirstElement(); + User user = (User) obj; + + String editorId = getEditorId(user); + CmsE4Utils.openEditor(partService, editorId, LdapAttrs.uid.name(), user.getName()); + } + + protected String getEditorId(User user) { + if (user instanceof Group) + return "org.argeo.cms.e4.partdescriptor.groupEditor"; + else + return "org.argeo.cms.e4.partdescriptor.userEditor"; + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UsersView.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UsersView.java new file mode 100644 index 000000000..720945c4c --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UsersView.java @@ -0,0 +1,182 @@ +package org.argeo.cms.e4.users; + +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.inject.Inject; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.e4.users.providers.CommonNameLP; +import org.argeo.cms.e4.users.providers.DomainNameLP; +import org.argeo.cms.e4.users.providers.MailLP; +import org.argeo.cms.e4.users.providers.UserDragListener; +import org.argeo.cms.e4.users.providers.UserNameLP; +import org.argeo.cms.swt.CmsException; +import org.argeo.eclipse.ui.ColumnDefinition; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.parts.LdifUsersTable; +import org.argeo.util.naming.LdapAttrs; +import org.argeo.util.naming.LdapObjs; +import org.eclipse.e4.ui.di.Focus; +import org.eclipse.e4.ui.workbench.modeling.EPartService; +import org.eclipse.e4.ui.workbench.modeling.ESelectionService; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.User; +import org.osgi.service.useradmin.UserAdminEvent; +import org.osgi.service.useradmin.UserAdminListener; + +/** List all users with filter - based on Ldif userAdmin */ +public class UsersView { + // private final static Log log = LogFactory.getLog(UsersView.class); + + // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".usersView"; + + @Inject + private UserAdminWrapper userAdminWrapper; + @Inject + private EPartService partService; + + // UI Objects + private LdifUsersTable userTableViewerCmp; + private TableViewer userViewer; + private List columnDefs = new ArrayList(); + + private UserAdminListener listener; + + @PostConstruct + public void createPartControl(Composite parent, ESelectionService selectionService) { + + parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); + // Define the displayed columns + columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name", 150)); + columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150)); + columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200)); + // Only show technical DN to admin + if (CurrentUser.isInRole(CmsConstants.ROLE_ADMIN)) + columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", 300)); + + // Create and configure the table + userTableViewerCmp = new MyUserTableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); + userTableViewerCmp.setLayoutData(EclipseUiUtils.fillAll()); + userTableViewerCmp.setColumnDefinitions(columnDefs); + userTableViewerCmp.populate(true, false); + + // Links + userViewer = userTableViewerCmp.getTableViewer(); + userViewer.addDoubleClickListener(new UserTableDefaultDClickListener(partService)); + userViewer.addSelectionChangedListener(new ISelectionChangedListener() { + + @Override + public void selectionChanged(SelectionChangedEvent event) { + IStructuredSelection selection = (IStructuredSelection) event.getSelection(); + selectionService.setSelection(selection.toList()); + } + }); + // getViewSite().setSelectionProvider(userViewer); + + // Really? + userTableViewerCmp.refresh(); + + // Drag and drop + int operations = DND.DROP_COPY | DND.DROP_MOVE; + Transfer[] tt = new Transfer[] { TextTransfer.getInstance() }; + userViewer.addDragSupport(operations, tt, new UserDragListener(userViewer)); + + // Register a useradmin listener + listener = new MyUiUAListener(parent.getDisplay()); + userAdminWrapper.addListener(listener); + } + + private class MyUiUAListener extends UiUserAdminListener { + public MyUiUAListener(Display display) { + super(display); + } + + @Override + public void roleChangedToUiThread(UserAdminEvent event) { + if (userViewer != null && !userViewer.getTable().isDisposed()) + refresh(); + } + } + + private class MyUserTableViewer extends LdifUsersTable { + private static final long serialVersionUID = 8467999509931900367L; + + private final String[] knownProps = { LdapAttrs.DN, LdapAttrs.uid.name(), LdapAttrs.cn.name(), + LdapAttrs.givenName.name(), LdapAttrs.sn.name(), LdapAttrs.mail.name() }; + + public MyUserTableViewer(Composite parent, int style) { + super(parent, style); + } + + @Override + protected List listFilteredElements(String filter) { + Role[] roles; + + try { + StringBuilder builder = new StringBuilder(); + + StringBuilder tmpBuilder = new StringBuilder(); + if (EclipseUiUtils.notEmpty(filter)) + for (String prop : knownProps) { + tmpBuilder.append("("); + tmpBuilder.append(prop); + tmpBuilder.append("=*"); + tmpBuilder.append(filter); + tmpBuilder.append("*)"); + } + if (tmpBuilder.length() > 1) { + builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=") + .append(LdapObjs.inetOrgPerson.name()).append(")(|"); + builder.append(tmpBuilder.toString()); + builder.append("))"); + } else + builder.append("(").append(LdapAttrs.objectClass.name()).append("=") + .append(LdapObjs.inetOrgPerson.name()).append(")"); + roles = userAdminWrapper.getUserAdmin().getRoles(builder.toString()); + } catch (InvalidSyntaxException e) { + throw new CmsException("Unable to get roles with filter: " + filter, e); + } + List users = new ArrayList(); + for (Role role : roles) + // if (role.getType() == Role.USER && role.getType() != + // Role.GROUP) + users.add((User) role); + return users; + } + } + + public void refresh() { + userTableViewerCmp.refresh(); + } + + // Override generic view methods + @PreDestroy + public void dispose() { + userAdminWrapper.removeListener(listener); + } + + @Focus + public void setFocus() { + userTableViewerCmp.setFocus(); + } + + /* DEPENDENCY INJECTION */ + public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { + this.userAdminWrapper = userAdminWrapper; + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/DeleteGroups.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/DeleteGroups.java new file mode 100644 index 000000000..742bc3f5f --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/DeleteGroups.java @@ -0,0 +1,95 @@ +package org.argeo.cms.e4.users.handlers; + +import java.util.List; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.argeo.cms.auth.UserAdminUtils; +import org.argeo.cms.e4.users.GroupsView; +import org.argeo.cms.e4.users.UserAdminWrapper; +import org.eclipse.e4.core.di.annotations.CanExecute; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.services.IServiceConstants; +import org.eclipse.e4.ui.workbench.modeling.ESelectionService; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.swt.widgets.Display; +import org.osgi.service.useradmin.Group; +import org.osgi.service.useradmin.UserAdmin; +import org.osgi.service.useradmin.UserAdminEvent; + +/** Delete the selected groups */ +public class DeleteGroups { + // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + + // ".deleteGroups"; + + /* DEPENDENCY INJECTION */ + @Inject + private UserAdminWrapper userAdminWrapper; + + @Inject + ESelectionService selectionService; + + @SuppressWarnings("unchecked") + @Execute + public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) { + // ISelection selection = null;// HandlerUtil.getCurrentSelection(event); + // if (selection.isEmpty()) + // return null; + // + // List groups = new ArrayList(); + // Iterator it = ((IStructuredSelection) selection).iterator(); + + List selection = (List) selectionService.getSelection(); + if (selection == null) + return; + + StringBuilder builder = new StringBuilder(); + for (Group group : selection) { + Group currGroup = group; + String groupName = UserAdminUtils.getUserLocalId(currGroup.getName()); + // TODO add checks + builder.append(groupName).append("; "); + // groups.add(currGroup); + } + + if (!MessageDialog.openQuestion(Display.getCurrent().getActiveShell(), "Delete Groups", "Are you sure that you " + + "want to delete these groups?\n" + builder.substring(0, builder.length() - 2))) + return; + + userAdminWrapper.beginTransactionIfNeeded(); + UserAdmin userAdmin = userAdminWrapper.getUserAdmin(); + // IWorkbenchPage iwp = + // HandlerUtil.getActiveWorkbenchWindow(event).getActivePage(); + for (Group group : selection) { + String groupName = group.getName(); + // TODO find a way to close the editor cleanly if opened. Cannot be + // done through the UserAdminListeners, it causes a + // java.util.ConcurrentModificationException because disposing the + // editor unregisters and disposes the listener + // IEditorPart part = iwp.findEditor(new UserEditorInput(groupName)); + // if (part != null) + // iwp.closeEditor(part, false); + userAdmin.removeRole(groupName); + } + userAdminWrapper.commitOrNotifyTransactionStateChange(); + + // Update the view + for (Group group : selection) { + userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_REMOVED, group)); + } + + // return null; + } + + @CanExecute + public boolean canExecute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) { + return part.getObject() instanceof GroupsView && selectionService.getSelection() != null; + } + + /* DEPENDENCY INJECTION */ + // public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { + // this.userAdminWrapper = userAdminWrapper; + // } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/DeleteUsers.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/DeleteUsers.java new file mode 100644 index 000000000..d1afd2210 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/DeleteUsers.java @@ -0,0 +1,88 @@ +package org.argeo.cms.e4.users.handlers; + +import java.util.List; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.argeo.cms.auth.UserAdminUtils; +import org.argeo.cms.e4.users.UserAdminWrapper; +import org.argeo.cms.e4.users.UsersView; +import org.eclipse.e4.core.di.annotations.CanExecute; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.services.IServiceConstants; +import org.eclipse.e4.ui.workbench.modeling.ESelectionService; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.swt.widgets.Display; +import org.osgi.service.useradmin.User; +import org.osgi.service.useradmin.UserAdmin; +import org.osgi.service.useradmin.UserAdminEvent; + +/** Delete the selected users */ +public class DeleteUsers { + // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".deleteUsers"; + + /* DEPENDENCY INJECTION */ + @Inject + private UserAdminWrapper userAdminWrapper; + + @SuppressWarnings("unchecked") + @Execute + public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) { + // ISelection selection = null;// HandlerUtil.getCurrentSelection(event); + // if (selection.isEmpty()) + // return null; + List selection = (List) selectionService.getSelection(); + if (selection == null) + return; + +// Iterator it = ((IStructuredSelection) selection).iterator(); +// List users = new ArrayList(); + StringBuilder builder = new StringBuilder(); + + for(User user:selection) { + User currUser = user; +// User currUser = it.next(); + String userName = UserAdminUtils.getUserLocalId(currUser.getName()); + if (UserAdminUtils.isCurrentUser(currUser)) { + MessageDialog.openError(Display.getCurrent().getActiveShell(), "Deletion forbidden", + "You cannot delete your own user this way."); + return; + } + builder.append(userName).append("; "); +// users.add(currUser); + } + + if (!MessageDialog.openQuestion(Display.getCurrent().getActiveShell(), "Delete Users", + "Are you sure that you want to delete these users?\n" + builder.substring(0, builder.length() - 2))) + return; + + userAdminWrapper.beginTransactionIfNeeded(); + UserAdmin userAdmin = userAdminWrapper.getUserAdmin(); + // IWorkbenchPage iwp = + // HandlerUtil.getActiveWorkbenchWindow(event).getActivePage(); + + for (User user : selection) { + String userName = user.getName(); + // TODO find a way to close the editor cleanly if opened. Cannot be + // done through the UserAdminListeners, it causes a + // java.util.ConcurrentModificationException because disposing the + // editor unregisters and disposes the listener + // IEditorPart part = iwp.findEditor(new UserEditorInput(userName)); + // if (part != null) + // iwp.closeEditor(part, false); + userAdmin.removeRole(userName); + } + userAdminWrapper.commitOrNotifyTransactionStateChange(); + + for (User user : selection) { + userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_REMOVED, user)); + } + } + + @CanExecute + public boolean canExecute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) { + return part.getObject() instanceof UsersView && selectionService.getSelection() != null; + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewGroup.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewGroup.java new file mode 100644 index 000000000..41e14e097 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewGroup.java @@ -0,0 +1,212 @@ +package org.argeo.cms.e4.users.handlers; + +import java.util.Dictionary; +import java.util.Map; + +import javax.inject.Inject; + +import org.argeo.cms.e4.users.UserAdminWrapper; +import org.argeo.cms.swt.CmsException; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.dialogs.ErrorFeedback; +import org.argeo.util.directory.DirectoryConf; +import org.argeo.util.naming.LdapAttrs; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.jface.wizard.Wizard; +import org.eclipse.jface.wizard.WizardDialog; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.osgi.service.useradmin.Group; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.UserAdminEvent; + +/** Create a new group */ +public class NewGroup { + // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".newGroup"; + + /* DEPENDENCY INJECTION */ + @Inject + private UserAdminWrapper userAdminWrapper; + + @Execute + public Object execute() { + NewGroupWizard newGroupWizard = new NewGroupWizard(); + newGroupWizard.setWindowTitle("Group creation"); + WizardDialog dialog = new WizardDialog(Display.getCurrent().getActiveShell(), newGroupWizard); + dialog.open(); + return null; + } + + private class NewGroupWizard extends Wizard { + + // Pages + private MainGroupInfoWizardPage mainGroupInfo; + + // UI fields + private Text dNameTxt, commonNameTxt, descriptionTxt; + private Combo baseDnCmb; + + public NewGroupWizard() { + } + + @Override + public void addPages() { + mainGroupInfo = new MainGroupInfoWizardPage(); + addPage(mainGroupInfo); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public boolean performFinish() { + if (!canFinish()) + return false; + String commonName = commonNameTxt.getText(); + try { + userAdminWrapper.beginTransactionIfNeeded(); + String dn = getDn(commonName); + Group group = (Group) userAdminWrapper.getUserAdmin().createRole(dn, Role.GROUP); + Dictionary props = group.getProperties(); + String descStr = descriptionTxt.getText(); + if (EclipseUiUtils.notEmpty(descStr)) + props.put(LdapAttrs.description.name(), descStr); + userAdminWrapper.commitOrNotifyTransactionStateChange(); + userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CREATED, group)); + return true; + } catch (Exception e) { + ErrorFeedback.show("Cannot create new group " + commonName, e); + return false; + } + } + + private class MainGroupInfoWizardPage extends WizardPage implements FocusListener { + private static final long serialVersionUID = -3150193365151601807L; + + public MainGroupInfoWizardPage() { + super("Main"); + setTitle("General information"); + setMessage("Please choose a domain, provide a common name " + "and a free description"); + } + + @Override + public void createControl(Composite parent) { + Composite bodyCmp = new Composite(parent, SWT.NONE); + setControl(bodyCmp); + bodyCmp.setLayout(new GridLayout(2, false)); + + dNameTxt = EclipseUiUtils.createGridLT(bodyCmp, "Distinguished name"); + dNameTxt.setEnabled(false); + + baseDnCmb = createGridLC(bodyCmp, "Base DN"); + // Initialise before adding the listener to avoid NPE + initialiseDnCmb(baseDnCmb); + baseDnCmb.addFocusListener(this); + + commonNameTxt = EclipseUiUtils.createGridLT(bodyCmp, "Common name"); + commonNameTxt.addFocusListener(this); + + Label descLbl = new Label(bodyCmp, SWT.LEAD); + descLbl.setText("Description"); + descLbl.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false)); + descriptionTxt = new Text(bodyCmp, SWT.LEAD | SWT.MULTI | SWT.WRAP | SWT.BORDER); + descriptionTxt.setLayoutData(EclipseUiUtils.fillAll()); + descriptionTxt.addFocusListener(this); + + // Initialize buttons + setPageComplete(false); + getContainer().updateButtons(); + } + + @Override + public void focusLost(FocusEvent event) { + String name = commonNameTxt.getText(); + if (EclipseUiUtils.isEmpty(name)) + dNameTxt.setText(""); + else + dNameTxt.setText(getDn(name)); + + String message = checkComplete(); + if (message != null) { + setMessage(message, WizardPage.ERROR); + setPageComplete(false); + } else { + setMessage("Complete", WizardPage.INFORMATION); + setPageComplete(true); + } + getContainer().updateButtons(); + } + + @Override + public void focusGained(FocusEvent event) { + } + + /** @return the error message or null if complete */ + protected String checkComplete() { + String name = commonNameTxt.getText(); + + if (name.trim().equals("")) + return "Common name must not be empty"; + Role role = userAdminWrapper.getUserAdmin().getRole(getDn(name)); + if (role != null) + return "Group " + name + " already exists"; + return null; + } + + @Override + public void setVisible(boolean visible) { + super.setVisible(visible); + if (visible) + if (baseDnCmb.getSelectionIndex() == -1) + baseDnCmb.setFocus(); + else + commonNameTxt.setFocus(); + } + } + + private Map getDns() { + return userAdminWrapper.getKnownBaseDns(true); + } + + private String getDn(String cn) { + Map dns = getDns(); + String bdn = baseDnCmb.getText(); + if (EclipseUiUtils.notEmpty(bdn)) { + Dictionary props = DirectoryConf.uriAsProperties(dns.get(bdn)); + String dn = LdapAttrs.cn.name() + "=" + cn + "," + DirectoryConf.groupBase.getValue(props) + "," + bdn; + return dn; + } + return null; + } + + private void initialiseDnCmb(Combo combo) { + Map dns = userAdminWrapper.getKnownBaseDns(true); + if (dns.isEmpty()) + throw new CmsException("No writable base dn found. Cannot create group"); + combo.setItems(dns.keySet().toArray(new String[0])); + if (dns.size() == 1) + combo.select(0); + } + } + + private Combo createGridLC(Composite parent, String label) { + Label lbl = new Label(parent, SWT.LEAD); + lbl.setText(label); + lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); + Combo combo = new Combo(parent, SWT.LEAD | SWT.BORDER | SWT.READ_ONLY); + combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + return combo; + } + + /* DEPENDENCY INJECTION */ + public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { + this.userAdminWrapper = userAdminWrapper; + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewUser.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewUser.java new file mode 100644 index 000000000..40a446006 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewUser.java @@ -0,0 +1,287 @@ +package org.argeo.cms.e4.users.handlers; + +import java.util.Dictionary; +import java.util.List; +import java.util.Map; + +import javax.inject.Inject; +import javax.naming.InvalidNameException; +import javax.naming.ldap.LdapName; +import javax.naming.ldap.Rdn; + +import org.argeo.cms.auth.UserAdminUtils; +import org.argeo.cms.e4.users.UiAdminUtils; +import org.argeo.cms.e4.users.UserAdminWrapper; +import org.argeo.cms.swt.CmsException; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.dialogs.ErrorFeedback; +import org.argeo.util.directory.DirectoryConf; +import org.argeo.util.naming.LdapAttrs; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.jface.wizard.Wizard; +import org.eclipse.jface.wizard.WizardDialog; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.User; +import org.osgi.service.useradmin.UserAdminEvent; + +/** Open a wizard that enables creation of a new user. */ +public class NewUser { + // private final static Log log = LogFactory.getLog(NewUser.class); + // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".newUser"; + + /* DEPENDENCY INJECTION */ + @Inject + private UserAdminWrapper userAdminWrapper; + + @Execute + public Object execute() { + NewUserWizard newUserWizard = new NewUserWizard(); + newUserWizard.setWindowTitle("User creation"); + WizardDialog dialog = new WizardDialog(Display.getCurrent().getActiveShell(), newUserWizard); + dialog.open(); + return null; + } + + private class NewUserWizard extends Wizard { + + // pages + private MainUserInfoWizardPage mainUserInfo; + + // End user fields + private Text dNameTxt, usernameTxt, firstNameTxt, lastNameTxt, primaryMailTxt, pwd1Txt, pwd2Txt; + private Combo baseDnCmb; + + public NewUserWizard() { + + } + + @Override + public void addPages() { + mainUserInfo = new MainUserInfoWizardPage(); + addPage(mainUserInfo); + String message = "Default wizard that also eases user creation tests:\n " + + "Mail and last name are automatically " + + "generated form the uid. Password are defauted to 'demo'."; + mainUserInfo.setMessage(message, WizardPage.WARNING); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public boolean performFinish() { + if (!canFinish()) + return false; + String username = mainUserInfo.getUsername(); + userAdminWrapper.beginTransactionIfNeeded(); + try { + User user = (User) userAdminWrapper.getUserAdmin().createRole(getDn(username), Role.USER); + + Dictionary props = user.getProperties(); + + String lastNameStr = lastNameTxt.getText(); + if (EclipseUiUtils.notEmpty(lastNameStr)) + props.put(LdapAttrs.sn.name(), lastNameStr); + + String firstNameStr = firstNameTxt.getText(); + if (EclipseUiUtils.notEmpty(firstNameStr)) + props.put(LdapAttrs.givenName.name(), firstNameStr); + + String cn = UserAdminUtils.buildDefaultCn(firstNameStr, lastNameStr); + if (EclipseUiUtils.notEmpty(cn)) + props.put(LdapAttrs.cn.name(), cn); + + String mailStr = primaryMailTxt.getText(); + if (EclipseUiUtils.notEmpty(mailStr)) + props.put(LdapAttrs.mail.name(), mailStr); + + char[] password = mainUserInfo.getPassword(); + user.getCredentials().put(null, password); + userAdminWrapper.commitOrNotifyTransactionStateChange(); + userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CREATED, user)); + return true; + } catch (Exception e) { + ErrorFeedback.show("Cannot create new user " + username, e); + return false; + } + } + + private class MainUserInfoWizardPage extends WizardPage implements ModifyListener { + private static final long serialVersionUID = -3150193365151601807L; + + public MainUserInfoWizardPage() { + super("Main"); + setTitle("Required Information"); + } + + @Override + public void createControl(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + composite.setLayout(new GridLayout(2, false)); + dNameTxt = EclipseUiUtils.createGridLT(composite, "Distinguished name", this); + dNameTxt.setEnabled(false); + + baseDnCmb = createGridLC(composite, "Base DN"); + initialiseDnCmb(baseDnCmb); + baseDnCmb.addModifyListener(this); + baseDnCmb.addModifyListener(new ModifyListener() { + private static final long serialVersionUID = -1435351236582736843L; + + @Override + public void modifyText(ModifyEvent event) { + String name = usernameTxt.getText(); + dNameTxt.setText(getDn(name)); + } + }); + + usernameTxt = EclipseUiUtils.createGridLT(composite, "Local ID", this); + usernameTxt.addModifyListener(new ModifyListener() { + private static final long serialVersionUID = -1435351236582736843L; + + @Override + public void modifyText(ModifyEvent event) { + String name = usernameTxt.getText(); + if (name.trim().equals("")) { + dNameTxt.setText(""); + lastNameTxt.setText(""); + primaryMailTxt.setText(""); + pwd1Txt.setText(""); + pwd2Txt.setText(""); + } else { + dNameTxt.setText(getDn(name)); + lastNameTxt.setText(name.toUpperCase()); + primaryMailTxt.setText(getMail(name)); + pwd1Txt.setText("demo"); + pwd2Txt.setText("demo"); + } + } + }); + + primaryMailTxt = EclipseUiUtils.createGridLT(composite, "Email", this); + firstNameTxt = EclipseUiUtils.createGridLT(composite, "First name", this); + lastNameTxt = EclipseUiUtils.createGridLT(composite, "Last name", this); + pwd1Txt = EclipseUiUtils.createGridLP(composite, "Password", this); + pwd2Txt = EclipseUiUtils.createGridLP(composite, "Repeat password", this); + setControl(composite); + + // Initialize buttons + setPageComplete(false); + getContainer().updateButtons(); + } + + @Override + public void modifyText(ModifyEvent event) { + String message = checkComplete(); + if (message != null) { + setMessage(message, WizardPage.ERROR); + setPageComplete(false); + } else { + setMessage("Complete", WizardPage.INFORMATION); + setPageComplete(true); + } + getContainer().updateButtons(); + } + + /** @return error message or null if complete */ + protected String checkComplete() { + String name = usernameTxt.getText(); + + if (name.trim().equals("")) + return "User name must not be empty"; + Role role = userAdminWrapper.getUserAdmin().getRole(getDn(name)); + if (role != null) + return "User " + name + " already exists"; + if (!primaryMailTxt.getText().matches(UiAdminUtils.EMAIL_PATTERN)) + return "Not a valid email address"; + if (lastNameTxt.getText().trim().equals("")) + return "Specify a last name"; + if (pwd1Txt.getText().trim().equals("")) + return "Specify a password"; + if (pwd2Txt.getText().trim().equals("")) + return "Repeat the password"; + if (!pwd2Txt.getText().equals(pwd1Txt.getText())) + return "Passwords are different"; + return null; + } + + @Override + public void setVisible(boolean visible) { + super.setVisible(visible); + if (visible) + if (baseDnCmb.getSelectionIndex() == -1) + baseDnCmb.setFocus(); + else + usernameTxt.setFocus(); + } + + public String getUsername() { + return usernameTxt.getText(); + } + + public char[] getPassword() { + return pwd1Txt.getTextChars(); + } + + } + + private Map getDns() { + return userAdminWrapper.getKnownBaseDns(true); + } + + private String getDn(String uid) { + Map dns = getDns(); + String bdn = baseDnCmb.getText(); + if (EclipseUiUtils.notEmpty(bdn)) { + Dictionary props = DirectoryConf.uriAsProperties(dns.get(bdn)); + String dn = LdapAttrs.uid.name() + "=" + uid + "," + DirectoryConf.userBase.getValue(props) + "," + bdn; + return dn; + } + return null; + } + + private void initialiseDnCmb(Combo combo) { + Map dns = userAdminWrapper.getKnownBaseDns(true); + if (dns.isEmpty()) + throw new CmsException("No writable base dn found. Cannot create user"); + combo.setItems(dns.keySet().toArray(new String[0])); + if (dns.size() == 1) + combo.select(0); + } + + private String getMail(String username) { + if (baseDnCmb.getSelectionIndex() == -1) + return null; + String baseDn = baseDnCmb.getText(); + try { + LdapName name = new LdapName(baseDn); + List rdns = name.getRdns(); + return username + "@" + (String) rdns.get(1).getValue() + '.' + (String) rdns.get(0).getValue(); + } catch (InvalidNameException e) { + throw new CmsException("Unable to generate mail for " + username + " with base dn " + baseDn, e); + } + } + } + + private Combo createGridLC(Composite parent, String label) { + Label lbl = new Label(parent, SWT.LEAD); + lbl.setText(label); + lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); + Combo combo = new Combo(parent, SWT.LEAD | SWT.BORDER | SWT.READ_ONLY); + combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + return combo; + } + + /* DEPENDENCY INJECTION */ + public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) { + this.userAdminWrapper = userAdminWrapper; + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/package-info.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/package-info.java new file mode 100644 index 000000000..cf3db1d16 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/package-info.java @@ -0,0 +1,2 @@ +/** Users management handlers. */ +package org.argeo.cms.e4.users.handlers; \ No newline at end of file diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/package-info.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/package-info.java new file mode 100644 index 000000000..c6f14b0cf --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/package-info.java @@ -0,0 +1,2 @@ +/** Users management perspective. */ +package org.argeo.cms.e4.users; \ No newline at end of file diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/CommonNameLP.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/CommonNameLP.java new file mode 100644 index 000000000..2d8db67d7 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/CommonNameLP.java @@ -0,0 +1,21 @@ +package org.argeo.cms.e4.users.providers; + +import org.argeo.cms.auth.UserAdminUtils; +import org.argeo.util.naming.LdapAttrs; +import org.osgi.service.useradmin.User; + +/** Simply declare a label provider that returns the common name of a user */ +public class CommonNameLP extends UserAdminAbstractLP { + private static final long serialVersionUID = 5256703081044911941L; + + @Override + public String getText(User user) { + return UserAdminUtils.getProperty(user, LdapAttrs.cn.name()); + } + + @Override + public String getToolTipText(Object element) { + return UserAdminUtils.getProperty((User) element, LdapAttrs.DN); + } + +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/DomainNameLP.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/DomainNameLP.java new file mode 100644 index 000000000..e23729da8 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/DomainNameLP.java @@ -0,0 +1,14 @@ +package org.argeo.cms.e4.users.providers; + +import org.argeo.cms.auth.UserAdminUtils; +import org.osgi.service.useradmin.User; + +/** The human friendly domain name for the corresponding user. */ +public class DomainNameLP extends UserAdminAbstractLP { + private static final long serialVersionUID = 5256703081044911941L; + + @Override + public String getText(User user) { + return UserAdminUtils.getDomainName(user); + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/MailLP.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/MailLP.java new file mode 100644 index 000000000..52d3b858f --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/MailLP.java @@ -0,0 +1,15 @@ +package org.argeo.cms.e4.users.providers; + +import org.argeo.cms.auth.UserAdminUtils; +import org.argeo.util.naming.LdapAttrs; +import org.osgi.service.useradmin.User; + +/** Simply declare a label provider that returns the Primary Mail of a user */ +public class MailLP extends UserAdminAbstractLP { + private static final long serialVersionUID = 8329764452141982707L; + + @Override + public String getText(User user) { + return UserAdminUtils.getProperty(user, LdapAttrs.mail.name()); + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/RoleIconLP.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/RoleIconLP.java new file mode 100644 index 000000000..8c94093e4 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/RoleIconLP.java @@ -0,0 +1,35 @@ +package org.argeo.cms.e4.users.providers; + +import org.argeo.api.cms.CmsContext; +import org.argeo.api.cms.CmsConstants; +import org.argeo.cms.auth.UserAdminUtils; +import org.argeo.cms.e4.users.SecurityAdminImages; +import org.argeo.util.naming.LdapAttrs; +import org.eclipse.swt.graphics.Image; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.User; + +/** Provide a bundle specific image depending on the current user type */ +public class RoleIconLP extends UserAdminAbstractLP { + private static final long serialVersionUID = 6550449442061090388L; + + @Override + public String getText(User user) { + return ""; + } + + @Override + public Image getImage(Object element) { + User user = (User) element; + String dn = user.getName(); + if (dn.endsWith(CmsConstants.ROLES_BASEDN)) + return SecurityAdminImages.ICON_ROLE; + else if (user.getType() == Role.GROUP) { + String businessCategory = UserAdminUtils.getProperty(user, LdapAttrs.businessCategory); + if (businessCategory != null && businessCategory.equals(CmsContext.WORKGROUP)) + return SecurityAdminImages.ICON_WORKGROUP; + return SecurityAdminImages.ICON_GROUP; + } else + return SecurityAdminImages.ICON_USER; + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserAdminAbstractLP.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserAdminAbstractLP.java new file mode 100644 index 000000000..29873db2f --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserAdminAbstractLP.java @@ -0,0 +1,66 @@ +package org.argeo.cms.e4.users.providers; + +import javax.naming.InvalidNameException; +import javax.naming.ldap.LdapName; + +import org.argeo.cms.auth.UserAdminUtils; +import org.argeo.cms.swt.CmsException; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.widgets.Display; +import org.osgi.service.useradmin.User; + +/** + * Utility class that add font modifications to a column label provider + * depending on the given user properties + */ +public abstract class UserAdminAbstractLP extends ColumnLabelProvider { + private static final long serialVersionUID = 137336765024922368L; + + // private Font italic; + private Font bold; + + @Override + public Font getFont(Object element) { + // Self as bold + try { + LdapName selfUserName = UserAdminUtils.getCurrentUserLdapName(); + String userName = ((User) element).getName(); + LdapName userLdapName = new LdapName(userName); + if (userLdapName.equals(selfUserName)) { + if (bold == null) + bold = JFaceResources.getFontRegistry() + .defaultFontDescriptor().setStyle(SWT.BOLD) + .createFont(Display.getCurrent()); + return bold; + } + } catch (InvalidNameException e) { + throw new CmsException("cannot parse dn for " + element, e); + } + + // Disabled as Italic + // Node userProfile = (Node) elem; + // if (!userProfile.getProperty(ARGEO_ENABLED).getBoolean()) + // return italic; + + return null; + // return super.getFont(element); + } + + @Override + public String getText(Object element) { + User user = (User) element; + return getText(user); + } + + public void setDisplay(Display display) { + // italic = JFaceResources.getFontRegistry().defaultFontDescriptor() + // .setStyle(SWT.ITALIC).createFont(display); + bold = JFaceResources.getFontRegistry().defaultFontDescriptor() + .setStyle(SWT.BOLD).createFont(Display.getCurrent()); + } + + public abstract String getText(User user); +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserDragListener.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserDragListener.java new file mode 100644 index 000000000..56a26244b --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserDragListener.java @@ -0,0 +1,40 @@ +package org.argeo.cms.e4.users.providers; + +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.dnd.DragSourceEvent; +import org.eclipse.swt.dnd.DragSourceListener; +import org.osgi.service.useradmin.User; + +/** Default drag listener to modify group and users via the UI */ +public class UserDragListener implements DragSourceListener { + private static final long serialVersionUID = -2074337775033781454L; + private final Viewer viewer; + + public UserDragListener(Viewer viewer) { + this.viewer = viewer; + } + + public void dragStart(DragSourceEvent event) { + // TODO implement finer checks + IStructuredSelection selection = (IStructuredSelection) viewer + .getSelection(); + if (selection.isEmpty() || selection.size() > 1) + event.doit = false; + else + event.doit = true; + } + + public void dragSetData(DragSourceEvent event) { + // TODO Support multiple selection + Object obj = ((IStructuredSelection) viewer.getSelection()) + .getFirstElement(); + if (obj != null) { + User user = (User) obj; + event.data = user.getName(); + } + } + + public void dragFinished(DragSourceEvent event) { + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserFilter.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserFilter.java new file mode 100644 index 000000000..154b04725 --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserFilter.java @@ -0,0 +1,58 @@ +package org.argeo.cms.e4.users.providers; + +import static org.argeo.eclipse.ui.EclipseUiUtils.notEmpty; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.cms.auth.UserAdminUtils; +import org.argeo.util.naming.LdapAttrs; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerFilter; +import org.osgi.service.useradmin.User; + +/** + * Filter user list using JFace mechanism on the client (yet on the server) side + * rather than having the UserAdmin to process the search + */ +public class UserFilter extends ViewerFilter { + private static final long serialVersionUID = 5082509381672880568L; + + private String searchString; + private boolean showSystemRole = true; + + private final String[] knownProps = { LdapAttrs.DN, LdapAttrs.cn.name(), LdapAttrs.givenName.name(), + LdapAttrs.sn.name(), LdapAttrs.uid.name(), LdapAttrs.description.name(), LdapAttrs.mail.name() }; + + public void setSearchText(String s) { + // ensure that the value can be used for matching + if (notEmpty(s)) + searchString = ".*" + s.toLowerCase() + ".*"; + else + searchString = ".*"; + } + + public void setShowSystemRole(boolean showSystemRole) { + this.showSystemRole = showSystemRole; + } + + @Override + public boolean select(Viewer viewer, Object parentElement, Object element) { + User user = (User) element; + if (!showSystemRole && user.getName().matches(".*(" + CmsConstants.ROLES_BASEDN + ")")) + // UserAdminUtils.getProperty(user, LdifName.dn.name()) + // .toLowerCase().endsWith(AuthConstants.ROLES_BASEDN)) + return false; + + if (searchString == null || searchString.length() == 0) + return true; + + if (user.getName().matches(searchString)) + return true; + + for (String key : knownProps) { + String currVal = UserAdminUtils.getProperty(user, key); + if (notEmpty(currVal) && currVal.toLowerCase().matches(searchString)) + return true; + } + return false; + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserNameLP.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserNameLP.java new file mode 100644 index 000000000..3cd00eb2b --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserNameLP.java @@ -0,0 +1,13 @@ +package org.argeo.cms.e4.users.providers; + +import org.osgi.service.useradmin.User; + +/** Simply declare a label provider that returns the username of a user */ +public class UserNameLP extends UserAdminAbstractLP { + private static final long serialVersionUID = 6550449442061090388L; + + @Override + public String getText(User user) { + return user.getName(); + } +} diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/package-info.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/package-info.java new file mode 100644 index 000000000..33bef8dee --- /dev/null +++ b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/package-info.java @@ -0,0 +1,2 @@ +/** Users management content providers. */ +package org.argeo.cms.e4.users.providers; \ No newline at end of file diff --git a/swt/org.argeo.cms.swt/.classpath b/swt/org.argeo.cms.swt/.classpath new file mode 100644 index 000000000..e03d341b1 --- /dev/null +++ b/swt/org.argeo.cms.swt/.classpath @@ -0,0 +1,9 @@ + + + + + + + diff --git a/swt/org.argeo.cms.swt/.project b/swt/org.argeo.cms.swt/.project new file mode 100644 index 000000000..8ac021b59 --- /dev/null +++ b/swt/org.argeo.cms.swt/.project @@ -0,0 +1,33 @@ + + + org.argeo.cms.swt + + + + + + 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/swt/org.argeo.cms.swt/META-INF/.gitignore b/swt/org.argeo.cms.swt/META-INF/.gitignore new file mode 100644 index 000000000..4854a41b9 --- /dev/null +++ b/swt/org.argeo.cms.swt/META-INF/.gitignore @@ -0,0 +1 @@ +/MANIFEST.MF diff --git a/swt/org.argeo.cms.swt/OSGI-INF/cmsUserApp.xml b/swt/org.argeo.cms.swt/OSGI-INF/cmsUserApp.xml new file mode 100644 index 000000000..4f2a405d5 --- /dev/null +++ b/swt/org.argeo.cms.swt/OSGI-INF/cmsUserApp.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/swt/org.argeo.cms.swt/bnd.bnd b/swt/org.argeo.cms.swt/bnd.bnd new file mode 100644 index 000000000..2dda08b2a --- /dev/null +++ b/swt/org.argeo.cms.swt/bnd.bnd @@ -0,0 +1,11 @@ +Import-Package: org.eclipse.swt,\ +org.eclipse.jface.window,\ +org.eclipse.core.commands.common,\ +javax.servlet.*;version="[3,5)",\ +* + +Bundle-ActivationPolicy: lazy + +Service-Component: \ +OSGI-INF/cmsUserApp.xml + \ No newline at end of file diff --git a/swt/org.argeo.cms.swt/build.properties b/swt/org.argeo.cms.swt/build.properties new file mode 100644 index 000000000..5f0f21af9 --- /dev/null +++ b/swt/org.argeo.cms.swt/build.properties @@ -0,0 +1,5 @@ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + OSGI-INF/cmsUserApp.xml +source.. = src/ diff --git a/swt/org.argeo.cms.swt/icons/actions/add.png b/swt/org.argeo.cms.swt/icons/actions/add.png new file mode 100644 index 000000000..5c06bf082 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/actions/add.png differ diff --git a/swt/org.argeo.cms.swt/icons/actions/close-all.png b/swt/org.argeo.cms.swt/icons/actions/close-all.png new file mode 100644 index 000000000..81bfc950b Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/actions/close-all.png differ diff --git a/swt/org.argeo.cms.swt/icons/actions/delete.png b/swt/org.argeo.cms.swt/icons/actions/delete.png new file mode 100644 index 000000000..9712723d7 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/actions/delete.png differ diff --git a/swt/org.argeo.cms.swt/icons/actions/edit.png b/swt/org.argeo.cms.swt/icons/actions/edit.png new file mode 100644 index 000000000..ad3db9f42 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/actions/edit.png differ diff --git a/swt/org.argeo.cms.swt/icons/actions/save-all.png b/swt/org.argeo.cms.swt/icons/actions/save-all.png new file mode 100644 index 000000000..f48ed320b Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/actions/save-all.png differ diff --git a/swt/org.argeo.cms.swt/icons/actions/save.png b/swt/org.argeo.cms.swt/icons/actions/save.png new file mode 100644 index 000000000..1c58ada49 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/actions/save.png differ diff --git a/swt/org.argeo.cms.swt/icons/active.gif b/swt/org.argeo.cms.swt/icons/active.gif new file mode 100644 index 000000000..7d24707ee Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/active.gif differ diff --git a/swt/org.argeo.cms.swt/icons/add.gif b/swt/org.argeo.cms.swt/icons/add.gif new file mode 100644 index 000000000..252d7ebcb Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/add.gif differ diff --git a/swt/org.argeo.cms.swt/icons/add.png b/swt/org.argeo.cms.swt/icons/add.png new file mode 100644 index 000000000..c7edfecaa Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/add.png differ diff --git a/swt/org.argeo.cms.swt/icons/addFolder.gif b/swt/org.argeo.cms.swt/icons/addFolder.gif new file mode 100644 index 000000000..d3f43d977 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/addFolder.gif differ diff --git a/swt/org.argeo.cms.swt/icons/addPrivileges.gif b/swt/org.argeo.cms.swt/icons/addPrivileges.gif new file mode 100644 index 000000000..a6b251fc8 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/addPrivileges.gif differ diff --git a/swt/org.argeo.cms.swt/icons/addRepo.gif b/swt/org.argeo.cms.swt/icons/addRepo.gif new file mode 100644 index 000000000..26d81c065 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/addRepo.gif differ diff --git a/swt/org.argeo.cms.swt/icons/addWorkspace.png b/swt/org.argeo.cms.swt/icons/addWorkspace.png new file mode 100644 index 000000000..bbee7755f Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/addWorkspace.png differ diff --git a/swt/org.argeo.cms.swt/icons/adminLog.gif b/swt/org.argeo.cms.swt/icons/adminLog.gif new file mode 100644 index 000000000..6ef3bca66 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/adminLog.gif differ diff --git a/swt/org.argeo.cms.swt/icons/batch.gif b/swt/org.argeo.cms.swt/icons/batch.gif new file mode 100644 index 000000000..b8ca14a8b Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/batch.gif differ diff --git a/swt/org.argeo.cms.swt/icons/begin.gif b/swt/org.argeo.cms.swt/icons/begin.gif new file mode 100755 index 000000000..feb8e94a7 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/begin.gif differ diff --git a/swt/org.argeo.cms.swt/icons/binary.png b/swt/org.argeo.cms.swt/icons/binary.png new file mode 100644 index 000000000..fdf4f82be Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/binary.png differ diff --git a/swt/org.argeo.cms.swt/icons/browser.gif b/swt/org.argeo.cms.swt/icons/browser.gif new file mode 100644 index 000000000..6c7320c69 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/browser.gif differ diff --git a/swt/org.argeo.cms.swt/icons/bundles.gif b/swt/org.argeo.cms.swt/icons/bundles.gif new file mode 100644 index 000000000..e9a6bd966 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/bundles.gif differ diff --git a/swt/org.argeo.cms.swt/icons/changePassword.gif b/swt/org.argeo.cms.swt/icons/changePassword.gif new file mode 100644 index 000000000..274a850e4 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/changePassword.gif differ diff --git a/swt/org.argeo.cms.swt/icons/clear.gif b/swt/org.argeo.cms.swt/icons/clear.gif new file mode 100644 index 000000000..6bc10f9d0 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/clear.gif differ diff --git a/swt/org.argeo.cms.swt/icons/close-all.png b/swt/org.argeo.cms.swt/icons/close-all.png new file mode 100644 index 000000000..85d4d429b Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/close-all.png differ diff --git a/swt/org.argeo.cms.swt/icons/commit.gif b/swt/org.argeo.cms.swt/icons/commit.gif new file mode 100755 index 000000000..876f3eb16 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/commit.gif differ diff --git a/swt/org.argeo.cms.swt/icons/delete.png b/swt/org.argeo.cms.swt/icons/delete.png new file mode 100644 index 000000000..676a39dcf Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/delete.png differ diff --git a/swt/org.argeo.cms.swt/icons/dumpNode.gif b/swt/org.argeo.cms.swt/icons/dumpNode.gif new file mode 100644 index 000000000..14eb1be09 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/dumpNode.gif differ diff --git a/swt/org.argeo.cms.swt/icons/file.gif b/swt/org.argeo.cms.swt/icons/file.gif new file mode 100644 index 000000000..ef3028807 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/file.gif differ diff --git a/swt/org.argeo.cms.swt/icons/folder.gif b/swt/org.argeo.cms.swt/icons/folder.gif new file mode 100644 index 000000000..42e027c93 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/folder.gif differ diff --git a/swt/org.argeo.cms.swt/icons/getSize.gif b/swt/org.argeo.cms.swt/icons/getSize.gif new file mode 100644 index 000000000..b05bf3e3d Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/getSize.gif differ diff --git a/swt/org.argeo.cms.swt/icons/group.png b/swt/org.argeo.cms.swt/icons/group.png new file mode 100644 index 000000000..cc6683aff Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/group.png differ diff --git a/swt/org.argeo.cms.swt/icons/home.gif b/swt/org.argeo.cms.swt/icons/home.gif new file mode 100644 index 000000000..fd0c66950 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/home.gif differ diff --git a/swt/org.argeo.cms.swt/icons/home.png b/swt/org.argeo.cms.swt/icons/home.png new file mode 100644 index 000000000..5eb096790 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/home.png differ diff --git a/swt/org.argeo.cms.swt/icons/import_fs.png b/swt/org.argeo.cms.swt/icons/import_fs.png new file mode 100644 index 000000000..d7c890c81 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/import_fs.png differ diff --git a/swt/org.argeo.cms.swt/icons/installed.gif b/swt/org.argeo.cms.swt/icons/installed.gif new file mode 100644 index 000000000..298871653 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/installed.gif differ diff --git a/swt/org.argeo.cms.swt/icons/log.gif b/swt/org.argeo.cms.swt/icons/log.gif new file mode 100644 index 000000000..e3ecc5535 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/log.gif differ diff --git a/swt/org.argeo.cms.swt/icons/logout.png b/swt/org.argeo.cms.swt/icons/logout.png new file mode 100644 index 000000000..f2952fa5b Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/logout.png differ diff --git a/swt/org.argeo.cms.swt/icons/maintenance.gif b/swt/org.argeo.cms.swt/icons/maintenance.gif new file mode 100644 index 000000000..e5690ecb1 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/maintenance.gif differ diff --git a/swt/org.argeo.cms.swt/icons/node.gif b/swt/org.argeo.cms.swt/icons/node.gif new file mode 100644 index 000000000..364c0e70b Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/node.gif differ diff --git a/swt/org.argeo.cms.swt/icons/nodes.gif b/swt/org.argeo.cms.swt/icons/nodes.gif new file mode 100644 index 000000000..bba3dbc69 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/nodes.gif differ diff --git a/swt/org.argeo.cms.swt/icons/osgi_explorer.gif b/swt/org.argeo.cms.swt/icons/osgi_explorer.gif new file mode 100644 index 000000000..e9a6bd966 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/osgi_explorer.gif differ diff --git a/swt/org.argeo.cms.swt/icons/password.gif b/swt/org.argeo.cms.swt/icons/password.gif new file mode 100644 index 000000000..a6b251fc8 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/password.gif differ diff --git a/swt/org.argeo.cms.swt/icons/person-logged-in.png b/swt/org.argeo.cms.swt/icons/person-logged-in.png new file mode 100644 index 000000000..87acc1435 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/person-logged-in.png differ diff --git a/swt/org.argeo.cms.swt/icons/person.png b/swt/org.argeo.cms.swt/icons/person.png new file mode 100644 index 000000000..7d979a531 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/person.png differ diff --git a/swt/org.argeo.cms.swt/icons/query.png b/swt/org.argeo.cms.swt/icons/query.png new file mode 100644 index 000000000..54c089de1 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/query.png differ diff --git a/swt/org.argeo.cms.swt/icons/refresh.png b/swt/org.argeo.cms.swt/icons/refresh.png new file mode 100644 index 000000000..71b3481c9 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/refresh.png differ diff --git a/swt/org.argeo.cms.swt/icons/remote_connected.gif b/swt/org.argeo.cms.swt/icons/remote_connected.gif new file mode 100644 index 000000000..1492b4efa Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/remote_connected.gif differ diff --git a/swt/org.argeo.cms.swt/icons/remote_disconnected.gif b/swt/org.argeo.cms.swt/icons/remote_disconnected.gif new file mode 100644 index 000000000..6c54da9ad Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/remote_disconnected.gif differ diff --git a/swt/org.argeo.cms.swt/icons/remove.gif b/swt/org.argeo.cms.swt/icons/remove.gif new file mode 100644 index 000000000..0ae6decd0 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/remove.gif differ diff --git a/swt/org.argeo.cms.swt/icons/removePrivileges.gif b/swt/org.argeo.cms.swt/icons/removePrivileges.gif new file mode 100644 index 000000000..aa78fd2fa Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/removePrivileges.gif differ diff --git a/swt/org.argeo.cms.swt/icons/rename.gif b/swt/org.argeo.cms.swt/icons/rename.gif new file mode 100644 index 000000000..8048405a7 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/rename.gif differ diff --git a/swt/org.argeo.cms.swt/icons/repositories.gif b/swt/org.argeo.cms.swt/icons/repositories.gif new file mode 100644 index 000000000..c13bea1ca Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/repositories.gif differ diff --git a/swt/org.argeo.cms.swt/icons/repository_connected.gif b/swt/org.argeo.cms.swt/icons/repository_connected.gif new file mode 100644 index 000000000..a15fa5538 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/repository_connected.gif differ diff --git a/swt/org.argeo.cms.swt/icons/repository_disconnected.gif b/swt/org.argeo.cms.swt/icons/repository_disconnected.gif new file mode 100644 index 000000000..4576dc563 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/repository_disconnected.gif differ diff --git a/swt/org.argeo.cms.swt/icons/resolved.gif b/swt/org.argeo.cms.swt/icons/resolved.gif new file mode 100644 index 000000000..f4a1ea150 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/resolved.gif differ diff --git a/swt/org.argeo.cms.swt/icons/role.gif b/swt/org.argeo.cms.swt/icons/role.gif new file mode 100644 index 000000000..274a850e4 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/role.gif differ diff --git a/swt/org.argeo.cms.swt/icons/rollback.gif b/swt/org.argeo.cms.swt/icons/rollback.gif new file mode 100755 index 000000000..c75399599 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/rollback.gif differ diff --git a/swt/org.argeo.cms.swt/icons/save-all.png b/swt/org.argeo.cms.swt/icons/save-all.png new file mode 100644 index 000000000..b68a29b2c Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/save-all.png differ diff --git a/swt/org.argeo.cms.swt/icons/save.gif b/swt/org.argeo.cms.swt/icons/save.gif new file mode 100644 index 000000000..654ad7b42 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/save.gif differ diff --git a/swt/org.argeo.cms.swt/icons/save.png b/swt/org.argeo.cms.swt/icons/save.png new file mode 100644 index 000000000..f27ef2d26 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/save.png differ diff --git a/swt/org.argeo.cms.swt/icons/save_security.png b/swt/org.argeo.cms.swt/icons/save_security.png new file mode 100644 index 000000000..ca41dc92b Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/save_security.png differ diff --git a/swt/org.argeo.cms.swt/icons/save_security_disabled.png b/swt/org.argeo.cms.swt/icons/save_security_disabled.png new file mode 100644 index 000000000..fb7d08d9a Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/save_security_disabled.png differ diff --git a/swt/org.argeo.cms.swt/icons/security.gif b/swt/org.argeo.cms.swt/icons/security.gif new file mode 100644 index 000000000..57fb95edc Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/security.gif differ diff --git a/swt/org.argeo.cms.swt/icons/service_published.gif b/swt/org.argeo.cms.swt/icons/service_published.gif new file mode 100644 index 000000000..17f771aff Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/service_published.gif differ diff --git a/swt/org.argeo.cms.swt/icons/service_referenced.gif b/swt/org.argeo.cms.swt/icons/service_referenced.gif new file mode 100644 index 000000000..c24a95fba Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/service_referenced.gif differ diff --git a/swt/org.argeo.cms.swt/icons/sort.gif b/swt/org.argeo.cms.swt/icons/sort.gif new file mode 100644 index 000000000..23c5d0b11 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/sort.gif differ diff --git a/swt/org.argeo.cms.swt/icons/starting.gif b/swt/org.argeo.cms.swt/icons/starting.gif new file mode 100644 index 000000000..563743d39 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/starting.gif differ diff --git a/swt/org.argeo.cms.swt/icons/sync.gif b/swt/org.argeo.cms.swt/icons/sync.gif new file mode 100644 index 000000000..b4fa052de Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/sync.gif differ diff --git a/swt/org.argeo.cms.swt/icons/user.gif b/swt/org.argeo.cms.swt/icons/user.gif new file mode 100644 index 000000000..90a00147b Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/user.gif differ diff --git a/swt/org.argeo.cms.swt/icons/users.gif b/swt/org.argeo.cms.swt/icons/users.gif new file mode 100644 index 000000000..2de7edd64 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/users.gif differ diff --git a/swt/org.argeo.cms.swt/icons/workgroup.png b/swt/org.argeo.cms.swt/icons/workgroup.png new file mode 100644 index 000000000..7fef996df Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/workgroup.png differ diff --git a/swt/org.argeo.cms.swt/icons/workgroup.xcf b/swt/org.argeo.cms.swt/icons/workgroup.xcf new file mode 100644 index 000000000..f517c827c Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/workgroup.xcf differ diff --git a/swt/org.argeo.cms.swt/icons/workspace_connected.png b/swt/org.argeo.cms.swt/icons/workspace_connected.png new file mode 100644 index 000000000..0430baaf5 Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/workspace_connected.png differ diff --git a/swt/org.argeo.cms.swt/icons/workspace_disconnected.png b/swt/org.argeo.cms.swt/icons/workspace_disconnected.png new file mode 100644 index 000000000..fddcb8c4e Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/workspace_disconnected.png differ diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/jface/dialog/CmsWizardDialog.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/jface/dialog/CmsWizardDialog.java new file mode 100644 index 000000000..33841a1bb --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/jface/dialog/CmsWizardDialog.java @@ -0,0 +1,222 @@ +package org.argeo.cms.jface.dialog; + +import java.lang.reflect.InvocationTargetException; + +import org.argeo.cms.CmsMsg; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.swt.Selected; +import org.argeo.cms.swt.dialogs.LightweightDialog; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.wizard.IWizard; +import org.eclipse.jface.wizard.IWizardContainer2; +import org.eclipse.jface.wizard.IWizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.FormAttachment; +import org.eclipse.swt.layout.FormData; +import org.eclipse.swt.layout.FormLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.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.Shell; + +/** A wizard dialog based on {@link LightweightDialog}. */ +public class CmsWizardDialog extends LightweightDialog implements IWizardContainer2 { + private static final long serialVersionUID = -2123153353654812154L; + + private IWizard wizard; + private IWizardPage currentPage; + private int currentPageIndex; + + private Label titleBar; + private Label message; + private Composite[] pageBodies; + private Composite buttons; + private Button back; + private Button next; + private Button finish; + + public CmsWizardDialog(Shell parentShell, IWizard wizard) { + super(parentShell); + this.wizard = wizard; + wizard.setContainer(this); + // create the pages + wizard.addPages(); + currentPage = wizard.getStartingPage(); + if (currentPage == null) + throw new IllegalArgumentException("At least one wizard page is required"); + } + + @Override + protected Control createDialogArea(Composite parent) { + updateWindowTitle(); + + Composite messageArea = new Composite(parent, SWT.NONE); + messageArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + { + messageArea.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(2, false))); + titleBar = new Label(messageArea, SWT.WRAP); + titleBar.setFont(EclipseUiUtils.getBoldFont(parent)); + titleBar.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, true, false)); + updateTitleBar(); + Button cancelButton = new Button(messageArea, SWT.FLAT); + cancelButton.setText(CmsMsg.cancel.lead()); + cancelButton.setLayoutData(new GridData(SWT.END, SWT.TOP, false, false, 1, 3)); + cancelButton.addSelectionListener((Selected) (e) -> closeShell(CANCEL)); + message = new Label(messageArea, SWT.WRAP); + message.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 2)); + updateMessage(); + } + + Composite body = new Composite(parent, SWT.BORDER); + body.setLayout(new FormLayout()); + body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + pageBodies = new Composite[wizard.getPageCount()]; + IWizardPage[] pages = wizard.getPages(); + for (int i = 0; i < pages.length; i++) { + pageBodies[i] = new Composite(body, SWT.NONE); + pageBodies[i].setLayout(CmsSwtUtils.noSpaceGridLayout()); + setSwitchingFormData(pageBodies[i]); + pages[i].createControl(pageBodies[i]); + } + showPage(currentPage); + + buttons = new Composite(parent, SWT.NONE); + buttons.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false)); + { + boolean singlePage = wizard.getPageCount() == 1; + // singlePage = false;// dev + GridLayout layout = new GridLayout(singlePage ? 1 : 3, true); + layout.marginWidth = 0; + layout.marginHeight = 0; + buttons.setLayout(layout); + // TODO revert order for right-to-left languages + + if (!singlePage) { + back = new Button(buttons, SWT.PUSH); + back.setText(CmsMsg.wizardBack.lead()); + back.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); + back.addSelectionListener((Selected) (e) -> backPressed()); + + next = new Button(buttons, SWT.PUSH); + next.setText(CmsMsg.wizardNext.lead()); + next.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); + next.addSelectionListener((Selected) (e) -> nextPressed()); + } + finish = new Button(buttons, SWT.PUSH); + finish.setText(CmsMsg.wizardFinish.lead()); + finish.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); + finish.addSelectionListener((Selected) (e) -> finishPressed()); + + updateButtons(); + } + return body; + } + + @Override + public IWizardPage getCurrentPage() { + return currentPage; + } + + @Override + public Shell getShell() { + return getForegoundShell(); + } + + @Override + public void showPage(IWizardPage page) { + IWizardPage[] pages = wizard.getPages(); + int index = -1; + for (int i = 0; i < pages.length; i++) { + if (page == pages[i]) { + index = i; + break; + } + } + if (index < 0) + throw new IllegalArgumentException("Cannot find index of wizard page " + page); + pageBodies[index].moveAbove(pageBodies[currentPageIndex]); + + // // clear + // for (Control c : body.getChildren()) + // c.dispose(); + // page.createControl(body); + // body.layout(true, true); + currentPageIndex = index; + currentPage = page; + } + + @Override + public void updateButtons() { + if (back != null) + back.setEnabled(wizard.getPreviousPage(currentPage) != null); + if (next != null) + next.setEnabled(wizard.getNextPage(currentPage) != null && currentPage.canFlipToNextPage()); + if (finish != null) { + finish.setEnabled(wizard.canFinish()); + } + } + + @Override + public void updateMessage() { + if (currentPage.getMessage() != null) + message.setText(currentPage.getMessage()); + } + + @Override + public void updateTitleBar() { + if (currentPage.getTitle() != null) + titleBar.setText(currentPage.getTitle()); + } + + @Override + public void updateWindowTitle() { + setTitle(wizard.getWindowTitle()); + } + + @Override + public void run(boolean fork, boolean cancelable, IRunnableWithProgress runnable) + throws InvocationTargetException, InterruptedException { + // FIXME it creates a dependency to Eclipse Core Runtime + // runnable.run(null); + } + + @Override + public void updateSize() { + // TODO pack? + } + + protected boolean onCancel() { + return wizard.performCancel(); + } + + protected void nextPressed() { + IWizardPage page = wizard.getNextPage(currentPage); + showPage(page); + updateButtons(); + } + + protected void backPressed() { + IWizardPage page = wizard.getPreviousPage(currentPage); + showPage(page); + updateButtons(); + } + + protected void finishPressed() { + if (wizard.performFinish()) + closeShell(OK); + } + + private static void setSwitchingFormData(Composite composite) { + FormData fdLabel = new FormData(); + fdLabel.top = new FormAttachment(0, 0); + fdLabel.left = new FormAttachment(0, 0); + fdLabel.right = new FormAttachment(100, 0); + fdLabel.bottom = new FormAttachment(100, 0); + composite.setLayoutData(fdLabel); + } + +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsException.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsException.java new file mode 100644 index 000000000..874ea9691 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsException.java @@ -0,0 +1,16 @@ +package org.argeo.cms.swt; + +/** @deprecated Use standard Java {@link RuntimeException} instead. */ +@Deprecated +public class CmsException extends RuntimeException { + private static final long serialVersionUID = -5341764743356771313L; + + public CmsException(String message) { + super(message); + } + + public CmsException(String message, Throwable e) { + super(message, e); + } + +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsStyles.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsStyles.java new file mode 100644 index 000000000..9eba6f6ec --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsStyles.java @@ -0,0 +1,32 @@ +package org.argeo.cms.swt; + +/** Styles references in the CSS. */ +@Deprecated +public interface CmsStyles { + // General + public final static String CMS_SHELL = "cms_shell"; + public final static String CMS_MENU_LINK = "cms_menu_link"; + + // Header + public final static String CMS_HEADER = "cms_header"; + public final static String CMS_HEADER_LEAD = "cms_header-lead"; + public final static String CMS_HEADER_CENTER = "cms_header-center"; + public final static String CMS_HEADER_END = "cms_header-end"; + + public final static String CMS_LEAD = "cms_lead"; + public final static String CMS_END = "cms_end"; + public final static String CMS_FOOTER = "cms_footer"; + + public final static String CMS_USER_MENU = "cms_user_menu"; + public final static String CMS_USER_MENU_LINK = "cms_user_menu-link"; + public final static String CMS_USER_MENU_ITEM = "cms_user_menu-item"; + public final static String CMS_LOGIN_DIALOG = "cms_login_dialog"; + public final static String CMS_LOGIN_DIALOG_USERNAME = "cms_login_dialog-username"; + public final static String CMS_LOGIN_DIALOG_PASSWORD = "cms_login_dialog-password"; + + // Body + public final static String CMS_SCROLLED_AREA = "cms_scrolled_area"; + public final static String CMS_BODY = "cms_body"; + public final static String CMS_STATIC_TEXT = "cms_static-text"; + public final static String CMS_LINK = "cms_link"; +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtTheme.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtTheme.java new file mode 100644 index 000000000..7669b1554 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtTheme.java @@ -0,0 +1,25 @@ +package org.argeo.cms.swt; + +import org.argeo.api.cms.ux.CmsIcon; +import org.argeo.api.cms.ux.CmsTheme; +import org.eclipse.swt.graphics.Image; + +/** SWT specific {@link CmsTheme}. */ +public interface CmsSwtTheme extends CmsTheme { +// /** The image registered at this path, or null if not found. */ +// Image getImage(String path); + + /** + * And icon with this file name (without the extension), with a best effort to + * find the appropriate size, or null if not found. + * + * @param name An icon file name without path and extension. + * @param preferredSize the preferred size, if null, + * {@link #getDefaultIconSize()} will be tried. + */ + Image getIcon(String name, Integer preferredSize); + + Image getSmallIcon(CmsIcon icon); + + Image getBigIcon(CmsIcon icon); +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtUi.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtUi.java new file mode 100644 index 000000000..2fb79f443 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtUi.java @@ -0,0 +1,17 @@ +package org.argeo.cms.swt; + +import org.argeo.api.cms.ux.CmsUi; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; + +/** A basic {@link CmsUi}, based on an SWT {@link Composite}. */ +public class CmsSwtUi extends Composite implements CmsUi { + + private static final long serialVersionUID = -107939076610406448L; + + public CmsSwtUi(Composite parent, int style) { + super(parent, style); + setLayout(new GridLayout()); + } + +} \ No newline at end of file diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtUtils.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtUtils.java new file mode 100644 index 000000000..5d964090b --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtUtils.java @@ -0,0 +1,315 @@ +package org.argeo.cms.swt; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; +import java.util.StringTokenizer; + +import org.argeo.api.cms.ux.CmsIcon; +import org.argeo.api.cms.ux.CmsStyle; +import org.argeo.api.cms.ux.CmsTheme; +import org.argeo.api.cms.ux.CmsView; +import org.argeo.eclipse.ui.specific.EclipseUiSpecificUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.FormAttachment; +import org.eclipse.swt.layout.FormData; +import org.eclipse.swt.layout.FormLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.layout.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.Layout; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.Widget; + +/** SWT utilities. */ +public class CmsSwtUtils { + /* + * THEME AND VIEW + */ + + public static CmsSwtTheme getCmsTheme(Composite parent) { + CmsSwtTheme theme = (CmsSwtTheme) parent.getData(CmsTheme.class.getName()); + if (theme == null) { + // find parent shell + Shell topShell = parent.getShell(); + while (topShell.getParent() != null) + topShell = (Shell) topShell.getParent(); + theme = (CmsSwtTheme) topShell.getData(CmsTheme.class.getName()); + parent.setData(CmsTheme.class.getName(), theme); + } + return theme; + } + + public static void registerCmsTheme(Shell shell, CmsTheme theme) { + // find parent shell + Shell topShell = shell; + while (topShell.getParent() != null) + topShell = (Shell) topShell.getParent(); + // check if already set + if (topShell.getData(CmsTheme.class.getName()) != null) { + CmsTheme registeredTheme = (CmsTheme) topShell.getData(CmsTheme.class.getName()); + throw new IllegalArgumentException( + "Theme " + registeredTheme.getThemeId() + " already registered in this shell"); + } + topShell.setData(CmsTheme.class.getName(), theme); + } + + public static CmsView getCmsView(Control parent) { + // find parent shell + Shell topShell = parent.getShell(); + while (topShell.getParent() != null) + topShell = (Shell) topShell.getParent(); + return (CmsView) topShell.getData(CmsView.class.getName()); + } + + public static void registerCmsView(Shell shell, CmsView view) { + // find parent shell + Shell topShell = shell; + while (topShell.getParent() != null) + topShell = (Shell) topShell.getParent(); + // check if already set + if (topShell.getData(CmsView.class.getName()) != null) { + CmsView registeredView = (CmsView) topShell.getData(CmsView.class.getName()); + throw new IllegalArgumentException("Cms view " + registeredView + " already registered in this shell"); + } + shell.setData(CmsView.class.getName(), view); + } + + /* + * EVENTS + */ + + /** Sends an event via {@link CmsView#sendEvent(String, Map)}. */ + public static void sendEventOnSelect(Control control, String topic, Map properties) { + SelectionListener listener = (Selected) (e) -> { + getCmsView(control.getParent()).sendEvent(topic, properties); + }; + if (control instanceof Button) { + ((Button) control).addSelectionListener(listener); + } else + throw new UnsupportedOperationException("Control type " + control.getClass() + " is not supported."); + } + + /** + * Convenience method to sends an event via + * {@link CmsView#sendEvent(String, Map)}. + */ + public static void sendEventOnSelect(Control control, String topic, String key, Object value) { + Map properties = new HashMap<>(); + properties.put(key, value); + sendEventOnSelect(control, topic, properties); + } + + /* + * ICONS + */ + /** Get a small icon from this theme. */ + public static Image getSmallIcon(CmsTheme theme, CmsIcon icon) { + return ((CmsSwtTheme) theme).getSmallIcon(icon); + } + + /** Get a big icon from this theme. */ + public static Image getBigIcon(CmsTheme theme, CmsIcon icon) { + return ((CmsSwtTheme) theme).getBigIcon(icon); + } + + /* + * LAYOUT INDEPENDENT + */ + /** Takes the most space possible, depending on parent layout. */ + public static void fill(Control control) { + Layout parentLayout = control.getParent().getLayout(); + if (parentLayout == null) + throw new IllegalStateException("Parent layout is not set"); + if (parentLayout instanceof GridLayout) { + control.setLayoutData(fillAll()); + } else if (parentLayout instanceof FormLayout) { + control.setLayoutData(coverAll()); + } else { + throw new IllegalArgumentException("Unsupported parent layout " + parentLayout.getClass().getName()); + } + } + + /* + * GRID LAYOUT + */ + /** A {@link GridLayout} without any spacing and one column. */ + public static GridLayout noSpaceGridLayout() { + return noSpaceGridLayout(new GridLayout()); + } + + /** + * A {@link GridLayout} without any spacing and multiple columns of unequal + * width. + */ + public static GridLayout noSpaceGridLayout(int columns) { + return noSpaceGridLayout(new GridLayout(columns, false)); + } + + /** @return the same layout, with spaces removed. */ + public static GridLayout noSpaceGridLayout(GridLayout layout) { + layout.horizontalSpacing = 0; + layout.verticalSpacing = 0; + layout.marginWidth = 0; + layout.marginHeight = 0; + return layout; + } + + public static GridData fillAll() { + return new GridData(SWT.FILL, SWT.FILL, true, true); + } + + public static GridData fillWidth() { + return grabWidth(SWT.FILL, SWT.FILL); + } + + public static GridData grabWidth(int horizontalAlignment, int verticalAlignment) { + return new GridData(horizontalAlignment, horizontalAlignment, true, false); + } + + public static GridData fillHeight() { + return grabHeight(SWT.FILL, SWT.FILL); + } + + public static GridData grabHeight(int horizontalAlignment, int verticalAlignment) { + return new GridData(horizontalAlignment, horizontalAlignment, false, true); + } + + /* + * ROW LAYOUT + */ + /** @return the same layout, with margins removed. */ + public static RowLayout noMarginsRowLayout(RowLayout rowLayout) { + rowLayout.marginTop = 0; + rowLayout.marginBottom = 0; + rowLayout.marginLeft = 0; + rowLayout.marginRight = 0; + return rowLayout; + } + + public static RowLayout noMarginsRowLayout(int type) { + return noMarginsRowLayout(new RowLayout(type)); + } + + public static RowData rowData16px() { + return new RowData(16, 16); + } + + /* + * FORM LAYOUT + */ + public static FormData coverAll() { + FormData fdLabel = new FormData(); + fdLabel.top = new FormAttachment(0, 0); + fdLabel.left = new FormAttachment(0, 0); + fdLabel.right = new FormAttachment(100, 0); + fdLabel.bottom = new FormAttachment(100, 0); + return fdLabel; + } + + /* + * STYLING + */ + + /** Style widget */ + public static T style(T widget, String style) { + if (style == null) + return widget;// does nothing + EclipseUiSpecificUtils.setStyleData(widget, style); + if (widget instanceof Control) { + CmsView cmsView = getCmsView((Control) widget); + if (cmsView != null) + cmsView.applyStyles(widget); + } + return widget; + } + + /** Style widget */ + public static T style(T widget, CmsStyle style) { + return style(widget, style.style()); + } + + /** Enable markups on widget */ + public static T markup(T widget) { + EclipseUiSpecificUtils.setMarkupData(widget); + return widget; + } + + /** Disable markup validation. */ + public static T disableMarkupValidation(T widget) { + EclipseUiSpecificUtils.setMarkupValidationDisabledData(widget); + return widget; + } + + /** + * Apply markup and set text on {@link Label}, {@link Button}, {@link Text}. + * + * @param widget the widget to style and to use in order to display text + * @param txt the object to display via its toString() method. + * This argument should not be null, but if it is null and + * assertions are disabled "" is displayed instead; if + * assertions are enabled the call will fail. + * + * @see markup + */ + public static T text(T widget, Object txt) { + assert txt != null; + String str = txt != null ? txt.toString() : ""; + markup(widget); + if (widget instanceof Label) + ((Label) widget).setText(str); + else if (widget instanceof Button) + ((Button) widget).setText(str); + else if (widget instanceof Text) + ((Text) widget).setText(str); + else + throw new IllegalArgumentException("Unsupported widget type " + widget.getClass()); + return widget; + } + + /** A {@link Label} with markup activated. */ + public static Label lbl(Composite parent, Object txt) { + return text(new Label(parent, SWT.NONE), txt); + } + + /** A read-only {@link Text} whose content can be copy/pasted. */ + public static Text txt(Composite parent, Object txt) { + return text(new Text(parent, SWT.NONE), txt); + } + + /** Dispose all children of a Composite */ + public static void clear(Composite composite) { + if (composite.isDisposed()) + return; + for (Control child : composite.getChildren()) + child.dispose(); + } + + /** Clean reserved URL characters for use in HTTP links. */ + public static String cleanPathForUrl(String path) { + StringTokenizer st = new StringTokenizer(path, "/"); + StringBuilder sb = new StringBuilder(); + while (st.hasMoreElements()) { + sb.append('/'); + String encoded = URLEncoder.encode(st.nextToken(), StandardCharsets.UTF_8); + encoded = encoded.replace("+", "%20"); + sb.append(encoded); + + } + return sb.toString(); + } + + /** Singleton. */ + private CmsSwtUtils() { + } + +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/MouseDoubleClick.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/MouseDoubleClick.java new file mode 100644 index 000000000..b818b06d9 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/MouseDoubleClick.java @@ -0,0 +1,26 @@ +package org.argeo.cms.swt; + +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; + +/** + * {@link MouseListener#mouseDoubleClick(MouseEvent)} as a functional interface + * in order to use as a short lambda expression in UI code. + * {@link MouseListener#mouseDownouseEvent)} and + * {@link MouseListener#mouseUp(MouseEvent)} do nothing by default. + */ +@FunctionalInterface +public interface MouseDoubleClick extends MouseListener { + @Override + void mouseDoubleClick(MouseEvent e); + + @Override + default void mouseDown(MouseEvent e) { + // does nothing + } + + @Override + default void mouseUp(MouseEvent e) { + // does nothing + } +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/MouseDown.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/MouseDown.java new file mode 100644 index 000000000..baecb0072 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/MouseDown.java @@ -0,0 +1,26 @@ +package org.argeo.cms.swt; + +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; + +/** + * {@link MouseListener#mouseDown(MouseEvent)} as a functional interface in + * order to use as a short lambda expression in UI code. + * {@link MouseListener#mouseDoubleClick(MouseEvent)} and + * {@link MouseListener#mouseUp(MouseEvent)} do nothing by default. + */ +@FunctionalInterface +public interface MouseDown extends MouseListener { + @Override + void mouseDown(MouseEvent e); + + @Override + default void mouseDoubleClick(MouseEvent e) { + // does nothing + } + + @Override + default void mouseUp(MouseEvent e) { + // does nothing + } +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/Selected.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/Selected.java new file mode 100644 index 000000000..03fbad01e --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/Selected.java @@ -0,0 +1,21 @@ +package org.argeo.cms.swt; + +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; + +/** + * {@link SelectionListener} as a functional interface in order to use as a + * short lambda expression in UI code. + * {@link SelectionListener#widgetDefaultSelected(SelectionEvent)} does nothing + * by default. + */ +@FunctionalInterface +public interface Selected extends SelectionListener { + @Override + public void widgetSelected(SelectionEvent e); + + default public void widgetDefaultSelected(SelectionEvent e) { + // does nothing + } + +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/SimpleSwtUxContext.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/SimpleSwtUxContext.java new file mode 100644 index 000000000..e468c6d52 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/SimpleSwtUxContext.java @@ -0,0 +1,50 @@ +package org.argeo.cms.swt; + +import org.argeo.api.cms.ux.UxContext; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Display; + +public class SimpleSwtUxContext implements UxContext { + private Point size; + private Point small = new Point(400, 400); + + public SimpleSwtUxContext() { + this(Display.getCurrent().getBounds()); + } + + public SimpleSwtUxContext(Rectangle rect) { + this.size = new Point(rect.width, rect.height); + } + + public SimpleSwtUxContext(Point size) { + this.size = size; + } + + @Override + public boolean isPortrait() { + return size.x >= size.y; + } + + @Override + public boolean isLandscape() { + return size.x < size.y; + } + + @Override + public boolean isSquare() { + return size.x == size.y; + } + + @Override + public boolean isSmall() { + return size.x <= small.x || size.y <= small.y; + } + + @Override + public boolean isMasterData() { + // TODO make it configurable + return true; + } + +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/SwtEditablePart.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/SwtEditablePart.java new file mode 100644 index 000000000..f2cceef07 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/SwtEditablePart.java @@ -0,0 +1,9 @@ +package org.argeo.cms.swt; + +import org.argeo.cms.ux.widgets.EditablePart; +import org.eclipse.swt.widgets.Control; + +/** Manages whether an editable or non editable control is shown. */ +public interface SwtEditablePart extends EditablePart { + public Control getControl(); +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/ContentComposite.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/ContentComposite.java new file mode 100644 index 000000000..951889eee --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/ContentComposite.java @@ -0,0 +1,41 @@ +package org.argeo.cms.swt.acr; + +import org.argeo.api.acr.Content; +import org.argeo.api.acr.spi.ProvidedContent; +import org.eclipse.swt.widgets.Composite; + +/** A composite which can (optionally) manage a content. */ +public class ContentComposite extends Composite { + private static final long serialVersionUID = -1447009015451153367L; + + public ContentComposite(Composite parent, int style, Content item) { + super(parent, style); + setData(item); + } + + public Content getContent() { + return (Content) getData(); + } + + @Deprecated + public Content getNode() { + return getContent(); + } + + protected ProvidedContent getProvidedContent() { + return (ProvidedContent) getContent(); + } + + public String getSessionLocalId() { + return getProvidedContent().getSessionLocalId(); + } + + protected void itemUpdated() { + layout(); + } + + public void setContent(Content content) { + setData(content); + itemUpdated(); + } +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtSection.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtSection.java new file mode 100644 index 000000000..89d003870 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtSection.java @@ -0,0 +1,158 @@ +package org.argeo.cms.swt.acr; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.argeo.api.acr.Content; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ux.widgets.EditablePart; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +/** A structured UI related to a JCR context. */ +public class SwtSection extends ContentComposite { + private static final long serialVersionUID = -5933796173755739207L; + + private final SwtSection parentSection; + private Composite sectionHeader; + private final Integer relativeDepth; + + public SwtSection(Composite parent, int style, Content node) { + this(parent, findSection(parent), style, node); + } + + public SwtSection(SwtSection section, int style, Content node) { + this(section, section, style, node); + } + + protected SwtSection(Composite parent, SwtSection parentSection, int style, Content node) { + super(parent, style, node); + this.parentSection = parentSection; + if (parentSection != null) { + relativeDepth = getProvidedContent().getDepth() - parentSection.getProvidedContent().getDepth(); + } else { + relativeDepth = 0; + } + setLayout(CmsSwtUtils.noSpaceGridLayout()); + } + + public Map getSubSections() { + LinkedHashMap result = new LinkedHashMap(); + for (Control child : getChildren()) { + if (child instanceof Composite) { + collectDirectSubSections((Composite) child, result); + } + } + return Collections.unmodifiableMap(result); + } + + private void collectDirectSubSections(Composite composite, LinkedHashMap subSections) { + if (composite == sectionHeader || composite instanceof EditablePart) + return; + if (composite instanceof SwtSection) { + SwtSection section = (SwtSection) composite; + subSections.put(section.getProvidedContent().getSessionLocalId(), section); + return; + } + + for (Control child : composite.getChildren()) + if (child instanceof Composite) + collectDirectSubSections((Composite) child, subSections); + } + + public Composite createHeader() { + return createHeader(this); + } + + public Composite createHeader(Composite parent) { + if (sectionHeader != null) + sectionHeader.dispose(); + + sectionHeader = new Composite(parent, SWT.NONE); + sectionHeader.setLayoutData(CmsSwtUtils.fillWidth()); + sectionHeader.setLayout(CmsSwtUtils.noSpaceGridLayout()); + // sectionHeader.moveAbove(null); + // layout(); + return sectionHeader; + } + + public Composite getHeader() { + if (sectionHeader != null && sectionHeader.isDisposed()) + sectionHeader = null; + return sectionHeader; + } + + // SECTION PARTS + public SwtSectionPart getSectionPart(String partId) { + for (Control child : getChildren()) { + if (child instanceof SwtSectionPart) { + SwtSectionPart sectionPart = (SwtSectionPart) child; + if (sectionPart.getPartId().equals(partId)) + return sectionPart; + } + } + return null; + } + + public SwtSectionPart nextSectionPart(SwtSectionPart sectionPart) { + Control[] children = getChildren(); + for (int i = 0; i < children.length; i++) { + if (sectionPart == children[i]) { + for (int j = i + 1; j < children.length; j++) { + if (children[i + 1] instanceof SwtSectionPart) { + return (SwtSectionPart) children[i + 1]; + } + } + +// if (i + 1 < children.length) { +// Composite next = (Composite) children[i + 1]; +// return (SectionPart) next; +// } else { +// // next section +// } + } + } + return null; + } + + public SwtSectionPart previousSectionPart(SwtSectionPart sectionPart) { + Control[] children = getChildren(); + for (int i = 0; i < children.length; i++) { + if (sectionPart == children[i]) + if (i != 0) { + Composite previous = (Composite) children[i - 1]; + return (SwtSectionPart) previous; + } else { + // previous section + } + } + return null; + } + + @Override + public String toString() { + if (parentSection == null) + return "Main section " + getContent(); + return "Section " + getContent(); + } + + public SwtSection getParentSection() { + return parentSection; + } + + public Integer getRelativeDepth() { + return relativeDepth; + } + + /** Recursively finds the related section in the parents (can be itself) */ + public static SwtSection findSection(Control control) { + if (control == null) + return null; + if (control instanceof SwtSection) + return (SwtSection) control; + else + return findSection(control.getParent()); + } +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtSectionPart.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtSectionPart.java new file mode 100644 index 000000000..7fbf4bbca --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtSectionPart.java @@ -0,0 +1,11 @@ +package org.argeo.cms.swt.acr; + +import org.argeo.cms.ux.acr.ContentPart; +import org.argeo.cms.ux.widgets.EditablePart; + +/** An editable part dynamically related to a Section */ +public interface SwtSectionPart extends EditablePart, ContentPart { + public String getPartId(); + + public SwtSection getSection(); +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtTabbedArea.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtTabbedArea.java new file mode 100644 index 000000000..cd4e37d19 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtTabbedArea.java @@ -0,0 +1,258 @@ +package org.argeo.cms.swt.acr; + +import java.util.ArrayList; +import java.util.List; + +import org.argeo.api.acr.Content; +import org.argeo.api.acr.spi.ProvidedContent; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.swt.Selected; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StackLayout; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.ToolBar; +import org.eclipse.swt.widgets.ToolItem; + +/** Manages {@link SwtSection} in a tab-like structure. */ +public class SwtTabbedArea extends Composite { + private static final long serialVersionUID = 8659669229482033444L; + + private Composite headers; + private Composite body; + + private List sections = new ArrayList<>(); + + private ProvidedContent previousNode; + private SwtUiProvider previousUiProvider; + private SwtUiProvider currentUiProvider; + + private String tabStyle; + private String tabSelectedStyle; + private String bodyStyle; + private Image closeIcon; + + private StackLayout stackLayout; + + private boolean singleTab = false; + + public SwtTabbedArea(Composite parent, int style) { + super(parent, SWT.NONE); + CmsSwtUtils.style(parent, bodyStyle); + + setLayout(CmsSwtUtils.noSpaceGridLayout()); + + // TODO manage tabs at bottom or sides + headers = new Composite(this, SWT.NONE); + headers.setLayoutData(CmsSwtUtils.fillWidth()); + body = new Composite(this, SWT.NONE); + body.setLayoutData(CmsSwtUtils.fillAll()); + // body.setLayout(new FormLayout()); + stackLayout = new StackLayout(); + body.setLayout(stackLayout); + emptyState(); + } + + protected void refreshTabHeaders() { + int tabCount = sections.size() > 0 ? sections.size() : 1; + for (Control tab : headers.getChildren()) + tab.dispose(); + + headers.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(tabCount, true))); + + if (sections.size() == 0) { + Composite emptyHeader = new Composite(headers, SWT.NONE); + emptyHeader.setLayoutData(CmsSwtUtils.fillAll()); + emptyHeader.setLayout(new GridLayout()); + Label lbl = new Label(emptyHeader, SWT.NONE); + lbl.setText(""); + lbl.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false)); + + } + + SwtSection currentSection = getCurrentSection(); + for (SwtSection section : sections) { + boolean selected = section == currentSection; + Composite sectionHeader = section.createHeader(headers); + CmsSwtUtils.style(sectionHeader, selected ? tabSelectedStyle : tabStyle); + int headerColumns = singleTab ? 1 : 2; + sectionHeader.setLayout(new GridLayout(headerColumns, false)); + sectionHeader.setLayout(CmsSwtUtils.noSpaceGridLayout(headerColumns)); + Button title = new Button(sectionHeader, SWT.FLAT); + CmsSwtUtils.style(title, selected ? tabSelectedStyle : tabStyle); + title.setLayoutData(CmsSwtUtils.fillWidth()); + title.addSelectionListener((Selected) (e) -> showTab(tabIndex(section.getNode()))); + Content node = section.getContent(); + + // FIXME find a standard way to display titles + String titleStr = node.getName().getLocalPart(); + + // TODO internationalize + title.setText(titleStr); + if (!singleTab) { + ToolBar toolBar = new ToolBar(sectionHeader, SWT.NONE); + ToolItem closeItem = new ToolItem(toolBar, SWT.FLAT); + if (closeIcon != null) + closeItem.setImage(closeIcon); + else + closeItem.setText("X"); + CmsSwtUtils.style(closeItem, selected ? tabSelectedStyle : tabStyle); + closeItem.addSelectionListener((Selected) (e) -> closeTab(section)); + } + } + + } + + public void view(SwtUiProvider uiProvider, Content context) { + if (body.isDisposed()) + return; + int index = tabIndex(context); + if (index >= 0) { + showTab(index); + previousNode = (ProvidedContent) context; + previousUiProvider = uiProvider; + return; + } + SwtSection section = (SwtSection) body.getChildren()[0]; + previousNode = (ProvidedContent) section.getNode(); + if (previousNode == null) {// empty state + previousNode = (ProvidedContent) context; + previousUiProvider = uiProvider; + } else { + previousUiProvider = currentUiProvider; + } + currentUiProvider = uiProvider; + section.setContent(context); + // section.setLayoutData(CmsUiUtils.coverAll()); + build(section, uiProvider, context); + if (sections.size() == 0) + sections.add(section); + refreshTabHeaders(); + index = tabIndex(context); + showTab(index); + layout(true, true); + } + + public void open(SwtUiProvider uiProvider, Content context) { + if (singleTab) + throw new UnsupportedOperationException("Open is not supported in single tab mode."); + + if (previousNode != null + && previousNode.getSessionLocalId().equals(((ProvidedContent) context).getSessionLocalId())) { + // does nothing + return; + } + if (sections.size() == 0) + CmsSwtUtils.clear(body); + SwtSection currentSection = getCurrentSection(); + int currentIndex = sections.indexOf(currentSection); + SwtSection previousSection = new SwtSection(body, SWT.NONE, context); + build(previousSection, previousUiProvider, previousNode); + // previousSection.setLayoutData(CmsUiUtils.coverAll()); + int newIndex = currentIndex + 1; + sections.add(currentIndex, previousSection); +// sections.add(newIndex, previousSection); + showTab(newIndex); + refreshTabHeaders(); + layout(true, true); + } + + public void showTab(int index) { + SwtSection sectionToShow = sections.get(index); + // sectionToShow.moveAbove(null); + stackLayout.topControl = sectionToShow; + refreshTabHeaders(); + layout(true, true); + } + + protected void build(SwtSection section, SwtUiProvider uiProvider, Content context) { + for (Control child : section.getChildren()) + child.dispose(); + CmsSwtUtils.style(section, bodyStyle); + section.setContent(context); + uiProvider.createUiPart(section, context); + + } + + private int tabIndex(Content context) { + for (int i = 0; i < sections.size(); i++) { + SwtSection section = sections.get(i); + if (section.getSessionLocalId().equals(((ProvidedContent) context).getSessionLocalId())) + return i; + } + return -1; + } + + public void closeTab(SwtSection section) { + int currentIndex = sections.indexOf(section); + int nextIndex = currentIndex == 0 ? 0 : currentIndex - 1; + sections.remove(section); + section.dispose(); + if (sections.size() == 0) { + emptyState(); + refreshTabHeaders(); + layout(true, true); + return; + } + refreshTabHeaders(); + showTab(nextIndex); + } + + public void closeAllTabs() { + for (SwtSection section : sections) { + section.dispose(); + } + sections.clear(); + emptyState(); + refreshTabHeaders(); + layout(true, true); + } + + protected void emptyState() { + new SwtSection(body, SWT.NONE, null); + refreshTabHeaders(); + } + + public Composite getCurrent() { + return getCurrentSection(); + } + + protected SwtSection getCurrentSection() { + return (SwtSection) stackLayout.topControl; + } + + public Content getCurrentContext() { + SwtSection section = getCurrentSection(); + if (section != null) { + return section.getNode(); + } else { + return null; + } + } + + public void setTabStyle(String tabStyle) { + this.tabStyle = tabStyle; + } + + public void setTabSelectedStyle(String tabSelectedStyle) { + this.tabSelectedStyle = tabSelectedStyle; + } + + public void setBodyStyle(String bodyStyle) { + this.bodyStyle = bodyStyle; + } + + public void setCloseIcon(Image closeIcon) { + this.closeIcon = closeIcon; + } + + public void setSingleTab(boolean singleTab) { + this.singleTab = singleTab; + } + +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtUiProvider.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtUiProvider.java new file mode 100644 index 000000000..4988fc6b8 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtUiProvider.java @@ -0,0 +1,10 @@ +package org.argeo.cms.swt.acr; + +import org.argeo.api.acr.Content; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +@FunctionalInterface +public interface SwtUiProvider { + Control createUiPart(Composite parent, Content context); +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/app/AcrContentTreeView.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/app/AcrContentTreeView.java new file mode 100644 index 000000000..fb1a79d44 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/app/AcrContentTreeView.java @@ -0,0 +1,158 @@ +package org.argeo.cms.swt.app; + +import static org.argeo.api.acr.NamespaceUtils.toPrefixedName; + +import java.util.ArrayList; +import java.util.List; + +import javax.xml.namespace.QName; + +import org.argeo.api.acr.Content; +import org.argeo.api.acr.NamespaceUtils; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.swt.widgets.SwtHierarchicalPart; +import org.argeo.cms.swt.widgets.SwtTabularPart; +import org.argeo.cms.ux.acr.ContentHierarchicalPart; +import org.argeo.cms.ux.widgets.Column; +import org.argeo.cms.ux.widgets.DefaultTabularPart; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.SashForm; +import org.eclipse.swt.widgets.Composite; + +public class AcrContentTreeView extends Composite { + private static final long serialVersionUID = -3707881216246077323L; + + private Content rootContent; + +// private Content selected; + + public AcrContentTreeView(Composite parent, int style, Content content) { + super(parent, style); + this.rootContent = content; + // this.selected = rootContent; + setLayout(CmsSwtUtils.noSpaceGridLayout()); + + SashForm split = new SashForm(this, SWT.HORIZONTAL); + split.setLayoutData(CmsSwtUtils.fillAll()); + + ContentHierarchicalPart contentPart = new ContentHierarchicalPart(); + contentPart.setInput(rootContent); + + SwtHierarchicalPart hPart = new SwtHierarchicalPart<>(split, getStyle(), contentPart); + + Composite area = new Composite(split, SWT.BORDER); + area.setLayout(CmsSwtUtils.noSpaceGridLayout(2)); + split.setWeights(new int[] { 30, 70 }); + + // attributes + DefaultTabularPart attributesPart = new DefaultTabularPart<>() { + + @Override + protected List asList(Content input) { + return new ArrayList<>(input.keySet()); + } + }; + + attributesPart.addColumn(new Column() { + + @Override + public String getText(QName model) { + try { + return NamespaceUtils.toPrefixedName(model); + } catch (IllegalStateException e) { + return model.toString(); + } + } + }); + attributesPart.addColumn(new Column() { + + @Override + public String getText(QName model) { + return attributesPart.getInput().get(model).toString(); + } + + @Override + public int getWidth() { + return 400; + } + + }); + // attributesPart.setInput(selected); + + SwtTabularPart attributeTable = new SwtTabularPart<>(area, style, attributesPart); + attributeTable.setLayoutData(CmsSwtUtils.fillAll()); + + // types + DefaultTabularPart typesPart = new DefaultTabularPart<>() { + + @Override + protected List asList(Content input) { + return input.getContentClasses(); + } + }; + typesPart.addColumn(new Column() { + + @Override + public String getText(QName model) { + return toPrefixedName(model); + } + + }); + + // typesPart.setInput(selected); + + SwtTabularPart typesTable = new SwtTabularPart<>(area, style, typesPart); + typesTable.setLayoutData(CmsSwtUtils.fillAll()); + + // controller + contentPart.setInput(rootContent); + contentPart.onSelected((o) -> { + Content c = (Content) o; +// selected = c; + attributesPart.setInput(c); + typesPart.setInput(c); + }); + + attributesPart.refresh(); + typesPart.refresh(); + } + +// protected void refreshTable() { +// for (TableItem item : table.getItems()) { +// item.dispose(); +// } +// for (QName key : selected.keySet()) { +// TableItem item = new TableItem(table, 0); +// item.setText(0, key.toString()); +// Object value = selected.get(key); +// item.setText(1, value.toString()); +// } +// table.getColumn(0).pack(); +// table.getColumn(1).pack(); +// } + +// public static void main(String[] args) { +// Path basePath; +// if (args.length > 0) { +// basePath = Paths.get(args[0]); +// } else { +// basePath = Paths.get(System.getProperty("user.home")); +// } +// +// final Display display = new Display(); +// final Shell shell = new Shell(display); +// shell.setText(basePath.toString()); +// shell.setLayout(new FillLayout()); +// +// FsContentProvider contentSession = new FsContentProvider("/", basePath); +//// GcrContentTreeView treeView = new GcrContentTreeView(shell, 0, contentSession.get("/")); +// +// shell.setSize(shell.computeSize(800, 600)); +// shell.open(); +// while (!shell.isDisposed()) { +// if (!display.readAndDispatch()) +// display.sleep(); +// } +// display.dispose(); +// } +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/app/CmsUserApp.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/app/CmsUserApp.java new file mode 100644 index 000000000..add6e9edb --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/app/CmsUserApp.java @@ -0,0 +1,62 @@ +package org.argeo.cms.swt.app; + +import java.util.HashSet; +import java.util.Set; + +import org.argeo.api.acr.Content; +import org.argeo.api.acr.ContentRepository; +import org.argeo.api.cms.CmsContext; +import org.argeo.api.cms.ux.CmsUi; +import org.argeo.api.cms.ux.CmsView; +import org.argeo.cms.AbstractCmsApp; +import org.argeo.cms.swt.CmsSwtUi; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.swt.auth.CmsLogin; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; + +public class CmsUserApp extends AbstractCmsApp { + private ContentRepository contentRepository; + + @Override + public Set getUiNames() { + Set uiNames = new HashSet<>(); + uiNames.add("login"); + uiNames.add("data"); + return uiNames; + } + + @Override + public CmsUi initUi(Object uiParent) { + Composite parent = (Composite) uiParent; + String uiName = parent.getData(UI_NAME_PROPERTY) != null ? parent.getData(UI_NAME_PROPERTY).toString() : null; + CmsSwtUi cmsUi = new CmsSwtUi(parent, SWT.NONE); + if ("login".equals(uiName)) { + CmsView cmsView = CmsSwtUtils.getCmsView(cmsUi); + CmsLogin cmsLogin = new CmsLogin(cmsView, getCmsContext()); + cmsLogin.createUi(cmsUi); + + } else if ("data".equals(uiName)) { + Content rootContent = contentRepository.get().get("/"); + AcrContentTreeView view = new AcrContentTreeView(cmsUi, 0, rootContent); + view.setLayoutData(CmsSwtUtils.fillAll()); + + } + return cmsUi; + } + + @Override + public void refreshUi(CmsUi cmsUi, String state) { + } + + @Override + public void setState(CmsUi cmsUi, String state) { + // TODO Auto-generated method stub + + } + + public void setContentRepository(ContentRepository contentRepository) { + this.contentRepository = contentRepository; + } + +} \ No newline at end of file diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CmsLogin.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CmsLogin.java new file mode 100644 index 000000000..6cc410ced --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CmsLogin.java @@ -0,0 +1,338 @@ +package org.argeo.cms.swt.auth; + +import static org.argeo.cms.CmsMsg.password; +import static org.argeo.cms.CmsMsg.username; + +import java.io.IOException; +import java.util.List; +import java.util.Locale; + +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.LanguageCallback; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; + +import org.argeo.api.cms.CmsAuth; +import org.argeo.api.cms.CmsContext; +import org.argeo.api.cms.CmsLog; +import org.argeo.api.cms.ux.CmsView; +import org.argeo.cms.CmsMsg; +import org.argeo.cms.LocaleUtils; +import org.argeo.cms.auth.RemoteAuthCallback; +import org.argeo.cms.servlet.ServletHttpRequest; +import org.argeo.cms.servlet.ServletHttpResponse; +import org.argeo.cms.swt.CmsStyles; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.eclipse.ui.specific.UiContext; +import org.eclipse.swt.SWT; +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.events.SelectionListener; +import org.eclipse.swt.events.TraverseEvent; +import org.eclipse.swt.events.TraverseListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.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.Shell; +import org.eclipse.swt.widgets.Text; + +public class CmsLogin implements CmsStyles, CallbackHandler { + private final static CmsLog log = CmsLog.getLog(CmsLogin.class); + + private Composite parent; + private Text usernameT, passwordT; + private Composite credentialsBlock; + private final SelectionListener loginSelectionListener; + + private final Locale defaultLocale; + private LocaleChoice localeChoice = null; + + private final CmsView cmsView; + + // optional subject to be set explicitly + private Subject subject = null; + + private CmsContext cmsContext; + + public CmsLogin(CmsView cmsView, CmsContext cmsContext) { + this.cmsView = cmsView; + this.cmsContext = cmsContext; + if (this.cmsContext != null) { + defaultLocale = this.cmsContext.getDefaultLocale(); + List locales = this.cmsContext.getLocales(); + if (locales != null) + localeChoice = new LocaleChoice(locales, defaultLocale); + } else { + defaultLocale = Locale.getDefault(); + } + loginSelectionListener = new SelectionListener() { + private static final long serialVersionUID = -8832133363830973578L; + + @Override + public void widgetSelected(SelectionEvent e) { + login(); + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + } + }; + } + + protected boolean isAnonymous() { + return cmsView.isAnonymous(); + } + + public final void createUi(Composite parent) { + this.parent = parent; + createContents(parent); + } + + protected void createContents(Composite parent) { + defaultCreateContents(parent); + } + + public final void defaultCreateContents(Composite parent) { + parent.setLayout(CmsSwtUtils.noSpaceGridLayout()); + Composite credentialsBlock = createCredentialsBlock(parent); + if (parent instanceof Shell) { + credentialsBlock.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true)); + } + } + + public final Composite createCredentialsBlock(Composite parent) { + if (isAnonymous()) { + return anonymousUi(parent); + } else { + return userUi(parent); + } + } + + public Composite getCredentialsBlock() { + return credentialsBlock; + } + + protected Composite userUi(Composite parent) { + Locale locale = localeChoice == null ? this.defaultLocale : localeChoice.getSelectedLocale(); + credentialsBlock = new Composite(parent, SWT.NONE); + credentialsBlock.setLayout(new GridLayout()); + // credentialsBlock.setLayoutData(CmsUiUtils.fillAll()); + + specificUserUi(credentialsBlock); + + Label l = new Label(credentialsBlock, SWT.NONE); + CmsSwtUtils.style(l, CMS_USER_MENU_ITEM); + l.setText(CmsMsg.logout.lead(locale)); + GridData lData = CmsSwtUtils.fillWidth(); + lData.widthHint = 120; + l.setLayoutData(lData); + + l.addMouseListener(new MouseAdapter() { + private static final long serialVersionUID = 6444395812777413116L; + + public void mouseDown(MouseEvent e) { + logout(); + } + }); + return credentialsBlock; + } + + /** To be overridden */ + protected void specificUserUi(Composite parent) { + + } + + protected Composite anonymousUi(Composite parent) { + Locale locale = localeChoice == null ? this.defaultLocale : localeChoice.getSelectedLocale(); + // We need a composite for the traversal + credentialsBlock = new Composite(parent, SWT.NONE); + credentialsBlock.setLayout(new GridLayout()); + // credentialsBlock.setLayoutData(CmsUiUtils.fillAll()); + CmsSwtUtils.style(credentialsBlock, CMS_LOGIN_DIALOG); + + Integer textWidth = 120; + if (parent instanceof Shell) + CmsSwtUtils.style(parent, CMS_USER_MENU); + // new Label(this, SWT.NONE).setText(CmsMsg.username.lead()); + usernameT = new Text(credentialsBlock, SWT.BORDER); + usernameT.setMessage(username.lead(locale)); + CmsSwtUtils.style(usernameT, CMS_LOGIN_DIALOG_USERNAME); + GridData gd = CmsSwtUtils.fillWidth(); + gd.widthHint = textWidth; + usernameT.setLayoutData(gd); + + // new Label(this, SWT.NONE).setText(CmsMsg.password.lead()); + passwordT = new Text(credentialsBlock, SWT.BORDER | SWT.PASSWORD); + passwordT.setMessage(password.lead(locale)); + CmsSwtUtils.style(passwordT, CMS_LOGIN_DIALOG_PASSWORD); + gd = CmsSwtUtils.fillWidth(); + gd.widthHint = textWidth; + passwordT.setLayoutData(gd); + + TraverseListener tl = new TraverseListener() { + private static final long serialVersionUID = -1158892811534971856L; + + public void keyTraversed(TraverseEvent e) { + if (e.detail == SWT.TRAVERSE_RETURN) + login(); + } + }; + credentialsBlock.addTraverseListener(tl); + usernameT.addTraverseListener(tl); + passwordT.addTraverseListener(tl); + parent.setTabList(new Control[] { credentialsBlock }); + credentialsBlock.setTabList(new Control[] { usernameT, passwordT }); + + // Button + Button loginButton = new Button(credentialsBlock, SWT.PUSH); + loginButton.setText(CmsMsg.login.lead(locale)); + loginButton.setLayoutData(CmsSwtUtils.fillWidth()); + loginButton.addSelectionListener(loginSelectionListener); + + extendsCredentialsBlock(credentialsBlock, locale, loginSelectionListener); + if (localeChoice != null) + createLocalesBlock(credentialsBlock); + return credentialsBlock; + } + + /** + * To be overridden in order to provide custom login button and other links. + */ + protected void extendsCredentialsBlock(Composite credentialsBlock, Locale selectedLocale, + SelectionListener loginSelectionListener) { + + } + + protected void updateLocale(Locale selectedLocale) { + // save already entered values + String usernameStr = usernameT.getText(); + char[] pwd = passwordT.getTextChars(); + + for (Control child : parent.getChildren()) + child.dispose(); + createContents(parent); + if (parent.getParent() != null) + parent.getParent().layout(true, true); + else + parent.layout(); + usernameT.setText(usernameStr); + passwordT.setTextChars(pwd); + } + + protected Composite createLocalesBlock(final Composite parent) { + Composite c = new Composite(parent, SWT.NONE); + CmsSwtUtils.style(c, CMS_USER_MENU_ITEM); + c.setLayout(CmsSwtUtils.noSpaceGridLayout()); + c.setLayoutData(CmsSwtUtils.fillAll()); + + SelectionListener selectionListener = new SelectionAdapter() { + private static final long serialVersionUID = 4891637813567806762L; + + public void widgetSelected(SelectionEvent event) { + Button button = (Button) event.widget; + if (button.getSelection()) { + localeChoice.setSelectedIndex((Integer) event.widget.getData()); + updateLocale(localeChoice.getSelectedLocale()); + } + }; + }; + + List locales = localeChoice.getLocales(); + for (Integer i = 0; i < locales.size(); i++) { + Locale locale = locales.get(i); + Button button = new Button(c, SWT.RADIO); + CmsSwtUtils.style(button, CMS_USER_MENU_ITEM); + button.setData(i); + button.setText(LocaleUtils.toLead(locale.getDisplayName(locale), locale) + " (" + locale + ")"); + // button.addListener(SWT.Selection, listener); + button.addSelectionListener(selectionListener); + if (i == localeChoice.getSelectedIndex()) + button.setSelection(true); + } + return c; + } + + protected boolean login() { + // TODO use CmsVie in order to retrieve subject? + // Subject subject = cmsView.getLoginContext().getSubject(); + // LoginContext loginContext = cmsView.getLoginContext(); + try { + // + // LOGIN + // + // loginContext.logout(); + LoginContext loginContext; + if (subject == null) + loginContext = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER, this); + else + loginContext = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER, subject, this); + loginContext.login(); + cmsView.authChange(loginContext); + return true; + } catch (LoginException e) { + if (log.isTraceEnabled()) + log.warn("Login failed: " + e.getMessage(), e); + else + log.warn("Login failed: " + e.getMessage()); + + try { + Thread.sleep(3000); + } catch (InterruptedException e2) { + // silent + } + // ErrorFeedback.show("Login failed", e); + return false; + } + // catch (LoginException e) { + // log.error("Cannot login", e); + // return false; + // } + } + + + protected void logout() { + cmsView.logout(); + cmsView.navigateTo("~"); + } + + @Override + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (Callback callback : callbacks) { + if (callback instanceof NameCallback && usernameT != null) + ((NameCallback) callback).setName(usernameT.getText()); + else if (callback instanceof PasswordCallback && passwordT != null) + ((PasswordCallback) callback).setPassword(passwordT.getTextChars()); + else if (callback instanceof RemoteAuthCallback) { + ((RemoteAuthCallback) callback).setRequest(new ServletHttpRequest(UiContext.getHttpRequest())); + ((RemoteAuthCallback) callback).setResponse(new ServletHttpResponse(UiContext.getHttpResponse())); + } else if (callback instanceof LanguageCallback) { + Locale toUse = null; + if (localeChoice != null) + toUse = localeChoice.getSelectedLocale(); + else if (defaultLocale != null) + toUse = defaultLocale; + + if (toUse != null) { + ((LanguageCallback) callback).setLocale(toUse); + UiContext.setLocale(toUse); + } + + } + } + } + + public void setSubject(Subject subject) { + this.subject = subject; + } + +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CmsLoginShell.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CmsLoginShell.java new file mode 100644 index 000000000..39cf82afc --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CmsLoginShell.java @@ -0,0 +1,73 @@ +package org.argeo.cms.swt.auth; + +import org.argeo.api.cms.CmsContext; +import org.argeo.api.cms.ux.CmsView; +import org.argeo.cms.swt.CmsSwtUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +/** The site-related user menu */ +public class CmsLoginShell extends CmsLogin { + private final Shell shell; + + public CmsLoginShell(CmsView cmsView, CmsContext cmsContext) { + super(cmsView, cmsContext); + shell = createShell(); +// createUi(shell); + } + + /** To be overridden. */ + protected Shell createShell() { + Shell shell = new Shell(Display.getCurrent(), SWT.NO_TRIM); + shell.setMaximized(true); + return shell; + } + + /** To be overridden. */ + public void open() { + CmsSwtUtils.style(shell, CMS_USER_MENU); + shell.open(); + } + + @Override + protected boolean login() { + boolean success = false; + try { + success = super.login(); + return success; + } finally { + if (success) + closeShell(); + else { + for (Control child : shell.getChildren()) + child.dispose(); + createUi(shell); + shell.layout(); + // TODO error message + } + } + } + + @Override + protected void logout() { + closeShell(); + super.logout(); + } + + protected void closeShell() { + if (!shell.isDisposed()) { + shell.close(); + shell.dispose(); + } + } + + public Shell getShell() { + return shell; + } + + public void createUi() { + createUi(shell); + } +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CompositeCallbackHandler.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CompositeCallbackHandler.java new file mode 100644 index 000000000..495007cb2 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CompositeCallbackHandler.java @@ -0,0 +1,273 @@ +package org.argeo.cms.swt.auth; + +import java.io.IOException; +import java.util.Arrays; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.TextOutputCallback; +import javax.security.auth.callback.UnsupportedCallbackException; + +import org.eclipse.swt.SWT; +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.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +/** + * A composite that can populate itself based on {@link Callback}s. It can be + * used directly as a {@link CallbackHandler} or be used by one by calling the + * {@link #createCallbackHandlers(Callback[])}. Supported standard + * {@link Callback}s are:
+ *
    + *
  • {@link PasswordCallback}
  • + *
  • {@link NameCallback}
  • + *
  • {@link TextOutputCallback}
  • + *
+ * Supported Argeo {@link Callback}s are:
+ *
    + *
  • {@link LocaleChoice}
  • + *
+ */ +public class CompositeCallbackHandler extends Composite implements CallbackHandler { + private static final long serialVersionUID = -928223893722723777L; + + private boolean wasUsedAlready = false; + private boolean isSubmitted = false; + private boolean isCanceled = false; + + public CompositeCallbackHandler(Composite parent, int style) { + super(parent, style); + } + + @Override + public synchronized void handle(final Callback[] callbacks) throws IOException, UnsupportedCallbackException { + // reset + if (wasUsedAlready && !isSubmitted() && !isCanceled()) { + cancel(); + for (Control control : getChildren()) + control.dispose(); + isSubmitted = false; + isCanceled = false; + } + + for (Callback callback : callbacks) + checkCallbackSupported(callback); + // create controls synchronously in the UI thread + getDisplay().syncExec(new Runnable() { + + @Override + public void run() { + createCallbackHandlers(callbacks); + } + }); + + if (!wasUsedAlready) + wasUsedAlready = true; + + // while (!isSubmitted() && !isCanceled()) { + // try { + // wait(1000l); + // } catch (InterruptedException e) { + // // silent + // } + // } + + // cleanCallbacksAfterCancel(callbacks); + } + + public void checkCallbackSupported(Callback callback) throws UnsupportedCallbackException { + if (callback instanceof TextOutputCallback || callback instanceof NameCallback + || callback instanceof PasswordCallback || callback instanceof LocaleChoice) { + return; + } else { + throw new UnsupportedCallbackException(callback); + } + } + + /** + * Set writable callbacks to null if the handle is canceled (check is done + * by the method) + */ + public void cleanCallbacksAfterCancel(Callback[] callbacks) { + if (isCanceled()) { + for (Callback callback : callbacks) { + if (callback instanceof NameCallback) { + ((NameCallback) callback).setName(null); + } else if (callback instanceof PasswordCallback) { + PasswordCallback pCallback = (PasswordCallback) callback; + char[] arr = pCallback.getPassword(); + if (arr != null) { + Arrays.fill(arr, '*'); + pCallback.setPassword(null); + } + } + } + } + } + + public void createCallbackHandlers(Callback[] callbacks) { + Composite composite = this; + for (int i = 0; i < callbacks.length; i++) { + Callback callback = callbacks[i]; + if (callback instanceof TextOutputCallback) { + createLabelTextoutputHandler(composite, (TextOutputCallback) callback); + } else if (callback instanceof NameCallback) { + createNameHandler(composite, (NameCallback) callback); + } else if (callback instanceof PasswordCallback) { + createPasswordHandler(composite, (PasswordCallback) callback); + } else if (callback instanceof LocaleChoice) { + createLocaleHandler(composite, (LocaleChoice) callback); + } + } + } + + protected Text createNameHandler(Composite composite, final NameCallback callback) { + Label label = new Label(composite, SWT.NONE); + label.setText(callback.getPrompt()); + final Text text = new Text(composite, SWT.SINGLE | SWT.LEAD | SWT.BORDER); + if (callback.getDefaultName() != null) { + // set default value, if provided + text.setText(callback.getDefaultName()); + callback.setName(callback.getDefaultName()); + } + text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + text.addModifyListener(new ModifyListener() { + private static final long serialVersionUID = 7300032545287292973L; + + public void modifyText(ModifyEvent event) { + callback.setName(text.getText()); + } + }); + text.addSelectionListener(new SelectionListener() { + private static final long serialVersionUID = 1820530045857665111L; + + @Override + public void widgetSelected(SelectionEvent e) { + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + submit(); + } + }); + + text.addKeyListener(new KeyListener() { + private static final long serialVersionUID = -8698107785092095713L; + + @Override + public void keyReleased(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + } + }); + return text; + } + + protected Text createPasswordHandler(Composite composite, final PasswordCallback callback) { + Label label = new Label(composite, SWT.NONE); + label.setText(callback.getPrompt()); + final Text passwordText = new Text(composite, SWT.SINGLE | SWT.LEAD | SWT.PASSWORD | SWT.BORDER); + passwordText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + passwordText.addModifyListener(new ModifyListener() { + private static final long serialVersionUID = -7099363995047686732L; + + public void modifyText(ModifyEvent event) { + callback.setPassword(passwordText.getTextChars()); + } + }); + passwordText.addSelectionListener(new SelectionListener() { + private static final long serialVersionUID = 1820530045857665111L; + + @Override + public void widgetSelected(SelectionEvent e) { + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + submit(); + } + }); + return passwordText; + } + + protected Combo createLocaleHandler(Composite composite, final LocaleChoice callback) { + String[] labels = callback.getSupportedLocalesLabels(); + if (labels.length == 0) + return null; + Label label = new Label(composite, SWT.NONE); + label.setText("Language"); + + final Combo combo = new Combo(composite, SWT.READ_ONLY); + combo.setItems(labels); + combo.select(callback.getDefaultIndex()); + combo.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + combo.addSelectionListener(new SelectionListener() { + private static final long serialVersionUID = 38678989091946277L; + + @Override + public void widgetSelected(SelectionEvent e) { + callback.setSelectedIndex(combo.getSelectionIndex()); + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + } + }); + return combo; + } + + protected Label createLabelTextoutputHandler(Composite composite, final TextOutputCallback callback) { + Label label = new Label(composite, SWT.NONE); + label.setText(callback.getMessage()); + GridData data = new GridData(SWT.FILL, SWT.FILL, true, true); + data.horizontalSpan = 2; + label.setLayoutData(data); + return label; + // TODO: find a way to pass this information + // int messageType = callback.getMessageType(); + // int dialogMessageType = IMessageProvider.NONE; + // switch (messageType) { + // case TextOutputCallback.INFORMATION: + // dialogMessageType = IMessageProvider.INFORMATION; + // break; + // case TextOutputCallback.WARNING: + // dialogMessageType = IMessageProvider.WARNING; + // break; + // case TextOutputCallback.ERROR: + // dialogMessageType = IMessageProvider.ERROR; + // break; + // } + // setMessage(callback.getMessage(), dialogMessageType); + } + + synchronized boolean isSubmitted() { + return isSubmitted; + } + + synchronized boolean isCanceled() { + return isCanceled; + } + + protected synchronized void submit() { + isSubmitted = true; + notifyAll(); + } + + protected synchronized void cancel() { + isCanceled = true; + notifyAll(); + } +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/DynamicCallbackHandler.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/DynamicCallbackHandler.java new file mode 100644 index 000000000..b0c36c602 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/DynamicCallbackHandler.java @@ -0,0 +1,34 @@ +package org.argeo.cms.swt.auth; + +import java.io.IOException; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; + +import org.argeo.eclipse.ui.dialogs.LightweightDialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +public class DynamicCallbackHandler implements CallbackHandler { + + @Override + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + Shell activeShell = Display.getCurrent().getActiveShell(); + LightweightDialog dialog = new LightweightDialog(activeShell) { + + @Override + protected Control createDialogArea(Composite parent) { + CompositeCallbackHandler cch = new CompositeCallbackHandler(parent, SWT.NONE); + cch.createCallbackHandlers(callbacks); + return cch; + } + }; + dialog.setBlockOnOpen(true); + dialog.open(); + } + +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/LocaleChoice.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/LocaleChoice.java new file mode 100644 index 000000000..3ce5ae516 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/LocaleChoice.java @@ -0,0 +1,86 @@ +package org.argeo.cms.swt.auth; + +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +import javax.security.auth.callback.LanguageCallback; + +import org.argeo.cms.swt.CmsException; + +/** Choose in a list of locales. TODO: replace with {@link LanguageCallback} */ +@Deprecated +public class LocaleChoice { + private final List locales; + + private Integer selectedIndex = null; + private final Integer defaultIndex; + + public LocaleChoice(List locales, Locale defaultLocale) { + Integer defaultIndex = null; + this.locales = Collections.unmodifiableList(locales); + for (int i = 0; i < locales.size(); i++) + if (locales.get(i).equals(defaultLocale)) + defaultIndex = i; + + // based on language only + if (defaultIndex == null) + for (int i = 0; i < locales.size(); i++) + if (locales.get(i).getLanguage().equals(defaultLocale.getLanguage())) + defaultIndex = i; + + if (defaultIndex == null) + throw new CmsException("Default locale " + defaultLocale + " is not in available locales " + locales); + this.defaultIndex = defaultIndex; + + this.selectedIndex = defaultIndex; + } + +// /** +// * Convenience constructor based on a comma separated list of iso codes (en, +// * en_US, fr_CA, etc.). Default selection is default locale. +// */ +// public LocaleChoice(String locales, Locale defaultLocale) { +// this(LocaleUtils.asLocaleList(locales), defaultLocale); +// } + + public String[] getSupportedLocalesLabels() { + String[] labels = new String[locales.size()]; + for (int i = 0; i < locales.size(); i++) { + Locale locale = locales.get(i); + if (locale.getCountry().equals("")) + labels[i] = locale.getDisplayLanguage(locale) + " [" + locale.getLanguage() + "]"; + else + labels[i] = locale.getDisplayLanguage(locale) + " (" + locale.getDisplayCountry(locale) + ") [" + + locale.getLanguage() + "_" + locale.getCountry() + "]"; + + } + return labels; + } + + public Locale getSelectedLocale() { + if (selectedIndex == null) + return null; + return locales.get(selectedIndex); + } + + public void setSelectedIndex(Integer selectedIndex) { + this.selectedIndex = selectedIndex; + } + + public Integer getSelectedIndex() { + return selectedIndex; + } + + public Integer getDefaultIndex() { + return defaultIndex; + } + + public List getLocales() { + return locales; + } + + public Locale getDefaultLocale() { + return locales.get(getDefaultIndex()); + } +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/package-info.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/package-info.java new file mode 100644 index 000000000..b431423d8 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/package-info.java @@ -0,0 +1,2 @@ +/** Argeo CMS authentication widgets, based on SWT. */ +package org.argeo.cms.swt.auth; \ No newline at end of file diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/ChangePasswordDialog.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/ChangePasswordDialog.java new file mode 100644 index 000000000..06e4d0f9f --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/ChangePasswordDialog.java @@ -0,0 +1,83 @@ +package org.argeo.cms.swt.dialogs; + +import java.security.PrivilegedAction; +import java.util.Arrays; + +import org.argeo.api.cms.CmsLog; +import org.argeo.api.cms.ux.CmsView; +import org.argeo.cms.CmsMsg; +import org.argeo.cms.CmsUserManager; +import org.argeo.cms.swt.CmsSwtUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +/** Dialog to change a password. */ +public class ChangePasswordDialog extends CmsMessageDialog { + private final static CmsLog log = CmsLog.getLog(ChangePasswordDialog.class); + + private CmsUserManager cmsUserManager; + private CmsView cmsView; + + private PrivilegedAction doIt; + + public ChangePasswordDialog(Shell parentShell, String message, int kind, CmsUserManager cmsUserManager) { + super(parentShell, message, kind); + this.cmsUserManager = cmsUserManager; + cmsView = CmsSwtUtils.getCmsView(parentShell); + } + + @Override + protected Control createInputArea(Composite userSection) { + addFormLabel(userSection, CmsMsg.currentPassword.lead()); + Text previousPassword = new Text(userSection, SWT.BORDER | SWT.PASSWORD); + previousPassword.setLayoutData(CmsSwtUtils.fillWidth()); + addFormLabel(userSection, CmsMsg.newPassword.lead()); + Text newPassword = new Text(userSection, SWT.BORDER | SWT.PASSWORD); + newPassword.setLayoutData(CmsSwtUtils.fillWidth()); + addFormLabel(userSection, CmsMsg.repeatNewPassword.lead()); + Text confirmPassword = new Text(userSection, SWT.BORDER | SWT.PASSWORD); + confirmPassword.setLayoutData(CmsSwtUtils.fillWidth()); + + doIt = () -> { + if (Arrays.equals(newPassword.getTextChars(), confirmPassword.getTextChars())) { + try { + cmsUserManager.changeOwnPassword(previousPassword.getTextChars(), newPassword.getTextChars()); + return OK; + } catch (Exception e1) { + log.error("Could not change password", e1); + cancel(); + CmsMessageDialog.openError(CmsMsg.invalidPassword.lead()); + return CANCEL; + } + } else { + cancel(); + CmsMessageDialog.openError(CmsMsg.repeatNewPassword.lead()); + return CANCEL; + } + }; + + pack(); + return previousPassword; + } + + @Override + protected void okPressed() { + Integer returnCode = cmsView.doAs(doIt); + if (returnCode.equals(OK)) { + super.okPressed(); + CmsMessageDialog.openInformation(CmsMsg.passwordChanged.lead()); + } + } + + private static Label addFormLabel(Composite parent, String label) { + Label lbl = new Label(parent, SWT.WRAP); + lbl.setText(label); +// CmsUiUtils.style(lbl, SuiteStyle.simpleLabel); + return lbl; + } + +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/CmsFeedback.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/CmsFeedback.java new file mode 100644 index 000000000..a01c919e9 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/CmsFeedback.java @@ -0,0 +1,100 @@ +package org.argeo.cms.swt.dialogs; + +import java.io.PrintWriter; +import java.io.StringWriter; + +import org.argeo.api.cms.CmsLog; +import org.argeo.cms.CmsMsg; +import org.argeo.cms.swt.Selected; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +/** A dialog feedback based on a {@link LightweightDialog}. */ +public class CmsFeedback extends LightweightDialog { + private final static CmsLog log = CmsLog.getLog(CmsFeedback.class); + + private String message; + private Throwable exception; + + public CmsFeedback(Shell parentShell, String message, Throwable e) { + super(parentShell); + this.message = message; + this.exception = e; + log.error(message, e); + } + + public static CmsFeedback show(String message, Throwable e) { + // rethrow ThreaDeath in order to make sure that RAP will properly clean + // up the UI thread + if (e instanceof ThreadDeath) + throw (ThreadDeath) e; + + try { + CmsFeedback cmsFeedback = new CmsFeedback(null, message, e); + cmsFeedback.setBlockOnOpen(false); + cmsFeedback.open(); + return cmsFeedback; + } catch (Throwable e1) { + log.error("Cannot open error feedback (" + e.getMessage() + "), original error below", e); + return null; + } + } + + public static CmsFeedback show(String message) { + CmsFeedback cmsFeedback = new CmsFeedback(null, message, null); + cmsFeedback.open(); + return cmsFeedback; + } + + /** Tries to find a display */ + // private static Display getDisplay() { + // try { + // Display display = Display.getCurrent(); + // if (display != null) + // return display; + // else + // return Display.getDefault(); + // } catch (Exception e) { + // return Display.getCurrent(); + // } + // } + + protected Control createDialogArea(Composite parent) { + parent.setLayout(new GridLayout(2, false)); + + Label messageLbl = new Label(parent, SWT.WRAP); + if (message != null) + messageLbl.setText(message); + else if (exception != null) + messageLbl.setText(exception.getLocalizedMessage()); + + Button close = new Button(parent, SWT.FLAT); + close.setText(CmsMsg.close.lead()); + close.setLayoutData(new GridData(SWT.END, SWT.TOP, false, false)); + close.addSelectionListener((Selected) (e) -> closeShell(OK)); + + // Composite composite = new Composite(dialogarea, SWT.NONE); + // composite.setLayout(new GridLayout(2, false)); + // composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + if (exception != null) { + Text stack = new Text(parent, SWT.MULTI | SWT.LEAD | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL); + stack.setEditable(false); + stack.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1)); + StringWriter sw = new StringWriter(); + exception.printStackTrace(new PrintWriter(sw)); + stack.setText(sw.toString()); + } + + // parent.pack(); + return messageLbl; + } + +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/CmsMessageDialog.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/CmsMessageDialog.java new file mode 100644 index 000000000..66e640595 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/CmsMessageDialog.java @@ -0,0 +1,167 @@ +package org.argeo.cms.swt.dialogs; + +import org.argeo.cms.CmsMsg; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.swt.Selected; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.TraverseEvent; +import org.eclipse.swt.events.TraverseListener; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; + +/** Base class for dialogs displaying messages or small forms. */ +public class CmsMessageDialog extends LightweightDialog { + public final static int NONE = 0; + public final static int ERROR = 1; + public final static int INFORMATION = 2; + public final static int QUESTION = 3; + public final static int WARNING = 4; + public final static int CONFIRM = 5; + public final static int QUESTION_WITH_CANCEL = 6; + + private int kind; + private String message; + + public CmsMessageDialog(Shell parentShell, String message, int kind) { + super(parentShell); + this.kind = kind; + this.message = message; + } + + protected Control createDialogArea(Composite parent) { + parent.setLayout(new GridLayout()); + + TraverseListener traverseListener = new TraverseListener() { + private static final long serialVersionUID = -1158892811534971856L; + + public void keyTraversed(TraverseEvent e) { + if (e.detail == SWT.TRAVERSE_RETURN) + okPressed(); + else if (e.detail == SWT.TRAVERSE_ESCAPE) + cancelPressed(); + } + }; + + // message + Composite body = new Composite(parent, SWT.NONE); + body.addTraverseListener(traverseListener); + GridLayout bodyGridLayout = new GridLayout(); + bodyGridLayout.marginHeight = 20; + bodyGridLayout.marginWidth = 20; + body.setLayout(bodyGridLayout); + body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + if (message != null) { + Label messageLbl = new Label(body, SWT.WRAP); + CmsSwtUtils.markup(messageLbl); + messageLbl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + messageLbl.setFont(EclipseUiUtils.getBoldFont(parent)); + messageLbl.setText(message); + } + + // buttons + Composite buttons = new Composite(parent, SWT.NONE); + buttons.addTraverseListener(traverseListener); + buttons.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false)); + if (kind == INFORMATION || kind == WARNING || kind == ERROR || kind == ERROR) { + GridLayout layout = new GridLayout(1, true); + layout.marginWidth = 0; + layout.marginHeight = 0; + buttons.setLayout(layout); + + Button close = new Button(buttons, SWT.FLAT); + close.setText(CmsMsg.close.lead()); + close.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); + close.addSelectionListener((Selected) (e) -> closeShell(OK)); + close.setFocus(); + close.addTraverseListener(traverseListener); + + buttons.setTabList(new Control[] { close }); + } else if (kind == CONFIRM || kind == QUESTION || kind == QUESTION_WITH_CANCEL) { + Control input = createInputArea(body); + if (input != null) { + input.addTraverseListener(traverseListener); + body.setTabList(new Control[] { input }); + } + GridLayout layout = new GridLayout(2, true); + layout.marginWidth = 0; + layout.marginHeight = 0; + buttons.setLayout(layout); + + Button cancel = new Button(buttons, SWT.FLAT); + cancel.setText(CmsMsg.cancel.lead()); + cancel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); + cancel.addSelectionListener((Selected) (e) -> cancelPressed()); + cancel.addTraverseListener(traverseListener); + + Button ok = new Button(buttons, SWT.FLAT); + ok.setText(CmsMsg.ok.lead()); + ok.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); + ok.addSelectionListener((Selected) (e) -> okPressed()); + ok.addTraverseListener(traverseListener); + if (input == null) + ok.setFocus(); + else + input.setFocus(); + + buttons.setTabList(new Control[] { ok, cancel }); + } + // pack(); + parent.setTabList(new Control[] { body, buttons }); + return body; + } + + protected Control createInputArea(Composite parent) { + return null; + } + + protected void okPressed() { + closeShell(OK); + } + + protected void cancelPressed() { + closeShell(CANCEL); + } + + protected void cancel() { + closeShell(CANCEL); + } + + protected Point getInitialSize() { + return new Point(400, 200); + } + + public static boolean open(int kind, Shell parent, String message) { + CmsMessageDialog dialog = new CmsMessageDialog(parent, message, kind); + return dialog.open() == 0; + } + + public static boolean openConfirm(String message) { + return open(CONFIRM, Display.getCurrent().getActiveShell(), message); + } + + public static void openInformation(String message) { + open(INFORMATION, Display.getCurrent().getActiveShell(), message); + } + + public static boolean openQuestion(String message) { + return open(QUESTION, Display.getCurrent().getActiveShell(), message); + } + + public static void openWarning(String message) { + open(WARNING, Display.getCurrent().getActiveShell(), message); + } + + public static void openError(String message) { + open(ERROR, Display.getCurrent().getActiveShell(), message); + } + +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/LightweightDialog.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/LightweightDialog.java new file mode 100644 index 000000000..bf6417bea --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/LightweightDialog.java @@ -0,0 +1,255 @@ +package org.argeo.cms.swt.dialogs; + +import org.argeo.api.cms.CmsLog; +import org.argeo.eclipse.ui.EclipseUiException; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.ShellAdapter; +import org.eclipse.swt.events.ShellEvent; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +/** Generic lightweight dialog, not based on JFace. */ +public class LightweightDialog { + private final static CmsLog log = CmsLog.getLog(LightweightDialog.class); + + // must be the same value as org.eclipse.jface.window.Window#OK + public final static int OK = 0; + // must be the same value as org.eclipse.jface.window.Window#CANCEL + public final static int CANCEL = 1; + + private Shell parentShell; + private Shell backgroundShell; + private Shell foregoundShell; + + private Integer returnCode = null; + private boolean block = true; + + private String title; + + /** Tries to find a display */ + private static Display getDisplay() { + try { + Display display = Display.getCurrent(); + if (display != null) + return display; + else + return Display.getDefault(); + } catch (Exception e) { + return Display.getCurrent(); + } + } + + public LightweightDialog(Shell parentShell) { + this.parentShell = parentShell; + } + + public int open() { + if (foregoundShell != null) + throw new EclipseUiException("There is already a shell"); + backgroundShell = new Shell(parentShell, SWT.ON_TOP); + backgroundShell.setFullScreen(true); + // if (parentShell != null) { + // backgroundShell.setBounds(parentShell.getBounds()); + // } else + // backgroundShell.setMaximized(true); + backgroundShell.setAlpha(128); + backgroundShell.setBackground(getDisplay().getSystemColor(SWT.COLOR_BLACK)); + foregoundShell = new Shell(backgroundShell, SWT.NO_TRIM | SWT.ON_TOP); + if (title != null) + setTitle(title); + foregoundShell.setLayout(new GridLayout()); + foregoundShell.setSize(getInitialSize()); + createDialogArea(foregoundShell); + // shell.pack(); + // shell.layout(); + + Rectangle shellBounds = parentShell != null ? parentShell.getBounds() : Display.getCurrent().getBounds();// RAP + Point dialogSize = foregoundShell.getSize(); + int x = shellBounds.x + (shellBounds.width - dialogSize.x) / 2; + int y = shellBounds.y + (shellBounds.height - dialogSize.y) / 2; + foregoundShell.setLocation(x, y); + + foregoundShell.addShellListener(new ShellAdapter() { + private static final long serialVersionUID = -2701270481953688763L; + + @Override + public void shellDeactivated(ShellEvent e) { + if (hasChildShells()) + return; + if (returnCode == null)// not yet closed + closeShell(CANCEL); + } + + @Override + public void shellClosed(ShellEvent e) { + notifyClose(); + } + + }); + + backgroundShell.open(); + foregoundShell.open(); + // after the foreground shell has been opened + backgroundShell.addFocusListener(new FocusListener() { + private static final long serialVersionUID = 3137408447474661070L; + + @Override + public void focusLost(FocusEvent event) { + } + + @Override + public void focusGained(FocusEvent event) { + if (hasChildShells()) + return; + if (returnCode == null)// not yet closed + closeShell(CANCEL); + } + }); + + if (block) { + block(); + } + if (returnCode == null) + returnCode = OK; + return returnCode; + } + + public void block() { + try { + runEventLoop(foregoundShell); + } catch (ThreadDeath t) { + returnCode = CANCEL; + if (log.isTraceEnabled()) + log.error("Thread death, canceling dialog", t); + } catch (Throwable t) { + returnCode = CANCEL; + log.error("Cannot open blocking lightweight dialog", t); + } + } + + private boolean hasChildShells() { + if (foregoundShell == null) + return false; + return foregoundShell.getShells().length != 0; + } + + // public synchronized int openAndWait() { + // open(); + // while (returnCode == null) + // try { + // wait(100); + // } catch (InterruptedException e) { + // // silent + // } + // return returnCode; + // } + + private synchronized void notifyClose() { + if (returnCode == null) + returnCode = CANCEL; + notifyAll(); + } + + protected void closeShell(int returnCode) { + this.returnCode = returnCode; + if (CANCEL == returnCode) + onCancel(); + if (foregoundShell != null && !foregoundShell.isDisposed()) { + foregoundShell.close(); + foregoundShell.dispose(); + foregoundShell = null; + } + + if (backgroundShell != null && !backgroundShell.isDisposed()) { + backgroundShell.close(); + backgroundShell.dispose(); + } + } + + protected Point getInitialSize() { + // if (exception != null) + // return new Point(800, 600); + // else + return new Point(600, 400); + } + + protected Control createDialogArea(Composite parent) { + Composite dialogarea = new Composite(parent, SWT.NONE); + dialogarea.setLayout(new GridLayout()); + dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + return dialogarea; + } + + protected Shell getBackgroundShell() { + return backgroundShell; + } + + protected Shell getForegoundShell() { + return foregoundShell; + } + + public void setBlockOnOpen(boolean shouldBlock) { + block = shouldBlock; + } + + public void pack() { + foregoundShell.pack(); + } + + private void runEventLoop(Shell loopShell) { + Display display; + if (foregoundShell == null) { + display = Display.getCurrent(); + } else { + display = loopShell.getDisplay(); + } + + while (loopShell != null && !loopShell.isDisposed()) { + try { + if (!display.readAndDispatch()) { + display.sleep(); + } + } catch (UnsupportedOperationException e) { + throw e; + } catch (Throwable e) { + handleException(e); + } + } + if (!display.isDisposed()) + display.update(); + } + + protected void handleException(Throwable t) { + if (t instanceof ThreadDeath) { + // Don't catch ThreadDeath as this is a normal occurrence when + // the thread dies + throw (ThreadDeath) t; + } + // Try to keep running. + t.printStackTrace(); + } + + /** @return false, if the dialog should not be closed. */ + protected boolean onCancel() { + return true; + } + + public void setTitle(String title) { + this.title = title; + if (title != null && getForegoundShell() != null) + getForegoundShell().setText(title); + } + + public Integer getReturnCode() { + return returnCode; + } + +} \ No newline at end of file diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/SingleValueDialog.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/SingleValueDialog.java new file mode 100644 index 000000000..9404b81da --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/SingleValueDialog.java @@ -0,0 +1,82 @@ +package org.argeo.cms.swt.dialogs; + +import org.eclipse.jface.window.Window; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +/** A dialog asking a for a single value. */ +public class SingleValueDialog extends CmsMessageDialog { + private Text valueT; + private String value; + private String defaultValue; + + public SingleValueDialog(Shell parentShell, String message) { + super(parentShell, message, QUESTION); + } + + public SingleValueDialog(Shell parentShell, String message, String defaultValue) { + super(parentShell, message, QUESTION); + this.defaultValue = defaultValue; + } + + @Override + protected Control createInputArea(Composite parent) { + valueT = new Text(parent, SWT.LEAD | SWT.BORDER | SWT.SINGLE); + valueT.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true)); + if (defaultValue != null) + valueT.setText(defaultValue); + return valueT; + } + + @Override + protected void okPressed() { + value = valueT.getText(); + super.okPressed(); + } + + public String getString() { + return value; + } + + public Long getLong() { + return Long.valueOf(getString()); + } + + public Double getDouble() { + return Double.valueOf(getString()); + } + + public static String ask(String message) { + return ask(message, null); + } + + public static String ask(String message, String defaultValue) { + SingleValueDialog svd = new SingleValueDialog(Display.getCurrent().getActiveShell(), message, defaultValue); + if (svd.open() == Window.OK) + return svd.getString(); + else + return null; + } + + public static Long askLong(String message) { + SingleValueDialog svd = new SingleValueDialog(Display.getCurrent().getActiveShell(), message); + if (svd.open() == Window.OK) + return svd.getLong(); + else + return null; + } + + public static Double askDouble(String message) { + SingleValueDialog svd = new SingleValueDialog(Display.getCurrent().getActiveShell(), message); + if (svd.open() == Window.OK) + return svd.getDouble(); + else + return null; + } + +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/package-info.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/package-info.java new file mode 100644 index 000000000..ac76dba81 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/package-info.java @@ -0,0 +1,2 @@ +/** SWT/JFace dialogs. */ +package org.argeo.cms.swt.dialogs; \ No newline at end of file diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/osgi/BundleCmsSwtTheme.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/osgi/BundleCmsSwtTheme.java new file mode 100644 index 000000000..b3fec78ec --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/osgi/BundleCmsSwtTheme.java @@ -0,0 +1,111 @@ +package org.argeo.cms.swt.osgi; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +import org.argeo.api.cms.ux.CmsIcon; +import org.argeo.cms.osgi.BundleCmsTheme; +import org.argeo.cms.swt.CmsSwtTheme; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.widgets.Display; + +/** Centralises some generic {@link CmsSwtTheme} patterns. */ +public class BundleCmsSwtTheme extends BundleCmsTheme implements CmsSwtTheme { + private Map imageCache = new HashMap<>(); + + private Map> iconPaths = new HashMap<>(); + + protected Image getImage(String path) { + if (!imageCache.containsKey(path)) { + try (InputStream in = getResourceAsStream(path)) { + if (in == null) + return null; + ImageData imageData = new ImageData(in); + imageCache.put(path, imageData); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + ImageData imageData = imageCache.get(path); + Image image = new Image(Display.getCurrent(), imageData); + return image; + } + + /** + * And icon with this file name (without the extension), with a best effort to + * find the appropriate size, or null if not found. + * + * @param name An icon file name without path and extension. + * @param preferredSize the preferred size, if null, + * {@link #getSmallIconSize()} will be tried. + */ + public Image getIcon(String name, Integer preferredSize) { + if (preferredSize == null) + preferredSize = getSmallIconSize(); + Map subCache; + if (!iconPaths.containsKey(name)) + subCache = new HashMap<>(); + else + subCache = iconPaths.get(name); + Image image = null; + if (!subCache.containsKey(preferredSize)) { + Image bestMatchSoFar = null; + paths: for (String p : getImagesPaths()) { + int lastSlash = p.lastIndexOf('/'); + String fileName = p; + String ext = ""; + if (lastSlash >= 0) + fileName = p.substring(lastSlash + 1); + int lastDot = fileName.lastIndexOf('.'); + if (lastDot >= 0) { + ext = fileName.substring(lastDot + 1); + fileName = fileName.substring(0, lastDot); + } + + if ("svg".equals(ext)) + continue paths; + + if (fileName.equals(name)) {// matched + Image img = getImage(p); + int width = img.getBounds().width; + if (width == preferredSize) {// perfect match + subCache.put(preferredSize, p); + image = img; + break paths; + } + if (bestMatchSoFar == null) { + bestMatchSoFar = img; + } else { + if (Math.abs(width - preferredSize) < Math + .abs(bestMatchSoFar.getBounds().width - preferredSize)) + bestMatchSoFar = img; + } + } + } + + if (image == null) + image = bestMatchSoFar; + } else { + image = getImage(subCache.get(preferredSize)); + } + + if (image != null && !iconPaths.containsKey(name)) + iconPaths.put(name, subCache); + + return image; + } + + @Override + public Image getSmallIcon(CmsIcon icon) { + return getIcon(icon.name(), getSmallIconSize()); + } + + @Override + public Image getBigIcon(CmsIcon icon) { + return getIcon(icon.name(), getBigIconSize()); + } + +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/osgi/BundleSvgTheme.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/osgi/BundleSvgTheme.java new file mode 100644 index 000000000..e65f226e2 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/osgi/BundleSvgTheme.java @@ -0,0 +1,105 @@ +package org.argeo.cms.swt.osgi; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.apache.batik.transcoder.TranscoderException; +import org.apache.batik.transcoder.TranscoderInput; +import org.apache.batik.transcoder.TranscoderOutput; +import org.apache.batik.transcoder.image.ImageTranscoder; +import org.apache.batik.transcoder.image.PNGTranscoder; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.widgets.Display; +import org.osgi.framework.BundleContext; + +/** Theme which can dynamically create icons from SVG data. */ +public class BundleSvgTheme extends BundleCmsSwtTheme { + private final static Logger logger = System.getLogger(BundleSvgTheme.class.getName()); + + private Map> imageCache = Collections.synchronizedMap(new HashMap<>()); + + private Map transcoders = Collections.synchronizedMap(new HashMap<>()); + + @Override + public Image getIcon(String name, Integer preferredSize) { + String path = "icons/types/svg/" + name + ".svg"; + return createImageFromSvg(path, preferredSize); + } + + protected Image createImageFromSvg(String path, Integer preferredSize) { + Image image = null; + if (imageCache.containsKey(path)) { + image = imageCache.get(path).get(preferredSize); + } + if (image != null) + return image; + ImageData imageData = loadFromSvg(path, preferredSize); + image = new Image(Display.getDefault(), imageData); + if (!imageCache.containsKey(path)) + imageCache.put(path, Collections.synchronizedMap(new HashMap<>())); + imageCache.get(path).put(preferredSize, image); + return image; + } + + protected ImageData loadFromSvg(String path, int size) { + ImageTranscoder transcoder = null; + synchronized (this) { + transcoder = transcoders.get(size); + if (transcoder == null) { + transcoder = new PNGTranscoder(); + transcoder.addTranscodingHint(PNGTranscoder.KEY_WIDTH, (float) size); + transcoder.addTranscodingHint(PNGTranscoder.KEY_HEIGHT, (float) size); + transcoders.put(size, transcoder); + } + } + ImageData imageData; + try (InputStream in = getResourceAsStream(path); ByteArrayOutputStream out = new ByteArrayOutputStream();) { + if (in == null) + throw new IllegalArgumentException(path + " not found"); + TranscoderInput input = new TranscoderInput(in); + TranscoderOutput output = new TranscoderOutput(out); + transcoder.transcode(input, output); + try (InputStream imageIn = new ByteArrayInputStream(out.toByteArray())) { + imageData = new ImageData(imageIn); + } + logger.log(Level.DEBUG, () -> "Generated " + size + "x" + size + " PNG icon from " + path); + } catch (IOException | TranscoderException e) { + throw new RuntimeException("Cannot transcode SVG " + path, e); + } + + return imageData; + } + + @Override + public void init(BundleContext bundleContext, Map properties) { + super.init(bundleContext, properties); + + // preload all icons +// paths: for (String p : getImagesPaths()) { +// if (!p.endsWith(".svg")) +// continue paths; +// createImageFromSvg(p, getDefaultIconSize()); +// } + } + + @Override + public void destroy(BundleContext bundleContext, Map properties) { + Display display = Display.getDefault(); + if (display != null) + for (String path : imageCache.keySet()) { + for (Image image : imageCache.get(path).values()) { + display.syncExec(() -> image.dispose()); + } + } + super.destroy(bundleContext, properties); + } + +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/PickUpUserDialog.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/PickUpUserDialog.java new file mode 100644 index 000000000..ed1bfd868 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/PickUpUserDialog.java @@ -0,0 +1,246 @@ +package org.argeo.cms.swt.useradmin; + +import java.util.ArrayList; +import java.util.List; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.eclipse.ui.ColumnDefinition; +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.parts.LdifUsersTable; +import org.argeo.util.naming.LdapAttrs; +import org.argeo.util.naming.LdapObjs; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.dialogs.TrayDialog; +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.TableViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Shell; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.service.useradmin.Group; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.User; +import org.osgi.service.useradmin.UserAdmin; + +/** Dialog with a user (or group) list to pick up one */ +public class PickUpUserDialog extends TrayDialog { + private static final long serialVersionUID = -1420106871173920369L; + + // Business objects + private final UserAdmin userAdmin; + private User selectedUser; + + // this page widgets and UI objects + private String title; + private LdifUsersTable userTableViewerCmp; + private TableViewer userViewer; + private List columnDefs = new ArrayList(); + + /** + * A dialog to pick up a group or a user, showing a table with default + * columns + */ + public PickUpUserDialog(Shell parentShell, String title, UserAdmin userAdmin) { + super(parentShell); + this.title = title; + this.userAdmin = userAdmin; + + columnDefs.add(new ColumnDefinition(new UserLP(UserLP.COL_ICON), "", + 24, 24)); + columnDefs.add(new ColumnDefinition( + new UserLP(UserLP.COL_DISPLAY_NAME), "Common Name", 150, 100)); + columnDefs.add(new ColumnDefinition(new UserLP(UserLP.COL_DOMAIN), + "Domain", 100, 120)); + columnDefs.add(new ColumnDefinition(new UserLP(UserLP.COL_DN), + "Distinguished Name", 300, 100)); + } + + /** A dialog to pick up a group or a user */ + public PickUpUserDialog(Shell parentShell, String title, + UserAdmin userAdmin, List columnDefs) { + super(parentShell); + this.title = title; + this.userAdmin = userAdmin; + this.columnDefs = columnDefs; + } + + @Override + protected void okPressed() { + if (getSelected() == null) + MessageDialog.openError(getShell(), "No user chosen", + "Please, choose a user or press Cancel."); + else + super.okPressed(); + } + + protected Control createDialogArea(Composite parent) { + Composite dialogArea = (Composite) super.createDialogArea(parent); + dialogArea.setLayout(new FillLayout()); + + Composite bodyCmp = new Composite(dialogArea, SWT.NO_FOCUS); + bodyCmp.setLayout(new GridLayout()); + + // Create and configure the table + userTableViewerCmp = new MyUserTableViewer(bodyCmp, SWT.MULTI + | SWT.H_SCROLL | SWT.V_SCROLL); + + userTableViewerCmp.setColumnDefinitions(columnDefs); + userTableViewerCmp.populateWithStaticFilters(false, false); + GridData gd = EclipseUiUtils.fillAll(); + gd.minimumHeight = 300; + userTableViewerCmp.setLayoutData(gd); + userTableViewerCmp.refresh(); + + // Controllers + userViewer = userTableViewerCmp.getTableViewer(); + userViewer.addDoubleClickListener(new MyDoubleClickListener()); + userViewer + .addSelectionChangedListener(new MySelectionChangedListener()); + + parent.pack(); + return dialogArea; + } + + public User getSelected() { + if (selectedUser == null) + return null; + else + return selectedUser; + } + + protected void configureShell(Shell shell) { + super.configureShell(shell); + shell.setText(title); + } + + class MyDoubleClickListener implements IDoubleClickListener { + public void doubleClick(DoubleClickEvent evt) { + if (evt.getSelection().isEmpty()) + return; + + Object obj = ((IStructuredSelection) evt.getSelection()) + .getFirstElement(); + if (obj instanceof User) { + selectedUser = (User) obj; + okPressed(); + } + } + } + + class MySelectionChangedListener implements ISelectionChangedListener { + @Override + public void selectionChanged(SelectionChangedEvent event) { + if (event.getSelection().isEmpty()) { + selectedUser = null; + return; + } + Object obj = ((IStructuredSelection) event.getSelection()) + .getFirstElement(); + if (obj instanceof Group) { + selectedUser = (Group) obj; + } + } + } + + private class MyUserTableViewer extends LdifUsersTable { + private static final long serialVersionUID = 8467999509931900367L; + + private final String[] knownProps = { LdapAttrs.uid.name(), + LdapAttrs.cn.name(), LdapAttrs.DN }; + + private Button showSystemRoleBtn; + private Button showUserBtn; + + public MyUserTableViewer(Composite parent, int style) { + super(parent, style); + } + + protected void populateStaticFilters(Composite staticFilterCmp) { + staticFilterCmp.setLayout(new GridLayout()); + showSystemRoleBtn = new Button(staticFilterCmp, SWT.CHECK); + showSystemRoleBtn.setText("Show system roles "); + + showUserBtn = new Button(staticFilterCmp, SWT.CHECK); + showUserBtn.setText("Show users "); + + SelectionListener sl = new SelectionAdapter() { + private static final long serialVersionUID = -7033424592697691676L; + + @Override + public void widgetSelected(SelectionEvent e) { + refresh(); + } + }; + + showSystemRoleBtn.addSelectionListener(sl); + showUserBtn.addSelectionListener(sl); + } + + @Override + protected List listFilteredElements(String filter) { + Role[] roles; + try { + StringBuilder builder = new StringBuilder(); + + StringBuilder filterBuilder = new StringBuilder(); + if (notNull(filter)) + for (String prop : knownProps) { + filterBuilder.append("("); + filterBuilder.append(prop); + filterBuilder.append("=*"); + filterBuilder.append(filter); + filterBuilder.append("*)"); + } + + String typeStr = "(" + LdapAttrs.objectClass.name() + "=" + + LdapObjs.groupOfNames.name() + ")"; + if ((showUserBtn.getSelection())) + typeStr = "(|(" + LdapAttrs.objectClass.name() + "=" + + LdapObjs.inetOrgPerson.name() + ")" + typeStr + + ")"; + + if (!showSystemRoleBtn.getSelection()) + typeStr = "(& " + typeStr + "(!(" + LdapAttrs.DN + "=*" + + CmsConstants.ROLES_BASEDN + ")))"; + + if (filterBuilder.length() > 1) { + builder.append("(&" + typeStr); + builder.append("(|"); + builder.append(filterBuilder.toString()); + builder.append("))"); + } else { + builder.append(typeStr); + } + roles = userAdmin.getRoles(builder.toString()); + } catch (InvalidSyntaxException e) { + throw new EclipseUiException( + "Unable to get roles with filter: " + filter, e); + } + List users = new ArrayList(); + for (Role role : roles) + if (!users.contains(role)) + users.add((User) role); + return users; + } + } + + private boolean notNull(String string) { + if (string == null) + return false; + else + return !"".equals(string.trim()); + } +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/UserLP.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/UserLP.java new file mode 100644 index 000000000..d1c90a43f --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/UserLP.java @@ -0,0 +1,76 @@ +package org.argeo.cms.swt.useradmin; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.cms.auth.UserAdminUtils; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Display; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.User; + +/** Centralize label providers for the group table */ +class UserLP extends ColumnLabelProvider { + private static final long serialVersionUID = -4645930210988368571L; + + final static String COL_ICON = "colID.icon"; + final static String COL_DN = "colID.dn"; + final static String COL_DISPLAY_NAME = "colID.displayName"; + final static String COL_DOMAIN = "colID.domain"; + + final String currType; + + // private Font italic; + private Font bold; + + UserLP(String colId) { + this.currType = colId; + } + + @Override + public Font getFont(Object element) { + // Current user as bold + if (UserAdminUtils.isCurrentUser(((User) element))) { + if (bold == null) + bold = JFaceResources.getFontRegistry().defaultFontDescriptor().setStyle(SWT.BOLD) + .createFont(Display.getCurrent()); + return bold; + } + return null; + } + + @Override + public Image getImage(Object element) { + if (COL_ICON.equals(currType)) { + User user = (User) element; + String dn = user.getName(); + if (dn.endsWith(CmsConstants.ROLES_BASEDN)) + return UsersImages.ICON_ROLE; + else if (user.getType() == Role.GROUP) + return UsersImages.ICON_GROUP; + else + return UsersImages.ICON_USER; + } else + return null; + } + + @Override + public String getText(Object element) { + User user = (User) element; + return getText(user); + + } + + public String getText(User user) { + if (COL_DN.equals(currType)) + return user.getName(); + else if (COL_DISPLAY_NAME.equals(currType)) + return UserAdminUtils.getCommonName(user); + else if (COL_DOMAIN.equals(currType)) + return UserAdminUtils.getDomainName(user); + else + return ""; + } +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/UsersImages.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/UsersImages.java new file mode 100644 index 000000000..21fc5afba --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/UsersImages.java @@ -0,0 +1,14 @@ +package org.argeo.cms.swt.useradmin; + +import org.argeo.cms.ui.theme.CmsImages; +import org.eclipse.swt.graphics.Image; + +/** Specific users icons. */ +public class UsersImages { + private final static String PREFIX = "icons/"; + + public final static Image ICON_USER = CmsImages.createImg(PREFIX + "person.png"); + public final static Image ICON_GROUP = CmsImages.createImg(PREFIX + "group.png"); + public final static Image ICON_ROLE = CmsImages.createImg(PREFIX + "role.gif"); + public final static Image ICON_CHANGE_PASSWORD = CmsImages.createImg(PREFIX + "security.gif"); +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/package-info.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/package-info.java new file mode 100644 index 000000000..3597bfc57 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/package-info.java @@ -0,0 +1,2 @@ +/** SWT/JFace users management components. */ +package org.argeo.cms.swt.useradmin; \ No newline at end of file diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/AbstractSwtPart.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/AbstractSwtPart.java new file mode 100644 index 000000000..c3d11a181 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/AbstractSwtPart.java @@ -0,0 +1,45 @@ +package org.argeo.cms.swt.widgets; + +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ux.widgets.DataPart; +import org.argeo.cms.ux.widgets.DataView; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.widgets.Composite; + +public abstract class AbstractSwtPart extends Composite implements DataView { + private static final long serialVersionUID = -1999179054267812170L; + + protected DataPart dataPart; + + protected final SelectionListener selectionListener; + + public AbstractSwtPart(Composite parent, int style, DataPart dataPart) { + super(parent, style); + setLayout(CmsSwtUtils.noSpaceGridLayout()); + + this.dataPart = dataPart; + + selectionListener = new SelectionListener() { + + private static final long serialVersionUID = 4334785560035009330L; + + @Override + public void widgetSelected(SelectionEvent e) { + if (dataPart.getOnSelected() != null) + dataPart.getOnSelected().accept((TYPE) e.item.getData()); + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + if (dataPart.getOnAction() != null) + dataPart.getOnAction().accept((TYPE) e.item.getData()); + } + }; + + dataPart.addView(this); + addDisposeListener((e) -> dataPart.removeView(this)); + } + + public abstract void refresh(); +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/ContextOverlay.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/ContextOverlay.java new file mode 100644 index 000000000..f7b644377 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/ContextOverlay.java @@ -0,0 +1,113 @@ +package org.argeo.cms.swt.widgets; + +import org.argeo.cms.swt.CmsSwtUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ShellAdapter; +import org.eclipse.swt.events.ShellEvent; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Shell; + +/** + * Manages a lightweight shell which is related to a {@link Control}, typically + * in order to reproduce a dropdown semantic, but with more flexibility. + */ +public class ContextOverlay extends ScrolledPage { + private static final long serialVersionUID = 6702077429573324009L; + +// private Shell shell; + private Control control; + + private int maxHeight = 400; + + public ContextOverlay(Control control, int style) { + super(createShell(control, style), SWT.NONE); + Shell shell = getShell(); + setLayoutData(CmsSwtUtils.fillAll()); + // TODO make autohide configurable? + //shell.addShellListener(new AutoHideShellListener()); + this.control = control; + control.addDisposeListener((e) -> { + dispose(); + shell.dispose(); + }); + } + + private static Composite createShell(Control control, int style) { + if (control == null) + throw new IllegalArgumentException("Control cannot be null"); + if (control.isDisposed()) + throw new IllegalArgumentException("Control is disposed"); + Shell shell = new Shell(control.getShell(), SWT.NO_TRIM); + shell.setLayout(CmsSwtUtils.noSpaceGridLayout()); + Composite placeholder = new Composite(shell, SWT.BORDER); + placeholder.setLayoutData(CmsSwtUtils.fillAll()); + placeholder.setLayout(CmsSwtUtils.noSpaceGridLayout()); + return placeholder; + } + + public void show() { + Point relativeControlLocation = control.getLocation(); + Point controlLocation = control.toDisplay(relativeControlLocation.x, relativeControlLocation.y); + + int controlWidth = control.getBounds().width; + + Shell shell = getShell(); + + layout(true, true); + shell.pack(); + shell.layout(true, true); + int targetShellWidth = shell.getSize().x < controlWidth ? controlWidth : shell.getSize().x; + if (shell.getSize().y > maxHeight) { + shell.setSize(targetShellWidth, maxHeight); + } else { + shell.setSize(targetShellWidth, shell.getSize().y); + } + + int shellHeight = shell.getSize().y; + int controlHeight = control.getBounds().height; + Point shellLocation = new Point(controlLocation.x, controlLocation.y + controlHeight); + int displayHeight = shell.getDisplay().getBounds().height; + if (shellLocation.y + shellHeight > displayHeight) {// bottom of page + shellLocation = new Point(controlLocation.x, controlLocation.y - shellHeight); + } + shell.setLocation(shellLocation); + + if (getChildren().length != 0) + shell.open(); + if (!control.isDisposed()) + control.setFocus(); + } + + public void hide() { + getShell().setVisible(false); + onHide(); + } + + public boolean isShellVisible() { + if (isDisposed()) + return false; + return getShell().isVisible(); + } + + /** to be overridden */ + protected void onHide() { + // does nothing by default. + } + + private class AutoHideShellListener extends ShellAdapter { + private static final long serialVersionUID = 7743287433907938099L; + + @Override + public void shellDeactivated(ShellEvent e) { + try { + Thread.sleep(1000); + } catch (InterruptedException e1) { + // silent + } + if (!control.isDisposed() && !control.isFocusControl()) + hide(); + } + } +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/EditableText.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/EditableText.java new file mode 100644 index 000000000..0612e8f9b --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/EditableText.java @@ -0,0 +1,135 @@ +package org.argeo.cms.swt.widgets; + +import org.argeo.cms.swt.CmsSwtUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +/** Editable text part displaying styled text. */ +public class EditableText extends StyledControl { + private static final long serialVersionUID = -6372283442330912755L; + + private boolean editable = true; + private boolean multiLine = true; + + private Color highlightColor; + private Composite highlight; + + private boolean useTextAsLabel = false; + + public EditableText(Composite parent, int style) { + super(parent, style); + editable = !(SWT.READ_ONLY == (style & SWT.READ_ONLY)); + multiLine = !(SWT.SINGLE == (style & SWT.SINGLE)); + highlightColor = parent.getDisplay().getSystemColor(SWT.COLOR_GRAY); + useTextAsLabel = SWT.FLAT == (style & SWT.FLAT); + } + + @Override + protected Control createControl(Composite box, String style) { + if (isEditing() && getEditable()) { + return createText(box, style, true); + } else { + if (useTextAsLabel) { + return createTextLabel(box, style); + } else { + return createLabel(box, style); + } + } + } + + protected Label createLabel(Composite box, String style) { + Label lbl = new Label(box, getStyle() | SWT.WRAP); + lbl.setLayoutData(CmsSwtUtils.fillWidth()); + if (style != null) + CmsSwtUtils.style(lbl, style); + CmsSwtUtils.markup(lbl); + if (mouseListener != null) + lbl.addMouseListener(mouseListener); + return lbl; + } + + protected Text createTextLabel(Composite box, String style) { + Text lbl = new Text(box, getStyle() | (multiLine ? SWT.MULTI : SWT.SINGLE)); + lbl.setEditable(false); + lbl.setLayoutData(CmsSwtUtils.fillWidth()); + if (style != null) + CmsSwtUtils.style(lbl, style); + CmsSwtUtils.markup(lbl); + if (mouseListener != null) + lbl.addMouseListener(mouseListener); + return lbl; + } + + protected Text createText(Composite box, String style, boolean editable) { + highlight = new Composite(box, SWT.NONE); + highlight.setBackground(highlightColor); + GridData highlightGd = new GridData(SWT.FILL, SWT.FILL, false, false); + highlightGd.widthHint = 5; + highlightGd.heightHint = 3; + highlight.setLayoutData(highlightGd); + + final Text text = new Text(box, getStyle() | (multiLine ? SWT.MULTI : SWT.SINGLE) | SWT.WRAP); + text.setEditable(editable); + GridData textLayoutData = CmsSwtUtils.fillWidth(); + // textLayoutData.heightHint = preferredHeight; + text.setLayoutData(textLayoutData); + if (style != null) + CmsSwtUtils.style(text, style); + text.setFocus(); + return text; + } + + @Override + protected void clear(boolean deep) { + if (highlight != null) + highlight.dispose(); + super.clear(deep); + } + + public void setText(String text) { + Control child = getControl(); + if (child instanceof Label) + ((Label) child).setText(text); + else if (child instanceof Text) + ((Text) child).setText(text); + } + + public Text getAsText() { + return (Text) getControl(); + } + + public Label getAsLabel() { + return (Label) getControl(); + } + + public String getText() { + Control child = getControl(); + + if (child instanceof Label) + return ((Label) child).getText(); + else if (child instanceof Text) + return ((Text) child).getText(); + else + throw new IllegalStateException("Unsupported control " + child.getClass()); + } + + /** @deprecated Use {@link #isEditable()} instead. */ + @Deprecated + public boolean getEditable() { + return isEditable(); + } + + public boolean isEditable() { + return editable; + } + + public void setUseTextAsLabel(boolean useTextAsLabel) { + this.useTextAsLabel = useTextAsLabel; + } + +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/ScrolledPage.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/ScrolledPage.java new file mode 100644 index 000000000..135f4c147 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/ScrolledPage.java @@ -0,0 +1,74 @@ +package org.argeo.cms.swt.widgets; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +/** + * A composite that can be scrolled vertically. It wraps a + * {@link ScrolledComposite} (and is being wrapped by it), simplifying its + * configuration. + */ +public class ScrolledPage extends Composite { + private static final long serialVersionUID = 1593536965663574437L; + + private ScrolledComposite scrolledComposite; + + public ScrolledPage(Composite parent, int style) { + this(parent, style, false); + } + + public ScrolledPage(Composite parent, int style, boolean alwaysShowScroll) { + super(createScrolledComposite(parent, alwaysShowScroll), style); + scrolledComposite = (ScrolledComposite) getParent(); + scrolledComposite.setContent(this); + + scrolledComposite.setExpandVertical(true); + scrolledComposite.setExpandHorizontal(true); + scrolledComposite.addControlListener(new ScrollControlListener()); + } + + private static ScrolledComposite createScrolledComposite(Composite parent, boolean alwaysShowScroll) { + ScrolledComposite scrolledComposite = new ScrolledComposite(parent, SWT.V_SCROLL); + scrolledComposite.setAlwaysShowScrollBars(alwaysShowScroll); + return scrolledComposite; + } + + @Override + public void layout(boolean changed, boolean all) { + updateScroll(); + super.layout(changed, all); + } + + public void showControl(Control control) { + scrolledComposite.showControl(control); + } + + protected void updateScroll() { + Rectangle r = scrolledComposite.getClientArea(); + Point preferredSize = computeSize(r.width, SWT.DEFAULT); + scrolledComposite.setMinHeight(preferredSize.y); + } + + // public ScrolledComposite getScrolledComposite() { + // return this.scrolledComposite; + // } + + /** Set it on the wrapping scrolled composite */ + @Override + public void setLayoutData(Object layoutData) { + scrolledComposite.setLayoutData(layoutData); + } + + private class ScrollControlListener extends org.eclipse.swt.events.ControlAdapter { + private static final long serialVersionUID = -3586986238567483316L; + + public void controlResized(ControlEvent e) { + updateScroll(); + } + } +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/StyledControl.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/StyledControl.java new file mode 100644 index 000000000..82c04a26c --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/StyledControl.java @@ -0,0 +1,151 @@ +package org.argeo.cms.swt.widgets; + +import org.argeo.api.cms.ux.CmsStyle; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.swt.SwtEditablePart; +import org.argeo.eclipse.ui.specific.EclipseUiSpecificUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +/** Editable text part displaying styled text. */ +public abstract class StyledControl extends Composite implements SwtEditablePart { + private static final long serialVersionUID = -6372283442330912755L; + private Control control; + + private Composite container; + private Composite box; + + protected MouseListener mouseListener; + protected FocusListener focusListener; + + private Boolean editing = Boolean.FALSE; + + private Composite ancestorToLayout; + + public StyledControl(Composite parent, int swtStyle) { + super(parent, swtStyle); + setLayout(CmsSwtUtils.noSpaceGridLayout()); + } + + protected abstract Control createControl(Composite box, String style); + + protected Composite createBox() { + Composite box = new Composite(container, SWT.INHERIT_DEFAULT); + setContainerLayoutData(box); + box.setLayout(CmsSwtUtils.noSpaceGridLayout(3)); + return box; + } + + protected Composite createContainer() { + Composite container = new Composite(this, SWT.INHERIT_DEFAULT); + setContainerLayoutData(container); + container.setLayout(CmsSwtUtils.noSpaceGridLayout()); + return container; + } + + @Override + public Control getControl() { + return control; + } + + protected synchronized Boolean isEditing() { + return editing; + } + + @Override + public synchronized void startEditing() { + assert !isEditing(); + editing = true; + // int height = control.getSize().y; + String style = (String) EclipseUiSpecificUtils.getStyleData(control); + clear(false); + refreshControl(style); + + // add the focus listener to the newly created edition control + if (focusListener != null) + control.addFocusListener(focusListener); + } + + @Override + public synchronized void stopEditing() { + assert isEditing(); + editing = false; + String style = (String) EclipseUiSpecificUtils.getStyleData(control); + clear(false); + refreshControl(style); + } + + protected void refreshControl(String style) { + control = createControl(box, style); + setControlLayoutData(control); + if (ancestorToLayout != null) + ancestorToLayout.layout(true, true); + else + getParent().layout(true, true); + } + + public void setStyle(CmsStyle style) { + setStyle(style.style()); + } + + public void setStyle(String style) { + Object currentStyle = null; + if (control != null) + currentStyle = EclipseUiSpecificUtils.getStyleData(control); + if (currentStyle != null && currentStyle.equals(style)) + return; + + clear(true); + refreshControl(style); + + if (style != null) { + CmsSwtUtils.style(box, style + "_box"); + CmsSwtUtils.style(container, style + "_container"); + } + } + + /** To be overridden */ + protected void setControlLayoutData(Control control) { + control.setLayoutData(CmsSwtUtils.fillWidth()); + } + + /** To be overridden */ + protected void setContainerLayoutData(Composite composite) { + composite.setLayoutData(CmsSwtUtils.fillWidth()); + } + + protected void clear(boolean deep) { + if (deep) { + for (Control control : getChildren()) + control.dispose(); + container = createContainer(); + box = createBox(); + } else { + control.dispose(); + } + } + + public void setMouseListener(MouseListener mouseListener) { + if (this.mouseListener != null && control != null) + control.removeMouseListener(this.mouseListener); + this.mouseListener = mouseListener; + if (control != null && this.mouseListener != null) + control.addMouseListener(mouseListener); + } + + public void setFocusListener(FocusListener focusListener) { + if (this.focusListener != null && control != null) + control.removeFocusListener(this.focusListener); + this.focusListener = focusListener; + if (control != null && this.focusListener != null) + control.addFocusListener(focusListener); + } + + public void setAncestorToLayout(Composite ancestorToLayout) { + this.ancestorToLayout = ancestorToLayout; + } + +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtHierarchicalPart.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtHierarchicalPart.java new file mode 100644 index 000000000..07c9bac2e --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtHierarchicalPart.java @@ -0,0 +1,106 @@ +package org.argeo.cms.swt.widgets; + +import java.util.List; + +import org.argeo.api.cms.ux.CmsIcon; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ux.widgets.HierarchicalPart; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeItem; + +/** {@link HierarchicalPart} implementation based on a {@link Tree}. */ +public class SwtHierarchicalPart extends AbstractSwtPart { + private static final long serialVersionUID = -6247710601465713047L; + + private final Tree tree; + + private HierarchicalPart hierarchicalPart; + + public SwtHierarchicalPart(Composite parent, int style, HierarchicalPart hierarchicalPart) { + super(parent, style, hierarchicalPart); + tree = new Tree(this, SWT.BORDER); + tree.setLayoutData(CmsSwtUtils.fillAll()); + this.hierarchicalPart = hierarchicalPart; + + tree.addSelectionListener(selectionListener); + } + + @Override + public void refresh() { + // TODO optimise + // tree.clearAll(true); + + for (TreeItem rootItem : tree.getItems()) { + rootItem.dispose(); + } + + List rootItems = hierarchicalPart.getChildren(hierarchicalPart.getInput()); + for (T child : rootItems) { + TreeItem childItem = addTreeItem(null, child); +// List grandChildren = hierarchicalPart.getChildren(child); +// for (T grandChild : grandChildren) { +// addTreeItem(childItem, grandChild); +// } + } +// tree.addListener(SWT.SetData, event -> { +// TreeItem item = (TreeItem) event.item; +// TreeItem parentItem = item.getParentItem(); +// if (parentItem == null) { +// refreshRootItem(item); +// } else { +// refreshItem(parentItem, item); +// } +// }); +// tree.setItemCount(getRootItemCount()); + + tree.addListener(SWT.Expand, event -> { + final TreeItem root = (TreeItem) event.item; + TreeItem[] items = root.getItems(); + for (TreeItem item : items) { + if (item.getData() != null) { +// List grandChildren = hierarchicalPart.getChildren((T) item.getData()); +// for (T grandChild : grandChildren) { +// addTreeItem(item, grandChild); +// } + return; + } + item.dispose(); + } + + List children = hierarchicalPart.getChildren((T) root.getData()); + for (T child : children) { + TreeItem childItem = addTreeItem(root, child); +// List grandChildren = hierarchicalPart.getChildren(child); +// for (T grandChild : grandChildren) { +// addTreeItem(childItem, grandChild); +// } + } + }); + + CmsSwtUtils.fill(tree); + + } + + protected TreeItem addTreeItem(TreeItem parent, T data) { + TreeItem item = parent == null ? new TreeItem(tree, SWT.NONE) : new TreeItem(parent, SWT.NONE); + item.setData(data); + String txt = hierarchicalPart.getText(data); + if (txt != null) + item.setText(hierarchicalPart.getText(data)); + CmsIcon icon = hierarchicalPart.getIcon(data); + // TODO optimize + List grandChildren = hierarchicalPart.getChildren(data); + if (grandChildren.size() != 0) + new TreeItem(item, SWT.NONE); + return item; +//if(icon!=null) +// item.setImage(null); + } + + protected Tree getTree() { + return tree; + } + +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtTabularPart.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtTabularPart.java new file mode 100644 index 000000000..2f10cac75 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtTabularPart.java @@ -0,0 +1,82 @@ +package org.argeo.cms.swt.widgets; + +import org.argeo.api.cms.ux.CmsIcon; +import org.argeo.cms.swt.CmsSwtTheme; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ux.widgets.Column; +import org.argeo.cms.ux.widgets.TabularPart; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.swt.widgets.TableItem; + +/** {@link TabularPart} implementation based on a {@link Table}. */ +public class SwtTabularPart extends AbstractSwtPart { + private static final long serialVersionUID = -1114155772446357750L; + private final Table table; + private TabularPart tabularPart; + + private CmsSwtTheme theme; + + public SwtTabularPart(Composite parent, int style, TabularPart tabularPart) { + super(parent, style, tabularPart); + theme = CmsSwtUtils.getCmsTheme(parent); + + table = new Table(this, SWT.VIRTUAL | SWT.BORDER); + table.setLinesVisible(true); + table.setLayoutData(CmsSwtUtils.fillAll()); + + this.tabularPart = tabularPart; + } + + @Override + public void refresh() { + // TODO optimise + table.clearAll(); + table.addListener(SWT.SetData, event -> { + TableItem item = (TableItem) event.item; + refreshItem(item); + }); + table.setItemCount(tabularPart.getItemCount()); + for (int i = 0; i < tabularPart.getColumnCount(); i++) { + TableColumn swtColumn = new TableColumn(table, SWT.NONE); + swtColumn.setWidth(tabularPart.getColumn(i).getWidth()); + } + CmsSwtUtils.fill(table); + + table.addSelectionListener(selectionListener); + + } + + protected Object getDataFromEvent(SelectionEvent e) { + Object data = e.item.getData(); + if (data == null) + data = tabularPart.getData(getTable().indexOf((TableItem) e.item)); + return data; + } + + protected void refreshItem(TableItem item) { + int row = getTable().indexOf(item); + for (int i = 0; i < tabularPart.getColumnCount(); i++) { + Column column = tabularPart.getColumn(i); + T data = tabularPart.getData(row); + item.setData(data); + String text = data != null ? column.getText(data) : ""; + if (text != null) + item.setText(i, text); + CmsIcon icon = column.getIcon(data); + if (icon != null) { + Image image = theme.getSmallIcon(icon); + item.setImage(i, image); + } + } + } + + protected Table getTable() { + return table; + } + +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/ui/theme/CmsImages.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/ui/theme/CmsImages.java new file mode 100644 index 000000000..1c4d79eee --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/ui/theme/CmsImages.java @@ -0,0 +1,49 @@ +package org.argeo.cms.ui.theme; + +import java.net.URL; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Display; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; + +public class CmsImages { + private static BundleContext themeBc = FrameworkUtil.getBundle(CmsImages.class).getBundleContext(); + + final public static String ICONS_BASE = "icons/"; + final public static String TYPES_BASE = ICONS_BASE + "types/"; + final public static String ACTIONS_BASE = ICONS_BASE + "actions/"; + + public static Image createIcon(String name) { + return createImg(CmsImages.ICONS_BASE + name); + } + + public static Image createAction(String name) { + return createImg(CmsImages.ACTIONS_BASE + name); + } + + public static Image createType(String name) { + return createImg(CmsImages.TYPES_BASE + name); + } + + public static Image createImg(String name) { + return CmsImages.createDesc(name).createImage(Display.getDefault()); + } + + public static ImageDescriptor createDesc(String name) { + return createDesc(themeBc, name); + } + + public static ImageDescriptor createDesc(BundleContext bc, String name) { + URL url = bc.getBundle().getResource(name); + if (url == null) + return ImageDescriptor.getMissingImageDescriptor(); + return ImageDescriptor.createFromURL(url); + } + + public static Image createImg(BundleContext bc, String name) { + return createDesc(bc, name).createImage(Display.getDefault()); + } + +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/ui/theme/package-info.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/ui/theme/package-info.java new file mode 100644 index 000000000..7d3a260f3 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/ui/theme/package-info.java @@ -0,0 +1,2 @@ +/** Argeo CMS core theme images. */ +package org.argeo.cms.ui.theme; \ No newline at end of file diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/AbstractTreeContentProvider.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/AbstractTreeContentProvider.java new file mode 100644 index 000000000..64ea2dbc9 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/AbstractTreeContentProvider.java @@ -0,0 +1,43 @@ +package org.argeo.eclipse.ui; + +import org.argeo.cms.ux.widgets.TreeParent; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.Viewer; + +/** + * Tree content provider dealing with tree objects and providing reasonable + * defaults. + */ +public abstract class AbstractTreeContentProvider implements + ITreeContentProvider { + private static final long serialVersionUID = 8246126401957763868L; + + /** Does nothing */ + public void dispose() { + } + + /** Does nothing */ + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + + public Object[] getChildren(Object element) { + if (element instanceof TreeParent) { + return ((TreeParent) element).getChildren(); + } + return new Object[0]; + } + + public Object getParent(Object element) { + if (element instanceof TreeParent) { + return ((TreeParent) element).getParent(); + } + return null; + } + + public boolean hasChildren(Object element) { + if (element instanceof TreeParent) { + return ((TreeParent) element).hasChildren(); + } + return false; + } +} \ No newline at end of file diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/ColumnDefinition.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/ColumnDefinition.java new file mode 100644 index 000000000..a38552c07 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/ColumnDefinition.java @@ -0,0 +1,68 @@ +package org.argeo.eclipse.ui; + +import org.eclipse.jface.viewers.ColumnLabelProvider; + +/** + * Wraps the definition of a column to be used in the various JFace viewers + * (typically tree and table). It enables definition of generic viewers which + * column can be then defined externally. Also used to generate export. + */ +public class ColumnDefinition { + private ColumnLabelProvider labelProvider; + private String label; + private int weight = 0; + private int minWidth = 120; + + public ColumnDefinition(ColumnLabelProvider labelProvider, String label) { + this.labelProvider = labelProvider; + this.label = label; + } + + public ColumnDefinition(ColumnLabelProvider labelProvider, String label, + int weight) { + this.labelProvider = labelProvider; + this.label = label; + this.weight = weight; + this.minWidth = weight; + } + + public ColumnDefinition(ColumnLabelProvider labelProvider, String label, + int weight, int minimumWidth) { + this.labelProvider = labelProvider; + this.label = label; + this.weight = weight; + this.minWidth = minimumWidth; + } + + public ColumnLabelProvider getLabelProvider() { + return labelProvider; + } + + public void setLabelProvider(ColumnLabelProvider labelProvider) { + this.labelProvider = labelProvider; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getMinWidth() { + return minWidth; + } + + public void setMinWidth(int minWidth) { + this.minWidth = minWidth; + } +} \ No newline at end of file diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/ColumnViewerComparator.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/ColumnViewerComparator.java new file mode 100644 index 000000000..9430a2083 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/ColumnViewerComparator.java @@ -0,0 +1,81 @@ +package org.argeo.eclipse.ui; + +import org.eclipse.jface.viewers.ColumnViewer; +import org.eclipse.jface.viewers.TableViewerColumn; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerComparator; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; + +/** Generic column viewer sorter */ +public class ColumnViewerComparator extends ViewerComparator { + private static final long serialVersionUID = -2266218906355859909L; + + public static final int ASC = 1; + + public static final int NONE = 0; + + public static final int DESC = -1; + + private int direction = 0; + + private TableViewerColumn column; + + private ColumnViewer viewer; + + public ColumnViewerComparator(TableViewerColumn column) { + super(null); + this.column = column; + this.viewer = column.getViewer(); + this.column.getColumn().addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = 7586796298965472189L; + + public void widgetSelected(SelectionEvent e) { + if (ColumnViewerComparator.this.viewer.getComparator() != null) { + if (ColumnViewerComparator.this.viewer.getComparator() == ColumnViewerComparator.this) { + int tdirection = ColumnViewerComparator.this.direction; + + if (tdirection == ASC) { + setSortDirection(DESC); + } else if (tdirection == DESC) { + setSortDirection(NONE); + } + } else { + setSortDirection(ASC); + } + } else { + setSortDirection(ASC); + } + } + }); + } + + private void setSortDirection(int direction) { + if (direction == NONE) { + column.getColumn().getParent().setSortColumn(null); + column.getColumn().getParent().setSortDirection(SWT.NONE); + viewer.setComparator(null); + } else { + column.getColumn().getParent().setSortColumn(column.getColumn()); + this.direction = direction; + + if (direction == ASC) { + column.getColumn().getParent().setSortDirection(SWT.DOWN); + } else { + column.getColumn().getParent().setSortDirection(SWT.UP); + } + + if (viewer.getComparator() == this) { + viewer.refresh(); + } else { + viewer.setComparator(this); + } + + } + } + + public int compare(Viewer viewer, Object e1, Object e2) { + return direction * super.compare(viewer, e1, e2); + } +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/EclipseUiException.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/EclipseUiException.java new file mode 100644 index 000000000..37a36e859 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/EclipseUiException.java @@ -0,0 +1,15 @@ +package org.argeo.eclipse.ui; + +/** CMS specific exceptions. */ +public class EclipseUiException extends RuntimeException { + private static final long serialVersionUID = -5341764743356771313L; + + public EclipseUiException(String message) { + super(message); + } + + public EclipseUiException(String message, Throwable e) { + super(message, e); + } + +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/EclipseUiUtils.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/EclipseUiUtils.java new file mode 100644 index 000000000..95b45fed6 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/EclipseUiUtils.java @@ -0,0 +1,195 @@ +package org.argeo.eclipse.ui; + +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.layout.FormAttachment; +import org.eclipse.swt.layout.FormData; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +/** Utilities to simplify UI development. */ +public class EclipseUiUtils { + + /** Dispose all children of a Composite */ + public static void clear(Composite composite) { + for (Control child : composite.getChildren()) + child.dispose(); + } + + /** + * Enables efficient call to the layout method of a composite, refreshing only + * some of the children controls. + */ + public static void layout(Composite parent, Control... toUpdateControls) { + parent.layout(toUpdateControls); + } + + // + // FONTS + // + /** Shortcut to retrieve default italic font from display */ + public static Font getItalicFont(Composite parent) { + return JFaceResources.getFontRegistry().defaultFontDescriptor().setStyle(SWT.ITALIC) + .createFont(parent.getDisplay()); + } + + /** Shortcut to retrieve default bold font from display */ + public static Font getBoldFont(Composite parent) { + return JFaceResources.getFontRegistry().defaultFontDescriptor().setStyle(SWT.BOLD) + .createFont(parent.getDisplay()); + } + + /** Shortcut to retrieve default bold italic font from display */ + public static Font getBoldItalicFont(Composite parent) { + return JFaceResources.getFontRegistry().defaultFontDescriptor().setStyle(SWT.BOLD | SWT.ITALIC) + .createFont(parent.getDisplay()); + } + + // + // Simplify grid layouts management + // + public static GridLayout noSpaceGridLayout() { + return noSpaceGridLayout(new GridLayout()); + } + + public static GridLayout noSpaceGridLayout(int columns) { + return noSpaceGridLayout(new GridLayout(columns, false)); + } + + public static GridLayout noSpaceGridLayout(GridLayout layout) { + layout.horizontalSpacing = 0; + layout.verticalSpacing = 0; + layout.marginWidth = 0; + layout.marginHeight = 0; + return layout; + } + + public static GridData fillWidth() { + return grabWidth(SWT.FILL, SWT.FILL); + } + + public static GridData fillWidth(int colSpan) { + GridData gd = grabWidth(SWT.FILL, SWT.FILL); + gd.horizontalSpan = colSpan; + return gd; + } + + public static GridData fillAll() { + return new GridData(SWT.FILL, SWT.FILL, true, true); + } + + public static GridData fillAll(int colSpan, int rowSpan) { + return new GridData(SWT.FILL, SWT.FILL, true, true, colSpan, rowSpan); + } + + public static GridData grabWidth(int horizontalAlignment, int verticalAlignment) { + return new GridData(horizontalAlignment, horizontalAlignment, true, false); + } + + // + // Simplify Form layout management + // + + /** + * Creates a basic form data that is attached to the 4 corners of the parent + * composite + */ + public static FormData fillFormData() { + FormData formData = new FormData(); + formData.top = new FormAttachment(0, 0); + formData.left = new FormAttachment(0, 0); + formData.right = new FormAttachment(100, 0); + formData.bottom = new FormAttachment(100, 0); + return formData; + } + + /** + * Create a label and a text field for a grid layout, the text field grabbing + * excess horizontal + * + * @param parent + * the parent composite + * @param label + * the label to display + * @param modifyListener + * a {@link ModifyListener} to listen on events on the text, can be + * null + * @return the created text + * + */ + // FIXME why was this deprecated. + // * @ deprecated use { @ link #createGridLT(Composite, String)} instead + // @ Deprecated + public static Text createGridLT(Composite parent, String label, ModifyListener modifyListener) { + Label lbl = new Label(parent, SWT.LEAD); + lbl.setText(label); + lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); + Text txt = new Text(parent, SWT.LEAD | SWT.BORDER); + txt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + if (modifyListener != null) + txt.addModifyListener(modifyListener); + return txt; + } + + /** + * Create a label and a text field for a grid layout, the text field grabbing + * excess horizontal + */ + public static Text createGridLT(Composite parent, String label) { + return createGridLT(parent, label, null); + } + + /** + * Creates one label and a text field not editable with background colour of the + * parent (like a label but with selectable text) + */ + public static Text createGridLL(Composite parent, String label, String text) { + Text txt = createGridLT(parent, label); + txt.setText(text); + txt.setEditable(false); + txt.setBackground(parent.getBackground()); + return txt; + } + + /** + * Create a label and a text field with password display for a grid layout, the + * text field grabbing excess horizontal + */ + public static Text createGridLP(Composite parent, String label) { + return createGridLP(parent, label, null); + } + + /** + * Create a label and a text field with password display for a grid layout, the + * text field grabbing excess horizontal. The given modify listener will be + * added to the newly created text field if not null. + */ + public static Text createGridLP(Composite parent, String label, ModifyListener modifyListener) { + Label lbl = new Label(parent, SWT.LEAD); + lbl.setText(label); + lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); + Text txt = new Text(parent, SWT.LEAD | SWT.BORDER | SWT.PASSWORD); + txt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + if (modifyListener != null) + txt.addModifyListener(modifyListener); + return txt; + } + + // MISCELLANEOUS + + /** Simply checks if a string is not null nor empty */ + public static boolean notEmpty(String stringToTest) { + return !(stringToTest == null || "".equals(stringToTest.trim())); + } + + /** Simply checks if a string is null or empty */ + public static boolean isEmpty(String stringToTest) { + return stringToTest == null || "".equals(stringToTest.trim()); + } +} \ No newline at end of file diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/FileProvider.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/FileProvider.java new file mode 100644 index 000000000..e82505df5 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/FileProvider.java @@ -0,0 +1,16 @@ +package org.argeo.eclipse.ui; + +import java.io.InputStream; + +/** + * Used for file download : subclasses must implement model specific methods to + * get a byte array representing a file given is ID. + */ +@Deprecated +public interface FileProvider { + + public byte[] getByteArrayFileFromId(String fileId); + + public InputStream getInputStreamFromFileId(String fileId); + +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/GenericTableComparator.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/GenericTableComparator.java new file mode 100644 index 000000000..e1d8b05ea --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/GenericTableComparator.java @@ -0,0 +1,39 @@ +package org.argeo.eclipse.ui; + +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerComparator; + +public abstract class GenericTableComparator extends ViewerComparator { + private static final long serialVersionUID = -1175894935075325810L; + protected int propertyIndex; + public static final int ASCENDING = 0, DESCENDING = 1; + protected int direction = DESCENDING; + + /** + * Creates an instance of a sorter for TableViewer. + * + * @param defaultColumnIndex + * the default sorter column + */ + + public GenericTableComparator(int defaultColumnIndex, int direction) { + propertyIndex = defaultColumnIndex; + this.direction = direction; + } + + public void setColumn(int column) { + if (column == this.propertyIndex) { + // Same column as last sort; toggle the direction + direction = 1 - direction; + } else { + // New column; do a descending sort + this.propertyIndex = column; + direction = DESCENDING; + } + } + + /** + * Must be Overriden in each view. + */ + public abstract int compare(Viewer viewer, Object e1, Object e2); +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/IListProvider.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/IListProvider.java new file mode 100644 index 000000000..ac7b2d8fb --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/IListProvider.java @@ -0,0 +1,20 @@ +package org.argeo.eclipse.ui; + +import java.util.List; + +/** + * Views and editors can implement this interface so that one of the list that + * is displayed in the part (For instance in a Table or a Tree Viewer) can be + * rebuilt externally. Typically to generate csv or calc extract. + */ +public interface IListProvider { + /** + * Returns an array of current and relevant elements + */ + public Object[] getElements(String extractId); + + /** + * Returns the column definition for passed ID + */ + public List getColumnDefinition(String extractId); +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/ErrorFeedback.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/ErrorFeedback.java new file mode 100644 index 000000000..a388e745e --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/ErrorFeedback.java @@ -0,0 +1,106 @@ +package org.argeo.eclipse.ui.dialogs; + +import java.io.PrintWriter; +import java.io.StringWriter; + +import org.argeo.api.cms.CmsLog; +import org.eclipse.jface.dialogs.IMessageProvider; +import org.eclipse.jface.dialogs.TitleAreaDialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +/** + * Generic error dialog to be used in try/catch blocks. + * + * @deprecated Use CMS dialogs instead. + */ +@Deprecated +public class ErrorFeedback extends TitleAreaDialog { + private static final long serialVersionUID = -8918084784628179044L; + + private final static CmsLog log = CmsLog.getLog(ErrorFeedback.class); + + private final String message; + private final Throwable exception; + + public static void show(String message, Throwable e) { + // rethrow ThreaDeath in order to make sure that RAP will properly clean + // up the UI thread + if (e instanceof ThreadDeath) + throw (ThreadDeath) e; + + new ErrorFeedback(newShell(), message, e).open(); + } + + public static void show(String message) { + new ErrorFeedback(newShell(), message, null).open(); + } + + private static Shell newShell() { + return new Shell(getDisplay(), SWT.NO_TRIM); + } + + /** Tries to find a display */ + private static Display getDisplay() { + try { + Display display = Display.getCurrent(); + if (display != null) + return display; + else + return Display.getDefault(); + } catch (Exception e) { + return Display.getCurrent(); + } + } + + public ErrorFeedback(Shell parentShell, String message, Throwable e) { + super(parentShell); + setShellStyle(SWT.NO_TRIM); + this.message = message; + this.exception = e; + log.error(message, e); + } + + protected Point getInitialSize() { + if (exception != null) + return new Point(800, 600); + else + return new Point(400, 300); + } + + @Override + protected Control createDialogArea(Composite parent) { + Composite dialogarea = (Composite) super.createDialogArea(parent); + dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + Composite composite = new Composite(dialogarea, SWT.NONE); + composite.setLayout(new GridLayout(2, false)); + composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + setMessage(message != null ? message + (exception != null ? ": " + exception.getMessage() : "") + : exception != null ? exception.getMessage() : "Unkown Error", IMessageProvider.ERROR); + + if (exception != null) { + Text stack = new Text(composite, SWT.MULTI | SWT.LEAD | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL); + stack.setEditable(false); + stack.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + StringWriter sw = new StringWriter(); + exception.printStackTrace(new PrintWriter(sw)); + stack.setText(sw.toString()); + } + + parent.pack(); + return composite; + } + + protected void configureShell(Shell shell) { + super.configureShell(shell); + shell.setText("Error"); + } +} \ No newline at end of file diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/FeedbackDialog.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/FeedbackDialog.java new file mode 100644 index 000000000..f2715bc05 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/FeedbackDialog.java @@ -0,0 +1,141 @@ +package org.argeo.eclipse.ui.dialogs; + +import java.io.PrintWriter; +import java.io.StringWriter; + +import org.argeo.api.cms.CmsLog; +import org.argeo.eclipse.ui.EclipseUiException; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ShellAdapter; +import org.eclipse.swt.events.ShellEvent; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +/** + * Generic lightweight dialog, not based on JFace. + * + * @deprecated Use CMS dialogs instead. + */ +@Deprecated +public class FeedbackDialog extends LightweightDialog { + private final static CmsLog log = CmsLog.getLog(FeedbackDialog.class); + + private String message; + private Throwable exception; + +// private Shell parentShell; + private Shell shell; + + public static void show(String message, Throwable e) { + // rethrow ThreaDeath in order to make sure that RAP will properly clean + // up the UI thread + if (e instanceof ThreadDeath) + throw (ThreadDeath) e; + + new FeedbackDialog(getDisplay().getActiveShell(), message, e).open(); + } + + public static void show(String message) { + new FeedbackDialog(getDisplay().getActiveShell(), message, null).open(); + } + + /** Tries to find a display */ + private static Display getDisplay() { + try { + Display display = Display.getCurrent(); + if (display != null) + return display; + else + return Display.getDefault(); + } catch (Exception e) { + return Display.getCurrent(); + } + } + + public FeedbackDialog(Shell parentShell, String message, Throwable e) { + super(parentShell); + this.message = message; + this.exception = e; + log.error(message, e); + } + + public int open() { + if (shell != null) + throw new EclipseUiException("There is already a shell"); + shell = new Shell(getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP); + shell.setLayout(new GridLayout()); + // shell.setText("Error"); + shell.setSize(getInitialSize()); + createDialogArea(shell); + // shell.pack(); + // shell.layout(); + + Rectangle shellBounds = Display.getCurrent().getBounds();// RAP + Point dialogSize = shell.getSize(); + int x = shellBounds.x + (shellBounds.width - dialogSize.x) / 2; + int y = shellBounds.y + (shellBounds.height - dialogSize.y) / 2; + shell.setLocation(x, y); + + shell.addShellListener(new ShellAdapter() { + private static final long serialVersionUID = -2701270481953688763L; + + @Override + public void shellDeactivated(ShellEvent e) { + closeShell(); + } + }); + + shell.open(); + return OK; + } + + protected void closeShell() { + shell.close(); + shell.dispose(); + shell = null; + } + + protected Point getInitialSize() { + // if (exception != null) + // return new Point(800, 600); + // else + return new Point(400, 300); + } + + protected Control createDialogArea(Composite parent) { + Composite dialogarea = new Composite(parent, SWT.NONE); + dialogarea.setLayout(new GridLayout()); + // Composite dialogarea = (Composite) super.createDialogArea(parent); + dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + Label messageLbl = new Label(dialogarea, SWT.NONE); + if (message != null) + messageLbl.setText(message); + else if (exception != null) + messageLbl.setText(exception.getLocalizedMessage()); + + Composite composite = new Composite(dialogarea, SWT.NONE); + composite.setLayout(new GridLayout(2, false)); + composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + if (exception != null) { + Text stack = new Text(composite, SWT.MULTI | SWT.LEAD | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL); + stack.setEditable(false); + stack.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + StringWriter sw = new StringWriter(); + exception.printStackTrace(new PrintWriter(sw)); + stack.setText(sw.toString()); + } + + // parent.pack(); + return composite; + } +} \ No newline at end of file diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/LightweightDialog.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/LightweightDialog.java new file mode 100644 index 000000000..615e1417a --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/LightweightDialog.java @@ -0,0 +1,256 @@ +package org.argeo.eclipse.ui.dialogs; + +import org.argeo.api.cms.CmsLog; +import org.argeo.eclipse.ui.EclipseUiException; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.ShellAdapter; +import org.eclipse.swt.events.ShellEvent; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +/** Generic lightweight dialog, not based on JFace. */ +@Deprecated +public class LightweightDialog { + private final static CmsLog log = CmsLog.getLog(LightweightDialog.class); + + // must be the same value as org.eclipse.jface.window.Window#OK + public final static int OK = 0; + // must be the same value as org.eclipse.jface.window.Window#CANCEL + public final static int CANCEL = 1; + + private Shell parentShell; + private Shell backgroundShell; + private Shell foregoundShell; + + private Integer returnCode = null; + private boolean block = true; + + private String title; + + /** Tries to find a display */ + private static Display getDisplay() { + try { + Display display = Display.getCurrent(); + if (display != null) + return display; + else + return Display.getDefault(); + } catch (Exception e) { + return Display.getCurrent(); + } + } + + public LightweightDialog(Shell parentShell) { + this.parentShell = parentShell; + } + + public int open() { + if (foregoundShell != null) + throw new EclipseUiException("There is already a shell"); + backgroundShell = new Shell(parentShell, SWT.ON_TOP); + backgroundShell.setFullScreen(true); + // if (parentShell != null) { + // backgroundShell.setBounds(parentShell.getBounds()); + // } else + // backgroundShell.setMaximized(true); + backgroundShell.setAlpha(128); + backgroundShell.setBackground(getDisplay().getSystemColor(SWT.COLOR_BLACK)); + foregoundShell = new Shell(backgroundShell, SWT.NO_TRIM | SWT.ON_TOP); + if (title != null) + setTitle(title); + foregoundShell.setLayout(new GridLayout()); + foregoundShell.setSize(getInitialSize()); + createDialogArea(foregoundShell); + // shell.pack(); + // shell.layout(); + + Rectangle shellBounds = parentShell != null ? parentShell.getBounds() : Display.getCurrent().getBounds();// RAP + Point dialogSize = foregoundShell.getSize(); + int x = shellBounds.x + (shellBounds.width - dialogSize.x) / 2; + int y = shellBounds.y + (shellBounds.height - dialogSize.y) / 2; + foregoundShell.setLocation(x, y); + + foregoundShell.addShellListener(new ShellAdapter() { + private static final long serialVersionUID = -2701270481953688763L; + + @Override + public void shellDeactivated(ShellEvent e) { + if (hasChildShells()) + return; + if (returnCode == null)// not yet closed + closeShell(CANCEL); + } + + @Override + public void shellClosed(ShellEvent e) { + notifyClose(); + } + + }); + + backgroundShell.open(); + foregoundShell.open(); + // after the foreground shell has been opened + backgroundShell.addFocusListener(new FocusListener() { + private static final long serialVersionUID = 3137408447474661070L; + + @Override + public void focusLost(FocusEvent event) { + } + + @Override + public void focusGained(FocusEvent event) { + if (hasChildShells()) + return; + if (returnCode == null)// not yet closed + closeShell(CANCEL); + } + }); + + if (block) { + block(); + } + if (returnCode == null) + returnCode = OK; + return returnCode; + } + + public void block() { + try { + runEventLoop(foregoundShell); + } catch (ThreadDeath t) { + returnCode = CANCEL; + if (log.isTraceEnabled()) + log.error("Thread death, canceling dialog", t); + } catch (Throwable t) { + returnCode = CANCEL; + log.error("Cannot open blocking lightweight dialog", t); + } + } + + private boolean hasChildShells() { + if (foregoundShell == null) + return false; + return foregoundShell.getShells().length != 0; + } + + // public synchronized int openAndWait() { + // open(); + // while (returnCode == null) + // try { + // wait(100); + // } catch (InterruptedException e) { + // // silent + // } + // return returnCode; + // } + + private synchronized void notifyClose() { + if (returnCode == null) + returnCode = CANCEL; + notifyAll(); + } + + protected void closeShell(int returnCode) { + this.returnCode = returnCode; + if (CANCEL == returnCode) + onCancel(); + if (foregoundShell != null && !foregoundShell.isDisposed()) { + foregoundShell.close(); + foregoundShell.dispose(); + foregoundShell = null; + } + + if (backgroundShell != null && !backgroundShell.isDisposed()) { + backgroundShell.close(); + backgroundShell.dispose(); + } + } + + protected Point getInitialSize() { + // if (exception != null) + // return new Point(800, 600); + // else + return new Point(600, 400); + } + + protected Control createDialogArea(Composite parent) { + Composite dialogarea = new Composite(parent, SWT.NONE); + dialogarea.setLayout(new GridLayout()); + dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + return dialogarea; + } + + protected Shell getBackgroundShell() { + return backgroundShell; + } + + protected Shell getForegoundShell() { + return foregoundShell; + } + + public void setBlockOnOpen(boolean shouldBlock) { + block = shouldBlock; + } + + public void pack() { + foregoundShell.pack(); + } + + private void runEventLoop(Shell loopShell) { + Display display; + if (foregoundShell == null) { + display = Display.getCurrent(); + } else { + display = loopShell.getDisplay(); + } + + while (loopShell != null && !loopShell.isDisposed()) { + try { + if (!display.readAndDispatch()) { + display.sleep(); + } + } catch (UnsupportedOperationException e) { + throw e; + } catch (Throwable e) { + handleException(e); + } + } + if (!display.isDisposed()) + display.update(); + } + + protected void handleException(Throwable t) { + if (t instanceof ThreadDeath) { + // Don't catch ThreadDeath as this is a normal occurrence when + // the thread dies + throw (ThreadDeath) t; + } + // Try to keep running. + t.printStackTrace(); + } + + /** @return false, if the dialog should not be closed. */ + protected boolean onCancel() { + return true; + } + + public void setTitle(String title) { + this.title = title; + if (title != null && getForegoundShell() != null) + getForegoundShell().setText(title); + } + + public Integer getReturnCode() { + return returnCode; + } + +} \ No newline at end of file diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/SingleValue.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/SingleValue.java new file mode 100644 index 000000000..8ce9b44fb --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/SingleValue.java @@ -0,0 +1,130 @@ +package org.argeo.eclipse.ui.dialogs; + +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.eclipse.jface.dialogs.IMessageProvider; +import org.eclipse.jface.dialogs.TitleAreaDialog; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +/** + * Dialog to retrieve a single value. + * + * @deprecated Use CMS dialogs instead. + */ +@Deprecated +public class SingleValue extends TitleAreaDialog { + private static final long serialVersionUID = 2843538207460082349L; + + private Text valueT; + private String value; + private final String title, message, label; + private final Boolean multiline; + + public static String ask(String label, String message) { + SingleValue svd = new SingleValue(label, message); + if (svd.open() == Window.OK) + return svd.getString(); + else + return null; + } + + public static Long askLong(String label, String message) { + SingleValue svd = new SingleValue(label, message); + if (svd.open() == Window.OK) + return svd.getLong(); + else + return null; + } + + public static Double askDouble(String label, String message) { + SingleValue svd = new SingleValue(label, message); + if (svd.open() == Window.OK) + return svd.getDouble(); + else + return null; + } + + public SingleValue(String label, String message) { + this(Display.getDefault().getActiveShell(), label, message, label, false); + } + + public SingleValue(Shell parentShell, String title, String message, String label, Boolean multiline) { + super(parentShell); + this.title = title; + this.message = message; + this.label = label; + this.multiline = multiline; + } + + protected Point getInitialSize() { + if (multiline) + return new Point(450, 350); + + else + return new Point(400, 270); + } + + protected Control createDialogArea(Composite parent) { + Composite dialogarea = (Composite) super.createDialogArea(parent); + dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + Composite composite = new Composite(dialogarea, SWT.NONE); + composite.setLayoutData(EclipseUiUtils.fillAll()); + GridLayout layout = new GridLayout(2, false); + layout.marginWidth = layout.marginHeight = 20; + composite.setLayout(layout); + + valueT = createLT(composite, label); + + setMessage(message, IMessageProvider.NONE); + + parent.pack(); + valueT.setFocus(); + return composite; + } + + @Override + protected void okPressed() { + value = valueT.getText(); + super.okPressed(); + } + + /** Creates label and text. */ + protected Text createLT(Composite parent, String label) { + new Label(parent, SWT.NONE).setText(label); + Text text; + if (multiline) { + text = new Text(parent, SWT.LEAD | SWT.BORDER | SWT.MULTI); + text.setLayoutData(EclipseUiUtils.fillAll()); + } else { + text = new Text(parent, SWT.LEAD | SWT.BORDER | SWT.SINGLE); + text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true)); + } + return text; + } + + protected void configureShell(Shell shell) { + super.configureShell(shell); + shell.setText(title); + } + + public String getString() { + return value; + } + + public Long getLong() { + return Long.valueOf(getString()); + } + + public Double getDouble() { + return Double.valueOf(getString()); + } +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/package-info.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/package-info.java new file mode 100644 index 000000000..d6ab1481e --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/package-info.java @@ -0,0 +1,2 @@ +/** Generic SWT/JFace dialogs. */ +package org.argeo.eclipse.ui.dialogs; \ No newline at end of file diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/AdvancedFsBrowser.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/AdvancedFsBrowser.java new file mode 100644 index 000000000..c01b2d751 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/AdvancedFsBrowser.java @@ -0,0 +1,450 @@ +package org.argeo.eclipse.ui.fs; + +import java.io.IOException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.LinkedHashMap; + +import org.argeo.api.cms.CmsLog; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.SashForm; +import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.events.ControlAdapter; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.Text; + +/** Simple UI provider that populates a composite parent given a NIO path */ +public class AdvancedFsBrowser { + private final static CmsLog log = CmsLog.getLog(AdvancedFsBrowser.class); + + // Some local constants to experiment. should be cleaned + // private final static int THUMBNAIL_WIDTH = 400; + // private Point imageWidth = new Point(250, 0); + private final static int COLUMN_WIDTH = 160; + + private Path initialPath; + private Path currEdited; + // Filter + private Composite displayBoxCmp; + private Text parentPathTxt; + private Text filterTxt; + // Browser columns + private ScrolledComposite scrolledCmp; + // Keep a cache of the opened directories + private LinkedHashMap browserCols = new LinkedHashMap<>(); + private Composite scrolledCmpBody; + + public Control createUi(Composite parent, Path basePath) { + if (basePath == null) + throw new IllegalArgumentException("Context cannot be null"); + parent.setLayout(new GridLayout()); + + // top filter + Composite filterCmp = new Composite(parent, SWT.NO_FOCUS); + filterCmp.setLayoutData(EclipseUiUtils.fillWidth()); + addFilterPanel(filterCmp); + + // Bottom part a sash with browser on the left + SashForm form = new SashForm(parent, SWT.HORIZONTAL); + // form.setLayout(new FillLayout()); + form.setLayoutData(EclipseUiUtils.fillAll()); + Composite leftCmp = new Composite(form, SWT.NO_FOCUS); + displayBoxCmp = new Composite(form, SWT.NONE); + form.setWeights(new int[] { 3, 1 }); + + createBrowserPart(leftCmp, basePath); + // leftCmp.addControlListener(new ControlAdapter() { + // @Override + // public void controlResized(ControlEvent e) { + // Rectangle r = leftCmp.getClientArea(); + // log.warn("Browser resized: " + r.toString()); + // scrolledCmp.setMinSize(browserCols.size() * (COLUMN_WIDTH + 2), + // SWT.DEFAULT); + // // scrolledCmp.setMinSize(scrolledCmpBody.computeSize(SWT.DEFAULT, + // // r.height)); + // } + // }); + + populateCurrEditedDisplay(displayBoxCmp, basePath); + + // INIT + setEdited(basePath); + initialPath = basePath; + // form.layout(true, true); + return parent; + } + + private void createBrowserPart(Composite parent, Path context) { + parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); + + // scrolled composite + scrolledCmp = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.BORDER | SWT.NO_FOCUS); + scrolledCmp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + scrolledCmp.setExpandVertical(true); + scrolledCmp.setExpandHorizontal(true); + scrolledCmp.setShowFocusedControl(true); + + scrolledCmpBody = new Composite(scrolledCmp, SWT.NO_FOCUS); + scrolledCmp.setContent(scrolledCmpBody); + scrolledCmpBody.addControlListener(new ControlAdapter() { + private static final long serialVersionUID = 183238447102854553L; + + @Override + public void controlResized(ControlEvent e) { + Rectangle r = scrolledCmp.getClientArea(); + scrolledCmp.setMinSize(scrolledCmpBody.computeSize(SWT.DEFAULT, r.height)); + } + }); + initExplorer(scrolledCmpBody, context); + scrolledCmpBody.layout(true, true); + scrolledCmp.layout(); + + } + + private Control initExplorer(Composite parent, Path context) { + parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); + return createBrowserColumn(parent, context); + } + + private Control createBrowserColumn(Composite parent, Path context) { + // TODO style is not correctly managed. + FilterEntitiesVirtualTable table = new FilterEntitiesVirtualTable(parent, SWT.BORDER | SWT.NO_FOCUS, context); + // CmsUtils.style(table, ArgeoOrgStyle.browserColumn.style()); + table.filterList("*"); + table.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, false, true)); + browserCols.put(context, table); + parent.layout(true, true); + return table; + } + + public void addFilterPanel(Composite parent) { + parent.setLayout(EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, false))); + + parentPathTxt = new Text(parent, SWT.NO_FOCUS); + parentPathTxt.setEditable(false); + + filterTxt = new Text(parent, SWT.SEARCH | SWT.ICON_CANCEL); + filterTxt.setMessage("Filter current list"); + filterTxt.setLayoutData(EclipseUiUtils.fillWidth()); + 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(currEdited, 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 FsUiException( + "Unable to determine unique child existence and get it under " + parent + " with filter " + filter, + ioe); + } + } + + private void setEdited(Path path) { + currEdited = path; + EclipseUiUtils.clear(displayBoxCmp); + populateCurrEditedDisplay(displayBoxCmp, currEdited); + refreshFilters(path); + refreshBrowser(path); + } + + private void refreshFilters(Path path) { + parentPathTxt.setText(path.toUri().toString()); + filterTxt.setText(""); + filterTxt.getParent().layout(); + } + + private void refreshBrowser(Path currPath) { + Path currParPath = currPath.getParent(); + Object[][] colMatrix = new Object[browserCols.size()][2]; + + int i = 0, currPathIndex = -1, lastLeftOpenedIndex = -1; + for (Path path : browserCols.keySet()) { + colMatrix[i][0] = path; + colMatrix[i][1] = browserCols.get(path); + if (currPathIndex >= 0 && lastLeftOpenedIndex < 0 && currParPath != null) { + boolean leaveOpened = path.startsWith(currPath); + if (!leaveOpened) + lastLeftOpenedIndex = i; + } + if (currParPath.equals(path)) + currPathIndex = i; + i++; + } + + if (currPathIndex >= 0 && lastLeftOpenedIndex >= 0) { + // dispose and remove useless cols + for (int l = i - 1; l >= lastLeftOpenedIndex; l--) { + ((FilterEntitiesVirtualTable) colMatrix[l][1]).dispose(); + browserCols.remove(colMatrix[l][0]); + } + } + + if (browserCols.containsKey(currPath)) { + FilterEntitiesVirtualTable currCol = browserCols.get(currPath); + if (currCol.isDisposed()) { + // Does it still happen ? + log.warn(currPath + " browser column was disposed and still listed"); + browserCols.remove(currPath); + } + } + + if (!browserCols.containsKey(currPath) && Files.isDirectory(currPath)) + createBrowserColumn(scrolledCmpBody, currPath); + + scrolledCmpBody.setLayout(EclipseUiUtils.noSpaceGridLayout(new GridLayout(browserCols.size(), false))); + scrolledCmpBody.layout(true, true); + // also resize the scrolled composite + scrolledCmp.layout(); + } + + private void modifyFilter(boolean fromOutside) { + if (!fromOutside) + if (currEdited != null) { + String filter = filterTxt.getText() + "*"; + FilterEntitiesVirtualTable table = browserCols.get(currEdited); + if (table != null && !table.isDisposed()) + table.filterList(filter); + } + } + + /** + * Recreates the content of the box that displays information about the current + * selected node. + */ + private void populateCurrEditedDisplay(Composite parent, Path context) { + parent.setLayout(new GridLayout()); + + // if (isImg(context)) { + // EditableImage image = new Img(parent, RIGHT, context, imageWidth); + // image.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false, + // 2, 1)); + // } + + try { + Label contextL = new Label(parent, SWT.NONE); + contextL.setText(context.getFileName().toString()); + contextL.setFont(EclipseUiUtils.getBoldFont(parent)); + addProperty(parent, "Last modified", Files.getLastModifiedTime(context).toString()); + addProperty(parent, "Owner", Files.getOwner(context).getName()); + if (Files.isDirectory(context)) { + addProperty(parent, "Type", "Folder"); + } else { + String mimeType = Files.probeContentType(context); + if (EclipseUiUtils.isEmpty(mimeType)) + mimeType = "Unknown"; + addProperty(parent, "Type", mimeType); + addProperty(parent, "Size", FsUiUtils.humanReadableByteCount(Files.size(context), false)); + } + parent.layout(true, true); + } catch (IOException e) { + throw new FsUiException("Cannot display details for " + context, e); + } + } + + private void addProperty(Composite parent, String propName, String value) { + Label contextL = new Label(parent, SWT.NONE); + contextL.setText(propName + ": " + value); + } + + /** + * Almost canonical implementation of a table that displays the content of a + * directory + */ + private class FilterEntitiesVirtualTable extends Composite { + private static final long serialVersionUID = 2223410043691844875L; + + // Context + private Path context; + private Path currSelected = null; + + // UI Objects + private FsTableViewer viewer; + + @Override + public boolean setFocus() { + if (viewer.getTable().isDisposed()) + return false; + if (currSelected != null) + viewer.setSelection(new StructuredSelection(currSelected), true); + else if (viewer.getSelection().isEmpty()) { + Object first = viewer.getElementAt(0); + if (first != null) + viewer.setSelection(new StructuredSelection(first), true); + } + return viewer.getTable().setFocus(); + } + + /** + * Enable highlighting the correct element in the table when externally browsing + * (typically via the command-line-like Text field) + */ + void setSelected(Path selected) { + // to prevent change selection event to be thrown + currSelected = selected; + viewer.setSelection(new StructuredSelection(currSelected), true); + } + + void filterList(String filter) { + viewer.setInput(context, filter); + } + + public FilterEntitiesVirtualTable(Composite parent, int style, Path context) { + super(parent, SWT.NO_FOCUS); + this.context = context; + createTableViewer(this); + } + + private void createTableViewer(final Composite parent) { + parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); + + // We must limit the size of the table otherwise the full list is + // loaded before the layout happens + // Composite listCmp = new Composite(parent, SWT.NO_FOCUS); + // GridData gd = new GridData(SWT.LEFT, SWT.FILL, false, true); + // gd.widthHint = COLUMN_WIDTH; + // listCmp.setLayoutData(gd); + // listCmp.setLayout(EclipseUiUtils.noSpaceGridLayout()); + // viewer = new TableViewer(listCmp, SWT.VIRTUAL | SWT.MULTI | + // SWT.V_SCROLL); + // Table table = viewer.getTable(); + // table.setLayoutData(EclipseUiUtils.fillAll()); + + viewer = new FsTableViewer(parent, SWT.MULTI); + Table table = viewer.configureDefaultSingleColumnTable(COLUMN_WIDTH); + + viewer.addSelectionChangedListener(new ISelectionChangedListener() { + + @Override + public void selectionChanged(SelectionChangedEvent event) { + IStructuredSelection selection = (IStructuredSelection) viewer.getSelection(); + if (selection.isEmpty()) + return; + Object obj = selection.getFirstElement(); + Path newSelected; + if (obj instanceof Path) + newSelected = (Path) obj; + else if (obj instanceof ParentDir) + newSelected = ((ParentDir) obj).getPath(); + else + return; + if (newSelected.equals(currSelected)) + return; + currSelected = newSelected; + setEdited(newSelected); + + } + }); + + table.addKeyListener(new KeyListener() { + private static final long serialVersionUID = -8083424284436715709L; + + @Override + public void keyReleased(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + IStructuredSelection selection = (IStructuredSelection) viewer.getSelection(); + Path selected = null; + if (!selection.isEmpty()) + selected = ((Path) selection.getFirstElement()); + if (e.keyCode == SWT.ARROW_RIGHT) { + if (!Files.isDirectory(selected)) + return; + if (selected != null) { + setEdited(selected); + browserCols.get(selected).setFocus(); + } + } else if (e.keyCode == SWT.ARROW_LEFT) { + if (context.equals(initialPath)) + return; + Path parent = context.getParent(); + if (parent == null) + return; + + setEdited(parent); + browserCols.get(parent).setFocus(); + } + } + }); + } + } +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FileIconNameLabelProvider.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FileIconNameLabelProvider.java new file mode 100644 index 000000000..d3fc1c903 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FileIconNameLabelProvider.java @@ -0,0 +1,84 @@ +package org.argeo.eclipse.ui.fs; + +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.swt.graphics.Image; + +/** Basic label provider with icon for NIO file viewers */ +public class FileIconNameLabelProvider extends ColumnLabelProvider { + private static final long serialVersionUID = 8187902187946523148L; + + private Image folderIcon; + private Image fileIcon; + + public FileIconNameLabelProvider() { + // if (!PlatformUI.isWorkbenchRunning()) { + folderIcon = ImageDescriptor.createFromFile(getClass(), "folder.png").createImage(); + fileIcon = ImageDescriptor.createFromFile(getClass(), "file.png").createImage(); + // } + } + + @Override + public void dispose() { + if (folderIcon != null) + folderIcon.dispose(); + if (fileIcon != null) + fileIcon.dispose(); + super.dispose(); + } + + @Override + public String getText(Object element) { + if (element instanceof Path) { + Path curr = ((Path) element); + Path name = curr.getFileName(); + if (name == null) + return "[No name]"; + else + return name.toString(); + } else if (element instanceof ParentDir) { + return ".."; + } + return null; + } + + @Override + public Image getImage(Object element) { + if (element instanceof Path) { + Path curr = ((Path) element); + if (Files.isDirectory(curr)) + // if (folderIcon != null) + return folderIcon; + // else + // return + // PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FOLDER); + // else if (fileIcon != null) + return fileIcon; + // else + // return + // PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FILE); + } else if (element instanceof ParentDir) { + return folderIcon; + } + return null; + } + + @Override + public String getToolTipText(Object element) { + if (element instanceof Path) { + Path curr = ((Path) element); + Path name = curr.getFileName(); + if (name == null) + return "[No name]"; + else + return name.toAbsolutePath().toString(); + } else if (element instanceof ParentDir) { + return ((ParentDir) element).getPath().toAbsolutePath().toString(); + } + return null; + } + +} \ No newline at end of file diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsTableViewer.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsTableViewer.java new file mode 100644 index 000000000..3b126e90b --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsTableViewer.java @@ -0,0 +1,141 @@ +package org.argeo.eclipse.ui.fs; + +import java.nio.file.Path; +import java.util.List; + +import org.argeo.eclipse.ui.ColumnDefinition; +import org.eclipse.jface.viewers.CellLabelProvider; +import org.eclipse.jface.viewers.ILazyContentProvider; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TableViewerColumn; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; + +/** + * Canonical implementation of a JFace table viewer to display the content of a + * file folder + */ +public class FsTableViewer extends TableViewer { + private static final long serialVersionUID = -5632407542678477234L; + + private boolean showHiddenItems = false; + private boolean folderFirst = true; + private boolean reverseOrder = false; + private String orderProperty = FsUiConstants.PROPERTY_NAME; + + private Path initialPath = null; + + public FsTableViewer(Composite parent, int style) { + super(parent, style | SWT.VIRTUAL); + } + + public Table configureDefaultSingleColumnTable(int tableWidthHint) { + + return configureDefaultSingleColumnTable(tableWidthHint, new FileIconNameLabelProvider()); + } + + public Table configureDefaultSingleColumnTable(int tableWidthHint, CellLabelProvider labelProvider) { + Table table = this.getTable(); + table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); + table.setLinesVisible(false); + table.setHeaderVisible(false); + // CmsUtils.markup(table); + // CmsUtils.style(table, MaintenanceStyles.BROWSER_COLUMN); + + TableViewerColumn column = new TableViewerColumn(this, SWT.NONE); + TableColumn tcol = column.getColumn(); + tcol.setWidth(tableWidthHint); + column.setLabelProvider(labelProvider); + this.setContentProvider(new MyLazyCP()); + return table; + } + + public Table configureDefaultTable(List columns) { + this.setContentProvider(new MyLazyCP()); + Table table = this.getTable(); + table.setLinesVisible(true); + table.setHeaderVisible(true); + // CmsUtils.markup(table); + // CmsUtils.style(table, MaintenanceStyles.BROWSER_COLUMN); + for (ColumnDefinition colDef : columns) { + TableViewerColumn column = new TableViewerColumn(this, SWT.NONE); + column.setLabelProvider(colDef.getLabelProvider()); + TableColumn tcol = column.getColumn(); + tcol.setResizable(true); + tcol.setText(colDef.getLabel()); + tcol.setWidth(colDef.getMinWidth()); + } + return table; + } + + public void setInput(Path dir, String filter) { + Path[] rows = FsUiUtils.getChildren(dir, filter, showHiddenItems, folderFirst, orderProperty, reverseOrder); + if (rows == null) { + this.setInput(null); + this.setItemCount(0); + return; + } + boolean isRoot; + try { + isRoot = dir.getRoot().equals(dir); + } catch (Exception e) { + // FIXME Workaround for JCR root node access + isRoot = dir.toString().equals("/"); + } + final Object[] res; + if (isRoot) + res = rows; + else if (initialPath != null && initialPath.equals(dir)) + res = rows; + else { + res = new Object[rows.length + 1]; + res[0] = new ParentDir(dir.getParent()); + for (int i = 1; i < res.length; i++) { + res[i] = rows[i - 1]; + } + } + this.setInput(res); + int length = res.length; + this.setItemCount(length); + this.refresh(); + } + + /** Directly displays bookmarks **/ + public void setPathsInput(Path... paths) { + this.setInput((Object[]) paths); + this.setItemCount(paths.length); + this.refresh(); + } + + /** + * A path which is to be considered as root (and thus provide no link to a + * parent directory) + */ + public void setInitialPath(Path initialPath) { + this.initialPath = initialPath; + } + + private class MyLazyCP implements ILazyContentProvider { + private static final long serialVersionUID = 9096550041395433128L; + private Object[] elements; + + public void dispose() { + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // IMPORTANT: don't forget this: an exception will be thrown if + // a selected object is not part of the results anymore. + viewer.setSelection(null); + this.elements = (Object[]) newInput; + } + + public void updateElement(int index) { + if (index < elements.length) + FsTableViewer.this.replace(elements[index], index); + } + } +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsTreeViewer.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsTreeViewer.java new file mode 100644 index 000000000..f55ead718 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsTreeViewer.java @@ -0,0 +1,144 @@ +package org.argeo.eclipse.ui.fs; + +import java.io.IOException; +import java.nio.file.DirectoryIteratorException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import org.argeo.eclipse.ui.ColumnDefinition; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.TreeViewerColumn; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeColumn; + +/** + * Canonical implementation of a JFace TreeViewer to display the content of a + * repository + */ +public class FsTreeViewer extends TreeViewer { + private static final long serialVersionUID = -5632407542678477234L; + + private boolean showHiddenItems = false; + private boolean showDirectoryFirst = true; + private String orderingProperty = FsUiConstants.PROPERTY_NAME; + + public FsTreeViewer(Composite parent, int style) { + super(parent, style | SWT.VIRTUAL); + } + + public Tree configureDefaultSingleColumnTable(int tableWidthHint) { + Tree tree = this.getTree(); + tree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); + tree.setLinesVisible(true); + tree.setHeaderVisible(false); +// CmsUtils.markup(tree); + + TreeViewerColumn column = new TreeViewerColumn(this, SWT.NONE); + TreeColumn tcol = column.getColumn(); + tcol.setWidth(tableWidthHint); + column.setLabelProvider(new FileIconNameLabelProvider()); + + this.setContentProvider(new MyCP()); + return tree; + } + + public Tree configureDefaultTable(List columns) { + this.setContentProvider(new MyCP()); + Tree tree = this.getTree(); + tree.setLinesVisible(true); + tree.setHeaderVisible(true); +// CmsUtils.markup(tree); +// CmsUtils.style(tree, MaintenanceStyles.BROWSER_COLUMN); + for (ColumnDefinition colDef : columns) { + TreeViewerColumn column = new TreeViewerColumn(this, SWT.NONE); + column.setLabelProvider(colDef.getLabelProvider()); + TreeColumn tcol = column.getColumn(); + tcol.setResizable(true); + tcol.setText(colDef.getLabel()); + tcol.setWidth(colDef.getMinWidth()); + } + return tree; + } + + public void setInput(Path dir, String filter) { + try (DirectoryStream stream = Files.newDirectoryStream(dir, filter)) { + // TODO make this lazy + List paths = new ArrayList<>(); + for (Path entry : stream) { + paths.add(entry); + } + Object[] rows = paths.toArray(new Object[0]); + this.setInput(rows); + // this.setItemCount(rows.length); + this.refresh(); + } catch (IOException | DirectoryIteratorException e) { + throw new FsUiException("Unable to filter " + dir + " children with filter " + filter, e); + } + } + + /** Directly displays bookmarks **/ + public void setPathsInput(Path... paths) { + this.setInput((Object[]) paths); + // this.setItemCount(paths.length); + this.refresh(); + } + + private class MyCP implements ITreeContentProvider { + private static final long serialVersionUID = 9096550041395433128L; + private Object[] elements; + + public void dispose() { + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // IMPORTANT: don't forget this: an exception will be thrown if + // a selected object is not part of the results anymore. + viewer.setSelection(null); + this.elements = (Object[]) newInput; + } + + @Override + public Object[] getElements(Object inputElement) { + return elements; + } + + @Override + public Object[] getChildren(Object parentElement) { + Path path = (Path) parentElement; + if (!Files.isDirectory(path)) + return null; + else + return FsUiUtils.getChildren(path, "*", showHiddenItems, showDirectoryFirst, orderingProperty, false); + } + + @Override + public Object getParent(Object element) { + Path path = (Path) element; + return path.getParent(); + } + + @Override + public boolean hasChildren(Object element) { + Path path = (Path) element; + try { + if (!Files.isDirectory(path)) + return false; + else + try (DirectoryStream children = Files.newDirectoryStream(path, "*")) { + return children.iterator().hasNext(); + } + } catch (IOException e) { + throw new FsUiException("Unable to check child existence on " + path, e); + } + } + + } +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiConstants.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiConstants.java new file mode 100644 index 000000000..2b51e71a2 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiConstants.java @@ -0,0 +1,11 @@ +package org.argeo.eclipse.ui.fs; + +/** Centralize constants used by the Nio FS UI parts */ +public interface FsUiConstants { + + // TODO use standard properties + String PROPERTY_NAME = "name"; + String PROPERTY_SIZE = "size"; + String PROPERTY_LAST_MODIFIED = "last-modified"; + String PROPERTY_TYPE = "type"; +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiException.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiException.java new file mode 100644 index 000000000..422b0e1ad --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiException.java @@ -0,0 +1,14 @@ +package org.argeo.eclipse.ui.fs; + +/** Files specific exception */ +public class FsUiException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public FsUiException(String message) { + super(message); + } + + public FsUiException(String message, Throwable e) { + super(message, e); + } +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiUtils.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiUtils.java new file mode 100644 index 000000000..956d96bb5 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiUtils.java @@ -0,0 +1,132 @@ +package org.argeo.eclipse.ui.fs; + +import java.io.IOException; +import java.nio.file.DirectoryIteratorException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** Centralise additional utilitary methods to manage Java7 NIO files */ +public class FsUiUtils { + + /** + * thanks to + * http://programming.guide/java/formatting-byte-size-to-human-readable-format.html + */ + public static String humanReadableByteCount(long bytes, boolean si) { + int unit = si ? 1000 : 1024; + if (bytes < unit) + return bytes + " B"; + int exp = (int) (Math.log(bytes) / Math.log(unit)); + String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + (si ? "" : "i"); + return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre); + } + + public static Path[] getChildren(Path parent, String filter, boolean showHiddenItems, boolean folderFirst, + String orderProperty, boolean reverseOrder) { + if (!Files.isDirectory(parent)) + return null; + List pairs = new ArrayList<>(); + try (DirectoryStream stream = Files.newDirectoryStream(parent, filter)) { + loop: for (Path entry : stream) { + if (!showHiddenItems) + if (Files.isHidden(entry)) + continue loop; + switch (orderProperty) { + case FsUiConstants.PROPERTY_SIZE: + if (folderFirst) + pairs.add(new LPair(entry, Files.size(entry), Files.isDirectory(entry))); + else + pairs.add(new LPair(entry, Files.size(entry))); + break; + case FsUiConstants.PROPERTY_LAST_MODIFIED: + if (folderFirst) + pairs.add(new LPair(entry, Files.getLastModifiedTime(entry).toMillis(), + Files.isDirectory(entry))); + else + pairs.add(new LPair(entry, Files.getLastModifiedTime(entry).toMillis())); + break; + case FsUiConstants.PROPERTY_NAME: + if (folderFirst) + pairs.add(new SPair(entry, entry.getFileName().toString(), Files.isDirectory(entry))); + else + pairs.add(new SPair(entry, entry.getFileName().toString())); + break; + default: + throw new FsUiException("Unable to prepare sort for property " + orderProperty); + } + } + Pair[] rows = pairs.toArray(new Pair[0]); + Arrays.sort(rows); + Path[] results = new Path[rows.length]; + if (reverseOrder) { + int j = rows.length - 1; + for (int i = 0; i < rows.length; i++) + results[i] = rows[j - i].p; + } else + for (int i = 0; i < rows.length; i++) + results[i] = rows[i].p; + return results; + } catch (IOException | DirectoryIteratorException e) { + throw new FsUiException("Unable to filter " + parent + " children with filter " + filter, e); + } + } + + static abstract class Pair implements Comparable { + Path p; + Boolean i; + }; + + static class LPair extends Pair { + long v; + + public LPair(Path path, long propValue) { + p = path; + v = propValue; + } + + public LPair(Path path, long propValue, boolean isDir) { + p = path; + v = propValue; + i = isDir; + } + + public int compareTo(Object o) { + if (i != null) { + Boolean j = ((LPair) o).i; + if (i.booleanValue() != j.booleanValue()) + return i.booleanValue() ? -1 : 1; + } + long u = ((LPair) o).v; + return v < u ? -1 : v == u ? 0 : 1; + } + }; + + static class SPair extends Pair { + String v; + + public SPair(Path path, String propValue) { + p = path; + v = propValue; + } + + public SPair(Path path, String propValue, boolean isDir) { + p = path; + v = propValue; + i = isDir; + } + + public int compareTo(Object o) { + if (i != null) { + Boolean j = ((SPair) o).i; + if (i.booleanValue() != j.booleanValue()) + return i.booleanValue() ? -1 : 1; + } + String u = ((SPair) o).v; + return v.compareTo(u); + } + }; +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/NioFileLabelProvider.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/NioFileLabelProvider.java new file mode 100644 index 000000000..2bb65eed0 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/NioFileLabelProvider.java @@ -0,0 +1,78 @@ +package org.argeo.eclipse.ui.fs; + +import java.io.IOException; +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.Date; + +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.eclipse.jface.viewers.ColumnLabelProvider; + +/** Expect a {@link Path} as input element */ +public class NioFileLabelProvider extends ColumnLabelProvider { + private final static FileTime EPOCH = FileTime.fromMillis(0); + private static final long serialVersionUID = 2160026425187796930L; + private final String propName; + private DateFormat dateFormat = new SimpleDateFormat("YYYY-MM-dd HH:mm"); + + // TODO use new formatting + // DateTimeFormatter formatter = + // DateTimeFormatter.ofLocalizedDateTime( FormatStyle.SHORT ) + // .withLocale( Locale.UK ) + // .withZone( ZoneId.systemDefault() ); + public NioFileLabelProvider(String propName) { + this.propName = propName; + } + + @Override + public String getText(Object element) { + try { + Path path; + if (element instanceof ParentDir) { +// switch (propName) { +// case FsUiConstants.PROPERTY_SIZE: +// return "-"; +// case FsUiConstants.PROPERTY_LAST_MODIFIED: +// return "-"; +// // return Files.getLastModifiedTime(((ParentDir) element).getPath()).toString(); +// case FsUiConstants.PROPERTY_TYPE: +// return "Folder"; +// } + path = ((ParentDir) element).getPath(); + } else + path = (Path) element; + switch (propName) { + case FsUiConstants.PROPERTY_SIZE: + if (Files.isDirectory(path)) + return "-"; + else + return FsUiUtils.humanReadableByteCount(Files.size(path), false); + case FsUiConstants.PROPERTY_LAST_MODIFIED: + if (Files.isDirectory(path)) + return "-"; + FileTime time = Files.getLastModifiedTime(path); + if (time.equals(EPOCH)) + return "-"; + else + return dateFormat.format(new Date(time.toMillis())); + case FsUiConstants.PROPERTY_TYPE: + if (Files.isDirectory(path)) + return "Folder"; + else { + String mimeType = Files.probeContentType(path); + if (EclipseUiUtils.isEmpty(mimeType)) + return "Unknown"; + else + return mimeType; + } + default: + throw new IllegalArgumentException("Unsupported property " + propName); + } + } catch (IOException ioe) { + throw new FsUiException("Cannot get property " + propName + " on " + element); + } + } +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/ParentDir.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/ParentDir.java new file mode 100644 index 000000000..6f09c2905 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/ParentDir.java @@ -0,0 +1,28 @@ +package org.argeo.eclipse.ui.fs; + +import java.nio.file.Path; + +/** A parent directory (..) reference. */ +public class ParentDir { + Path path; + + public ParentDir(Path path) { + super(); + this.path = path; + } + + public Path getPath() { + return path; + } + + @Override + public int hashCode() { + return path.hashCode(); + } + + @Override + public String toString() { + return "Parent dir " + path; + } + +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/SimpleFsBrowser.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/SimpleFsBrowser.java new file mode 100644 index 000000000..2e3d6b405 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/SimpleFsBrowser.java @@ -0,0 +1,211 @@ +package org.argeo.eclipse.ui.fs; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import org.argeo.api.cms.CmsLog; +import org.argeo.eclipse.ui.ColumnDefinition; +import org.argeo.eclipse.ui.EclipseUiUtils; +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.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Table; + +/** + * Experimental UI upon Java 7 nio files api: SashForm layout with bookmarks on + * the left hand side and a simple table on the right hand side. + */ +public class SimpleFsBrowser extends Composite { + private final static CmsLog log = CmsLog.getLog(SimpleFsBrowser.class); + private static final long serialVersionUID = -40347919096946585L; + + private Path currSelected; + private FsTableViewer bookmarksViewer; + private FsTableViewer directoryDisplayViewer; + + public SimpleFsBrowser(Composite parent, int style) { + super(parent, style); + createContent(this); + // parent.layout(true, true); + } + + public Viewer getViewer() { + return directoryDisplayViewer; + } + + private void createContent(Composite parent) { + parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); + + SashForm form = new SashForm(parent, SWT.HORIZONTAL); + Composite leftCmp = new Composite(form, SWT.NONE); + populateBookmarks(leftCmp); + + Composite rightCmp = new Composite(form, SWT.BORDER); + populateDisplay(rightCmp); + form.setLayoutData(EclipseUiUtils.fillAll()); + form.setWeights(new int[] { 1, 3 }); + } + + public void setInput(Path... paths) { + bookmarksViewer.setPathsInput(paths); + bookmarksViewer.getTable().getParent().layout(true, true); + } + + private void populateBookmarks(final Composite parent) { + // GridLayout layout = EclipseUiUtils.noSpaceGridLayout(); + // layout.verticalSpacing = 5; + parent.setLayout(new GridLayout()); + + ISelectionChangedListener selList = new MySelectionChangedListener(); + + appendTitle(parent, "My bookmarks"); + bookmarksViewer = new FsTableViewer(parent, SWT.MULTI | SWT.NO_SCROLL); + Table table = bookmarksViewer.configureDefaultSingleColumnTable(500); + GridData gd = EclipseUiUtils.fillWidth(); + gd.horizontalIndent = 10; + table.setLayoutData(gd); + bookmarksViewer.addSelectionChangedListener(selList); + + appendTitle(parent, "Jcr + File"); + + FsTableViewer jcrFilesViewers = new FsTableViewer(parent, SWT.MULTI | SWT.NO_SCROLL); + table = jcrFilesViewers.configureDefaultSingleColumnTable(500); + gd = EclipseUiUtils.fillWidth(); + gd.horizontalIndent = 10; + table.setLayoutData(gd); + jcrFilesViewers.addSelectionChangedListener(selList); + + // FileSystemProvider fsProvider = new JackrabbitMemoryFsProvider(); + // try { + // Path testPath = fsProvider.getPath(new URI("jcr+memory:/")); + // jcrFilesViewers.setPathsInput(testPath); + // } catch (URISyntaxException e) { + // // TODO Auto-generated catch block + // e.printStackTrace(); + // } + } + + private Label appendTitle(Composite parent, String value) { + Label titleLbl = new Label(parent, SWT.NONE); + titleLbl.setText(value); + titleLbl.setFont(EclipseUiUtils.getBoldFont(parent)); + GridData gd = EclipseUiUtils.fillWidth(); + gd.horizontalIndent = 5; + gd.verticalIndent = 5; + titleLbl.setLayoutData(gd); + return titleLbl; + } + + private class MySelectionChangedListener implements ISelectionChangedListener { + @Override + public void selectionChanged(SelectionChangedEvent event) { + IStructuredSelection selection = (IStructuredSelection) bookmarksViewer.getSelection(); + if (selection.isEmpty()) + return; + else { + Path newSelected = (Path) selection.getFirstElement(); + if (newSelected.equals(currSelected)) + return; + currSelected = newSelected; + directoryDisplayViewer.setInput(currSelected, "*"); + } + } + } + + private void populateDisplay(final Composite parent) { + parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); + directoryDisplayViewer = new FsTableViewer(parent, SWT.MULTI); + List colDefs = new ArrayList<>(); + colDefs.add(new ColumnDefinition(new FileIconNameLabelProvider(), "Name", 200)); + colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_SIZE), "Size", 100)); + colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_TYPE), "Type", 250)); + colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_LAST_MODIFIED), + "Last modified", 200)); + Table table = directoryDisplayViewer.configureDefaultTable(colDefs); + table.setLayoutData(EclipseUiUtils.fillAll()); + + table.addKeyListener(new KeyListener() { + private static final long serialVersionUID = -8083424284436715709L; + + @Override + public void keyReleased(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + log.debug("Key event received: " + e.keyCode); + IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection(); + Path selected = null; + if (!selection.isEmpty()) + selected = ((Path) selection.getFirstElement()); + if (e.keyCode == SWT.CR) { + if (!Files.isDirectory(selected)) + return; + if (selected != null) { + currSelected = selected; + directoryDisplayViewer.setInput(currSelected, "*"); + } + } else if (e.keyCode == SWT.BS) { + currSelected = currSelected.getParent(); + directoryDisplayViewer.setInput(currSelected, "*"); + directoryDisplayViewer.getTable().setFocus(); + } + } + }); + +// directoryDisplayViewer.addDoubleClickListener(new IDoubleClickListener() { +// @Override +// public void doubleClick(DoubleClickEvent event) { +// IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection(); +// Path selected = null; +// if (!selection.isEmpty()) { +// Object obj = selection.getFirstElement(); +// if (obj instanceof Path) +// selected = (Path) obj; +// else if (obj instanceof ParentDir) +// selected = ((ParentDir) obj).getPath(); +// } +// if (selected != null) { +// if (!Files.isDirectory(selected)) +// return; +// currSelected = selected; +// directoryDisplayViewer.setInput(currSelected, "*"); +// } +// } +// }); + + directoryDisplayViewer.addDoubleClickListener(new IDoubleClickListener() { + @Override + public void doubleClick(DoubleClickEvent event) { + IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection(); + Path selected = null; + if (!selection.isEmpty()) { + Object obj = selection.getFirstElement(); + if (obj instanceof Path) + selected = (Path) obj; + else if (obj instanceof ParentDir) + selected = ((ParentDir) obj).getPath(); + } + if (selected != null) { + if (!Files.isDirectory(selected)) + return; + currSelected = selected; + directoryDisplayViewer.setInput(currSelected, "*"); + } + } + }); + } +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/SimpleFsTreeBrowser.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/SimpleFsTreeBrowser.java new file mode 100644 index 000000000..401e5cb5e --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/SimpleFsTreeBrowser.java @@ -0,0 +1,128 @@ +package org.argeo.eclipse.ui.fs; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import org.argeo.api.cms.CmsLog; +import org.argeo.eclipse.ui.ColumnDefinition; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +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.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.Tree; + +/** A simple Java 7 nio files browser with a tree */ +public class SimpleFsTreeBrowser extends Composite { + private final static CmsLog log = CmsLog.getLog(SimpleFsTreeBrowser.class); + private static final long serialVersionUID = -40347919096946585L; + + private Path currSelected; + private FsTreeViewer treeViewer; + private FsTableViewer directoryDisplayViewer; + + public SimpleFsTreeBrowser(Composite parent, int style) { + super(parent, style); + createContent(this); + // parent.layout(true, true); + } + + private void createContent(Composite parent) { + parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); + SashForm form = new SashForm(parent, SWT.HORIZONTAL); + Composite child1 = new Composite(form, SWT.NONE); + populateTree(child1); + Composite child2 = new Composite(form, SWT.BORDER); + populateDisplay(child2); + form.setLayoutData(EclipseUiUtils.fillAll()); + form.setWeights(new int[] { 1, 3 }); + } + + public void setInput(Path... paths) { + treeViewer.setPathsInput(paths); + treeViewer.getControl().getParent().layout(true, true); + } + + private void populateTree(final Composite parent) { + // GridLayout layout = EclipseUiUtils.noSpaceGridLayout(); + // layout.verticalSpacing = 5; + parent.setLayout(new GridLayout()); + + ISelectionChangedListener selList = new MySelectionChangedListener(); + + treeViewer = new FsTreeViewer(parent, SWT.MULTI); + Tree tree = treeViewer.configureDefaultSingleColumnTable(500); + GridData gd = EclipseUiUtils.fillAll(); + // gd.horizontalIndent = 10; + tree.setLayoutData(gd); + treeViewer.addSelectionChangedListener(selList); + } + + private class MySelectionChangedListener implements ISelectionChangedListener { + @Override + public void selectionChanged(SelectionChangedEvent event) { + IStructuredSelection selection = (IStructuredSelection) treeViewer.getSelection(); + if (selection.isEmpty()) + return; + else { + Path newSelected = (Path) selection.getFirstElement(); + if (newSelected.equals(currSelected)) + return; + currSelected = newSelected; + if (Files.isDirectory(currSelected)) + directoryDisplayViewer.setInput(currSelected, "*"); + } + } + } + + private void populateDisplay(final Composite parent) { + parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); + directoryDisplayViewer = new FsTableViewer(parent, SWT.MULTI); + List colDefs = new ArrayList<>(); + colDefs.add(new ColumnDefinition(new FileIconNameLabelProvider(), "Name", 200, 200)); + colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_SIZE), "Size", 100, 100)); + colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_TYPE), "Type", 300, 300)); + colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_LAST_MODIFIED), + "Last modified", 100, 100)); + Table table = directoryDisplayViewer.configureDefaultTable(colDefs); + table.setLayoutData(EclipseUiUtils.fillAll()); + + table.addKeyListener(new KeyListener() { + private static final long serialVersionUID = -8083424284436715709L; + + @Override + public void keyReleased(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + log.debug("Key event received: " + e.keyCode); + IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection(); + Path selected = null; + if (!selection.isEmpty()) + selected = ((Path) selection.getFirstElement()); + if (e.keyCode == SWT.CR) { + if (!Files.isDirectory(selected)) + return; + if (selected != null) { + currSelected = selected; + directoryDisplayViewer.setInput(currSelected, "*"); + } + } else if (e.keyCode == SWT.BS) { + currSelected = currSelected.getParent(); + directoryDisplayViewer.setInput(currSelected, "*"); + directoryDisplayViewer.getTable().setFocus(); + } + } + }); + } +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/file.png b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/file.png new file mode 100644 index 000000000..ce2f2a8f4 Binary files /dev/null and b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/file.png differ diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/folder.png b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/folder.png new file mode 100644 index 000000000..c31f37e07 Binary files /dev/null and b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/folder.png differ diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/package-info.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/package-info.java new file mode 100644 index 000000000..d7f83c3e1 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/package-info.java @@ -0,0 +1,2 @@ +/** Generic SWT/JFace file system utilities. */ +package org.argeo.eclipse.ui.fs; \ No newline at end of file diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/package-info.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/package-info.java new file mode 100644 index 000000000..0d245db07 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/package-info.java @@ -0,0 +1,2 @@ +/** Generic SWT/JFace utilities. */ +package org.argeo.eclipse.ui; \ No newline at end of file diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/parts/LdifUsersTable.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/parts/LdifUsersTable.java new file mode 100644 index 000000000..57139056c --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/parts/LdifUsersTable.java @@ -0,0 +1,402 @@ +package org.argeo.eclipse.ui.parts; + +import java.util.ArrayList; +import java.util.List; + +import org.argeo.eclipse.ui.ColumnDefinition; +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.util.ViewerUtils; +import org.eclipse.jface.layout.TableColumnLayout; +import org.eclipse.jface.viewers.CheckboxTableViewer; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.jface.viewers.ColumnWeightData; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TableViewerColumn; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Link; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.swt.widgets.Text; +import org.osgi.service.useradmin.User; + +/** + * Generic composite that display a filter and a table viewer to display users + * (can also be groups) + * + * Warning: this class does not extends TableViewer. Use the + * getTableViewer method to access it. + * + */ +public abstract class LdifUsersTable extends Composite { + private static final long serialVersionUID = -7385959046279360420L; + + // Context + // private UserAdmin userAdmin; + + // Configuration + private List columnDefs = new ArrayList(); + private boolean hasFilter; + private boolean preventTableLayout = false; + private boolean hasSelectionColumn; + private int tableStyle; + + // Local UI Objects + private TableViewer usersViewer; + private Text filterTxt; + + /* EXPOSED METHODS */ + + /** + * @param parent + * @param style + */ + public LdifUsersTable(Composite parent, int style) { + super(parent, SWT.NO_FOCUS); + this.tableStyle = style; + } + + // TODO workaround the bug of the table layout in the Form + public LdifUsersTable(Composite parent, int style, boolean preventTableLayout) { + super(parent, SWT.NO_FOCUS); + this.tableStyle = style; + this.preventTableLayout = preventTableLayout; + } + + /** This must be called before the call to populate method */ + public void setColumnDefinitions(List columnDefinitions) { + this.columnDefs = columnDefinitions; + } + + /** + * + * @param addFilter + * choose to add a field to filter results or not + * @param addSelection + * choose to add a column to select some of the displayed results or + * not + */ + public void populate(boolean addFilter, boolean addSelection) { + // initialization + Composite parent = this; + hasFilter = addFilter; + hasSelectionColumn = addSelection; + + // Main Layout + GridLayout layout = EclipseUiUtils.noSpaceGridLayout(); + layout.verticalSpacing = 5; + this.setLayout(layout); + if (hasFilter) + createFilterPart(parent); + + Composite tableComp = new Composite(parent, SWT.NO_FOCUS); + tableComp.setLayoutData(EclipseUiUtils.fillAll()); + usersViewer = createTableViewer(tableComp); + usersViewer.setContentProvider(new UsersContentProvider()); + } + + /** + * + * @param showMore + * display static filters on creation + * @param addSelection + * choose to add a column to select some of the displayed results or + * not + */ + public void populateWithStaticFilters(boolean showMore, boolean addSelection) { + // initialization + Composite parent = this; + hasFilter = true; + hasSelectionColumn = addSelection; + + // Main Layout + GridLayout layout = EclipseUiUtils.noSpaceGridLayout(); + layout.verticalSpacing = 5; + this.setLayout(layout); + createStaticFilterPart(parent, showMore); + + Composite tableComp = new Composite(parent, SWT.NO_FOCUS); + tableComp.setLayoutData(EclipseUiUtils.fillAll()); + usersViewer = createTableViewer(tableComp); + usersViewer.setContentProvider(new UsersContentProvider()); + } + + /** Enable access to the selected users or groups */ + public List getSelectedUsers() { + if (hasSelectionColumn) { + Object[] elements = ((CheckboxTableViewer) usersViewer).getCheckedElements(); + + List result = new ArrayList(); + for (Object obj : elements) { + result.add((User) obj); + } + return result; + } else + throw new EclipseUiException( + "Unvalid request: no selection column " + "has been created for the current table"); + } + + /** Returns the User table viewer, typically to add doubleclick listener */ + public TableViewer getTableViewer() { + return usersViewer; + } + + /** + * Force the refresh of the underlying table using the current filter string if + * relevant + */ + public void refresh() { + String filter = hasFilter ? filterTxt.getText().trim() : null; + if ("".equals(filter)) + filter = null; + refreshFilteredList(filter); + } + + /** Effective repository request: caller must implement this method */ + abstract protected List listFilteredElements(String filter); + + // protected List listFilteredElements(String filter) { + // List users = new ArrayList(); + // try { + // Role[] roles = userAdmin.getRoles(filter); + // // Display all users and groups + // for (Role role : roles) + // users.add((User) role); + // } catch (InvalidSyntaxException e) { + // throw new EclipseUiException("Unable to get roles with filter: " + // + filter, e); + // } + // return users; + // } + + /* GENERIC COMPOSITE METHODS */ + @Override + public boolean setFocus() { + if (hasFilter) + return filterTxt.setFocus(); + else + return usersViewer.getTable().setFocus(); + } + + @Override + public void dispose() { + super.dispose(); + } + + /* LOCAL CLASSES AND METHODS */ + // Will be usefull to rather use a virtual table viewer + private void refreshFilteredList(String filter) { + List users = listFilteredElements(filter); + usersViewer.setInput(users.toArray()); + } + + private class UsersContentProvider implements IStructuredContentProvider { + private static final long serialVersionUID = 1L; + + public Object[] getElements(Object inputElement) { + return (Object[]) inputElement; + } + + public void dispose() { + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + } + + /* MANAGE FILTER */ + private void createFilterPart(Composite parent) { + // Text Area for the filter + filterTxt = new Text(parent, SWT.BORDER | SWT.SEARCH | SWT.ICON_SEARCH | SWT.ICON_CANCEL); + filterTxt.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false)); + filterTxt.addModifyListener(new ModifyListener() { + private static final long serialVersionUID = 1L; + + public void modifyText(ModifyEvent event) { + refreshFilteredList(filterTxt.getText()); + } + }); + } + + private void createStaticFilterPart(Composite parent, boolean showMore) { + Composite filterComp = new Composite(parent, SWT.NO_FOCUS); + filterComp.setLayout(new GridLayout(2, false)); + filterComp.setLayoutData(EclipseUiUtils.fillWidth()); + // generic search + filterTxt = new Text(filterComp, SWT.BORDER | SWT.SEARCH | SWT.ICON_SEARCH | SWT.ICON_CANCEL); + filterTxt.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false)); + // filterTxt.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | + // GridData.HORIZONTAL_ALIGN_FILL)); + filterTxt.addModifyListener(new ModifyListener() { + private static final long serialVersionUID = 1L; + + public void modifyText(ModifyEvent event) { + refreshFilteredList(filterTxt.getText()); + } + }); + + // add static filter abilities + Link moreLk = new Link(filterComp, SWT.NONE); + Composite staticFilterCmp = new Composite(filterComp, SWT.NO_FOCUS); + staticFilterCmp.setLayoutData(EclipseUiUtils.fillWidth(2)); + populateStaticFilters(staticFilterCmp); + + MoreLinkListener listener = new MoreLinkListener(moreLk, staticFilterCmp, showMore); + // initialise the layout + listener.refresh(); + moreLk.addSelectionListener(listener); + } + + /** Overwrite to add static filters */ + protected void populateStaticFilters(Composite staticFilterCmp) { + } + + // private void addMoreSL(final Link more) { + // more.addSelectionListener( } + + private class MoreLinkListener extends SelectionAdapter { + private static final long serialVersionUID = -524987616510893463L; + private boolean isShown; + private final Composite staticFilterCmp; + private final Link moreLk; + + public MoreLinkListener(Link moreLk, Composite staticFilterCmp, boolean isShown) { + this.moreLk = moreLk; + this.staticFilterCmp = staticFilterCmp; + this.isShown = isShown; + } + + @Override + public void widgetSelected(SelectionEvent e) { + isShown = !isShown; + refresh(); + } + + public void refresh() { + GridData gd = (GridData) staticFilterCmp.getLayoutData(); + if (isShown) { + moreLk.setText(" Less... "); + gd.heightHint = SWT.DEFAULT; + } else { + moreLk.setText(" More... "); + gd.heightHint = 0; + } + forceLayout(); + } + } + + private void forceLayout() { + LdifUsersTable.this.getParent().layout(true, true); + } + + private TableViewer createTableViewer(final Composite parent) { + + int style = tableStyle | SWT.H_SCROLL | SWT.V_SCROLL; + if (hasSelectionColumn) + style = style | SWT.CHECK; + Table table = new Table(parent, style); + TableColumnLayout layout = new TableColumnLayout(); + + // TODO the table layout does not works with the scrolled form + + if (preventTableLayout) { + parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); + table.setLayoutData(EclipseUiUtils.fillAll()); + } else + parent.setLayout(layout); + + TableViewer viewer; + if (hasSelectionColumn) + viewer = new CheckboxTableViewer(table); + else + viewer = new TableViewer(table); + table.setLinesVisible(true); + table.setHeaderVisible(true); + + TableViewerColumn column; + // int offset = 0; + if (hasSelectionColumn) { + // offset = 1; + column = ViewerUtils.createTableViewerColumn(viewer, "", SWT.NONE, 25); + column.setLabelProvider(new ColumnLabelProvider() { + private static final long serialVersionUID = 1L; + + @Override + public String getText(Object element) { + return null; + } + }); + layout.setColumnData(column.getColumn(), new ColumnWeightData(25, 25, false)); + + SelectionAdapter selectionAdapter = new SelectionAdapter() { + private static final long serialVersionUID = 1L; + + boolean allSelected = false; + + @Override + public void widgetSelected(SelectionEvent e) { + allSelected = !allSelected; + ((CheckboxTableViewer) usersViewer).setAllChecked(allSelected); + } + }; + column.getColumn().addSelectionListener(selectionAdapter); + } + + // NodeViewerComparator comparator = new NodeViewerComparator(); + // TODO enable the sort by click on the header + // int i = offset; + for (ColumnDefinition colDef : columnDefs) + createTableColumn(viewer, layout, colDef); + + // column = ViewerUtils.createTableViewerColumn(viewer, + // colDef.getHeaderLabel(), SWT.NONE, colDef.getColumnSize()); + // column.setLabelProvider(new CLProvider(colDef.getPropertyName())); + // column.getColumn().addSelectionListener( + // JcrUiUtils.getNodeSelectionAdapter(i, + // colDef.getPropertyType(), colDef.getPropertyName(), + // comparator, viewer)); + // i++; + // } + + // IMPORTANT: initialize comparator before setting it + // JcrColumnDefinition firstCol = colDefs.get(0); + // comparator.setColumn(firstCol.getPropertyType(), + // firstCol.getPropertyName()); + // viewer.setComparator(comparator); + + return viewer; + } + + /** Default creation of a column for a user table */ + private TableViewerColumn createTableColumn(TableViewer tableViewer, TableColumnLayout layout, + ColumnDefinition columnDef) { + + boolean resizable = true; + TableViewerColumn tvc = new TableViewerColumn(tableViewer, SWT.NONE); + TableColumn column = tvc.getColumn(); + + column.setText(columnDef.getLabel()); + column.setWidth(columnDef.getMinWidth()); + column.setResizable(resizable); + + ColumnLabelProvider lp = columnDef.getLabelProvider(); + // add a reference to the display to enable font management + // if (lp instanceof UserAdminAbstractLP) + // ((UserAdminAbstractLP) lp).setDisplay(tableViewer.getTable() + // .getDisplay()); + tvc.setLabelProvider(lp); + + layout.setColumnData(column, new ColumnWeightData(columnDef.getWeight(), columnDef.getMinWidth(), resizable)); + + return tvc; + } +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/parts/package-info.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/parts/package-info.java new file mode 100644 index 000000000..9e93b1106 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/parts/package-info.java @@ -0,0 +1,2 @@ +/** Generic SWT/JFace composites. */ +package org.argeo.eclipse.ui.parts; \ No newline at end of file diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/util/ViewerUtils.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/util/ViewerUtils.java new file mode 100644 index 000000000..8f4df1799 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/util/ViewerUtils.java @@ -0,0 +1,58 @@ +package org.argeo.eclipse.ui.util; + +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TableViewerColumn; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.TreeViewerColumn; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.swt.widgets.TreeColumn; + +/** + * Centralise useful methods to manage JFace Table, Tree and TreeColumn viewers. + */ +public class ViewerUtils { + + /** + * Creates a basic column for the given table. For the time being, we do not + * support movable columns. + */ + public static TableColumn createColumn(Table parent, String name, int style, int width) { + TableColumn result = new TableColumn(parent, style); + result.setText(name); + result.setWidth(width); + result.setResizable(true); + return result; + } + + /** + * Creates a TableViewerColumn for the given viewer. For the time being, we do + * not support movable columns. + */ + public static TableViewerColumn createTableViewerColumn(TableViewer parent, String name, int style, int width) { + TableViewerColumn tvc = new TableViewerColumn(parent, style); + TableColumn column = tvc.getColumn(); + column.setText(name); + column.setWidth(width); + column.setResizable(true); + return tvc; + } + + // public static TableViewerColumn createTableViewerColumn(TableViewer parent, + // Localized name, int style, int width) { + // return createTableViewerColumn(parent, name.lead(), style, width); + // } + + /** + * Creates a TreeViewerColumn for the given viewer. For the time being, we do + * not support movable columns. + */ + public static TreeViewerColumn createTreeViewerColumn(TreeViewer parent, String name, int style, int width) { + TreeViewerColumn tvc = new TreeViewerColumn(parent, style); + TreeColumn column = tvc.getColumn(); + column.setText(name); + column.setWidth(width); + column.setResizable(true); + return tvc; + } +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/util/package-info.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/util/package-info.java new file mode 100644 index 000000000..798d17482 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/util/package-info.java @@ -0,0 +1,2 @@ +/** Generic SWT/JFace JCR helpers. */ +package org.argeo.eclipse.ui.util; \ No newline at end of file diff --git a/swt/rap/org.argeo.cms.e4.rap/.classpath b/swt/rap/org.argeo.cms.e4.rap/.classpath new file mode 100644 index 000000000..e801ebfb4 --- /dev/null +++ b/swt/rap/org.argeo.cms.e4.rap/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/swt/rap/org.argeo.cms.e4.rap/.project b/swt/rap/org.argeo.cms.e4.rap/.project new file mode 100644 index 000000000..40c9e013f --- /dev/null +++ b/swt/rap/org.argeo.cms.e4.rap/.project @@ -0,0 +1,33 @@ + + + org.argeo.cms.e4.rap + + + + + + 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/swt/rap/org.argeo.cms.e4.rap/META-INF/.gitignore b/swt/rap/org.argeo.cms.e4.rap/META-INF/.gitignore new file mode 100644 index 000000000..4854a41b9 --- /dev/null +++ b/swt/rap/org.argeo.cms.e4.rap/META-INF/.gitignore @@ -0,0 +1 @@ +/MANIFEST.MF diff --git a/swt/rap/org.argeo.cms.e4.rap/OSGI-INF/cms-admin-rap.xml b/swt/rap/org.argeo.cms.e4.rap/OSGI-INF/cms-admin-rap.xml new file mode 100644 index 000000000..1f688baa6 --- /dev/null +++ b/swt/rap/org.argeo.cms.e4.rap/OSGI-INF/cms-admin-rap.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/swt/rap/org.argeo.cms.e4.rap/bnd.bnd b/swt/rap/org.argeo.cms.e4.rap/bnd.bnd new file mode 100644 index 000000000..5bbe4bc4b --- /dev/null +++ b/swt/rap/org.argeo.cms.e4.rap/bnd.bnd @@ -0,0 +1,9 @@ +Bundle-ActivationPolicy: lazy +Service-Component: OSGI-INF/cms-admin-rap.xml + +Import-Package: org.eclipse.swt,\ +org.eclipse.swt.graphics,\ +org.eclipse.e4.ui.workbench,\ +org.eclipse.rap.rwt.client,\ +org.eclipse.nebula.widgets.richtext;resolution:=optional,\ +* diff --git a/swt/rap/org.argeo.cms.e4.rap/build.properties b/swt/rap/org.argeo.cms.e4.rap/build.properties new file mode 100644 index 000000000..c58ea2178 --- /dev/null +++ b/swt/rap/org.argeo.cms.e4.rap/build.properties @@ -0,0 +1,5 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + OSGI-INF/ diff --git a/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/AbstractRapE4App.java b/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/AbstractRapE4App.java new file mode 100644 index 000000000..5fe22ae33 --- /dev/null +++ b/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/AbstractRapE4App.java @@ -0,0 +1,139 @@ +package org.argeo.cms.e4.rap; + +import java.util.HashMap; +import java.util.Map; + +import org.argeo.cms.swt.dialogs.CmsFeedback; +import org.eclipse.rap.e4.E4ApplicationConfig; +import org.eclipse.rap.rwt.application.Application; +import org.eclipse.rap.rwt.application.Application.OperationMode; +import org.eclipse.rap.rwt.application.ApplicationConfiguration; +import org.eclipse.rap.rwt.application.ExceptionHandler; +import org.eclipse.rap.rwt.client.WebClient; +import org.osgi.framework.BundleContext; + +/** Base class for CMS RAP applications. */ +public abstract class AbstractRapE4App implements ApplicationConfiguration { + private String e4Xmi; + private String path; + private String lifeCycleUri = "bundleclass://org.argeo.cms.e4.rap/org.argeo.cms.e4.rap.CmsLoginLifecycle"; + + private Map baseProperties = new HashMap(); + + private BundleContext bundleContext; + public final static String CONTEXT_NAME_PROPERTY = "contextName"; + private String contextName; + + /** + * To be overridden in order to add multiple entry points, directly or using + * {@link #addE4EntryPoint(Application, String, String, Map)}. + */ + protected void addEntryPoints(Application application) { + } + + public void configure(Application application) { + application.setExceptionHandler(new ExceptionHandler() { + + @Override + public void handleException(Throwable throwable) { + CmsFeedback.show("Unexpected RWT exception", throwable); + } + }); + + if (e4Xmi != null) {// backward compatibility + addE4EntryPoint(application, path, e4Xmi, getBaseProperties()); + } else { + addEntryPoints(application); + } + } + + protected Map getBaseProperties() { + return baseProperties; + } + +// protected void addEntryPoint(Application application, E4ApplicationConfig config, Map properties) { +// CmsE4EntryPointFactory entryPointFactory = new CmsE4EntryPointFactory(config); +// application.addEntryPoint(path, entryPointFactory, properties); +// application.setOperationMode(OperationMode.SWT_COMPATIBILITY); +// } + + protected void addE4EntryPoint(Application application, String path, String e4Xmi, Map properties) { + E4ApplicationConfig config = createE4ApplicationConfig(e4Xmi); + CmsE4EntryPointFactory entryPointFactory = new CmsE4EntryPointFactory(config); + application.addEntryPoint(path, entryPointFactory, properties); + application.setOperationMode(OperationMode.SWT_COMPATIBILITY); + } + + /** + * To be overridden for further configuration. + * + * @see E4ApplicationConfig + */ + protected E4ApplicationConfig createE4ApplicationConfig(String e4Xmi) { + return new E4ApplicationConfig(e4Xmi, lifeCycleUri, null, null, false, true, true); + } + + @Deprecated + public void setPageTitle(String pageTitle) { + if (pageTitle != null) + baseProperties.put(WebClient.PAGE_TITLE, pageTitle); + } + + /** Returns a new map used to customise and entry point. */ + public Map customise(String pageTitle) { + Map custom = new HashMap<>(getBaseProperties()); + if (pageTitle != null) + custom.put(WebClient.PAGE_TITLE, pageTitle); + return custom; + } + + @Deprecated + public void setE4Xmi(String e4Xmi) { + this.e4Xmi = e4Xmi; + } + + @Deprecated + public void setPath(String path) { + this.path = path; + } + + public void setLifeCycleUri(String lifeCycleUri) { + this.lifeCycleUri = lifeCycleUri; + } + + protected BundleContext getBundleContext() { + return bundleContext; + } + + protected void setBundleContext(BundleContext bundleContext) { + this.bundleContext = bundleContext; + } + + public String getContextName() { + return contextName; + } + + public void setContextName(String contextName) { + this.contextName = contextName; + } + + public void init(BundleContext bundleContext, Map properties) { + this.bundleContext = bundleContext; + for (String key : properties.keySet()) { + Object value = properties.get(key); + if (value != null) + baseProperties.put(key, value.toString()); + } + + if (properties.containsKey(CONTEXT_NAME_PROPERTY)) { + assert properties.get(CONTEXT_NAME_PROPERTY) != null; + contextName = properties.get(CONTEXT_NAME_PROPERTY).toString(); + } else { + contextName = ""; + } + } + + public void destroy(Map properties) { + + } +} diff --git a/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4AdminApp.java b/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4AdminApp.java new file mode 100644 index 000000000..66be1e8e9 --- /dev/null +++ b/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4AdminApp.java @@ -0,0 +1,17 @@ +package org.argeo.cms.e4.rap; + +import org.eclipse.rap.rwt.application.Application; + +/** + * Access to canonical views of the core CMS concepts, useful for devleopers and + * operators. + */ +public class CmsE4AdminApp extends AbstractRapE4App { + @Override + protected void addEntryPoints(Application application) { + addE4EntryPoint(application, "/devops", "org.argeo.cms.e4/e4xmi/cms-devops.e4xmi", + customise("Argeo CMS DevOps")); + addE4EntryPoint(application, "/ego", "org.argeo.cms.e4/e4xmi/cms-ego.e4xmi", customise("Argeo CMS Ego")); + } + +} diff --git a/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4EntryPointFactory.java b/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4EntryPointFactory.java new file mode 100644 index 000000000..a5a32348e --- /dev/null +++ b/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4EntryPointFactory.java @@ -0,0 +1,76 @@ +package org.argeo.cms.e4.rap; + +import java.security.PrivilegedAction; + +import javax.security.auth.Subject; + +import org.eclipse.rap.e4.E4ApplicationConfig; +import org.eclipse.rap.e4.E4EntryPointFactory; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.rap.rwt.application.EntryPoint; +import org.eclipse.rap.rwt.client.service.JavaScriptExecutor; + +public class CmsE4EntryPointFactory extends E4EntryPointFactory { + public final static String DEFAULT_LIFECYCLE_URI = "bundleclass://org.argeo.cms.e4.rap/org.argeo.cms.e4.rap.CmsLoginLifecycle"; + + public CmsE4EntryPointFactory(E4ApplicationConfig config) { + super(config); + } + + public CmsE4EntryPointFactory(String e4Xmi, String lifeCycleUri) { + super(defaultConfig(e4Xmi, lifeCycleUri)); + } + + public CmsE4EntryPointFactory(String e4Xmi) { + this(e4Xmi, DEFAULT_LIFECYCLE_URI); + } + + public static E4ApplicationConfig defaultConfig(String e4Xmi, String lifeCycleUri) { + E4ApplicationConfig config = new E4ApplicationConfig(e4Xmi, lifeCycleUri, null, null, false, true, true); + return config; + } + + @Override + public EntryPoint create() { + EntryPoint ep = createEntryPoint(); + EntryPoint authEp = new EntryPoint() { + + @Override + public int createUI() { + Subject subject = new Subject(); + return Subject.doAs(subject, new PrivilegedAction() { + + @Override + public Integer run() { + // SPNEGO + // HttpServletRequest request = RWT.getRequest(); + // String authorization = request.getHeader(HEADER_AUTHORIZATION); + // if (authorization == null || !authorization.startsWith("Negotiate")) { + // HttpServletResponse response = RWT.getResponse(); + // response.setStatus(401); + // response.setHeader(HEADER_WWW_AUTHENTICATE, "Negotiate"); + // response.setDateHeader("Date", System.currentTimeMillis()); + // response.setDateHeader("Expires", System.currentTimeMillis() + (24 * 60 * 60 + // * 1000)); + // response.setHeader("Accept-Ranges", "bytes"); + // response.setHeader("Connection", "Keep-Alive"); + // response.setHeader("Keep-Alive", "timeout=5, max=97"); + // // response.setContentType("text/html; charset=UTF-8"); + // } + + JavaScriptExecutor jsExecutor = RWT.getClient().getService(JavaScriptExecutor.class); + Integer exitCode = ep.createUI(); + jsExecutor.execute("location.reload()"); + return exitCode; + } + + }); + } + }; + return authEp; + } + + protected EntryPoint createEntryPoint() { + return super.create(); + } +} diff --git a/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsLoginLifecycle.java b/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsLoginLifecycle.java new file mode 100644 index 000000000..471cdeca5 --- /dev/null +++ b/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsLoginLifecycle.java @@ -0,0 +1,183 @@ +package org.argeo.cms.e4.rap; + +import java.security.AccessController; +import java.util.UUID; + +import javax.security.auth.Subject; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; + +import org.argeo.api.cms.CmsAuth; +import org.argeo.api.cms.CmsLog; +import org.argeo.api.cms.ux.CmsImageManager; +import org.argeo.api.cms.ux.CmsView; +import org.argeo.api.cms.ux.UxContext; +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.swt.SimpleSwtUxContext; +import org.argeo.cms.swt.auth.CmsLoginShell; +import org.argeo.cms.swt.dialogs.CmsFeedback; +import org.argeo.cms.ui.util.SimpleImageManager; +import org.eclipse.e4.core.services.events.IEventBroker; +import org.eclipse.e4.ui.workbench.UIEvents; +import org.eclipse.e4.ui.workbench.lifecycle.PostContextCreate; +import org.eclipse.e4.ui.workbench.lifecycle.PreSave; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.rap.rwt.client.service.BrowserNavigation; +import org.eclipse.rap.rwt.client.service.BrowserNavigationEvent; +import org.eclipse.rap.rwt.client.service.BrowserNavigationListener; +import org.eclipse.swt.widgets.Display; +import org.osgi.service.event.Event; +import org.osgi.service.event.EventHandler; + +@SuppressWarnings("restriction") +public class CmsLoginLifecycle implements CmsView { + private final static CmsLog log = CmsLog.getLog(CmsLoginLifecycle.class); + + private UxContext uxContext; + private CmsImageManager imageManager; + + private LoginContext loginContext; + private BrowserNavigation browserNavigation; + + private String state = null; + private String uid; + + @PostContextCreate + boolean login(final IEventBroker eventBroker) { + uid = UUID.randomUUID().toString(); + browserNavigation = RWT.getClient().getService(BrowserNavigation.class); + if (browserNavigation != null) + browserNavigation.addBrowserNavigationListener(new BrowserNavigationListener() { + private static final long serialVersionUID = -3668136623771902865L; + + @Override + public void navigated(BrowserNavigationEvent event) { + state = event.getState(); + if (uxContext != null)// is logged in + stateChanged(); + } + }); + + Subject subject = Subject.getSubject(AccessController.getContext()); + Display display = Display.getCurrent(); +// UiContext.setData(CmsView.KEY, this); + // FIXME get CMS context + CmsLoginShell loginShell = new CmsLoginShell(this, null); + CmsSwtUtils.registerCmsView(loginShell.getShell(), this); + loginShell.setSubject(subject); + try { + // try pre-auth + loginContext = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER, subject, loginShell); + loginContext.login(); + } catch (LoginException e) { + loginShell.createUi(); + loginShell.open(); + + while (!loginShell.getShell().isDisposed()) { + if (!display.readAndDispatch()) + display.sleep(); + } + } + if (CurrentUser.getUsername(getSubject()) == null) + return false; + uxContext = new SimpleSwtUxContext(); + imageManager = new SimpleImageManager(); + + eventBroker.subscribe(UIEvents.UILifeCycle.APP_STARTUP_COMPLETE, new EventHandler() { + @Override + public void handleEvent(Event event) { + startupComplete(); + eventBroker.unsubscribe(this); + } + }); + + // lcs.changeApplicationLocale(Locale.FRENCH); + return true; + } + + @PreSave + void destroy() { + // logout(); + } + + @Override + public UxContext getUxContext() { + return uxContext; + } + + @Override + public void navigateTo(String state) { + browserNavigation.pushState(state, state); + } + + @Override + public void authChange(LoginContext loginContext) { + if (loginContext == null) + throw new IllegalArgumentException("Login context cannot be null"); + // logout previous login context + // if (this.loginContext != null) + // try { + // this.loginContext.logout(); + // } catch (LoginException e1) { + // System.err.println("Could not log out: " + e1); + // } + this.loginContext = loginContext; + } + + @Override + public void logout() { + if (loginContext == null) + throw new IllegalStateException("Login context should not be null"); + try { + CurrentUser.logoutCmsSession(loginContext.getSubject()); + loginContext.logout(); + } catch (LoginException e) { + throw new IllegalStateException("Cannot log out", e); + } + } + + @Override + public void exception(Throwable e) { + String msg = "Unexpected exception in Eclipse 4 RAP"; + log.error(msg, e); + CmsFeedback.show(msg, e); + } + + @Override + public CmsImageManager getImageManager() { + return imageManager; + } + + protected Subject getSubject() { + return loginContext.getSubject(); + } + + @Override + public boolean isAnonymous() { + return CurrentUser.isAnonymous(getSubject()); + } + + @Override + public String getUid() { + return uid; + } + + // CALLBACKS + protected void startupComplete() { + } + + protected void stateChanged() { + + } + + // GETTERS + protected BrowserNavigation getBrowserNavigation() { + return browserNavigation; + } + + protected String getState() { + return state; + } + +} diff --git a/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/SimpleRapE4App.java b/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/SimpleRapE4App.java new file mode 100644 index 000000000..1bca333c9 --- /dev/null +++ b/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/SimpleRapE4App.java @@ -0,0 +1,32 @@ +package org.argeo.cms.e4.rap; + +import java.util.Enumeration; + +import org.apache.commons.io.FilenameUtils; +import org.argeo.api.cms.CmsLog; +import org.eclipse.rap.rwt.application.Application; +import org.osgi.framework.Bundle; + +/** Simple RAP app which loads all e4xmi files. */ +public class SimpleRapE4App extends AbstractRapE4App { + private final static CmsLog log = CmsLog.getLog(SimpleRapE4App.class); + + private String baseE4xmi = "/e4xmi"; + + @Override + protected void addEntryPoints(Application application) { + Bundle bundle = getBundleContext().getBundle(); + Enumeration paths = bundle.getEntryPaths(baseE4xmi); + while (paths.hasMoreElements()) { + String p = paths.nextElement(); + if (p.endsWith(".e4xmi")) { + String e4xmiPath = bundle.getSymbolicName() + '/' + p; + String name = '/' + FilenameUtils.removeExtension(FilenameUtils.getName(p)); + addE4EntryPoint(application, name, e4xmiPath, getBaseProperties()); + if (log.isDebugEnabled()) + log.debug("Registered " + e4xmiPath + " as " + getContextName() + name); + } + } + } + +} diff --git a/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/package-info.java b/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/package-info.java new file mode 100644 index 000000000..1122f1922 --- /dev/null +++ b/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/package-info.java @@ -0,0 +1,2 @@ +/** Eclipse 4 RAP specific extensions. */ +package org.argeo.cms.e4.rap; \ No newline at end of file diff --git a/swt/rap/org.argeo.cms.swt.rap.cli/.classpath b/swt/rap/org.argeo.cms.swt.rap.cli/.classpath new file mode 100644 index 000000000..81fe078c2 --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap.cli/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/swt/rap/org.argeo.cms.swt.rap.cli/.project b/swt/rap/org.argeo.cms.swt.rap.cli/.project new file mode 100644 index 000000000..7b3af9dc9 --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap.cli/.project @@ -0,0 +1,28 @@ + + + org.argeo.cms.swt.rap.cli + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/swt/rap/org.argeo.cms.swt.rap.cli/bnd.bnd b/swt/rap/org.argeo.cms.swt.rap.cli/bnd.bnd new file mode 100644 index 000000000..4dce79bc7 --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap.cli/bnd.bnd @@ -0,0 +1,8 @@ +Import-Package: \ +org.eclipse.jetty.util.component;version="[9.4,12)";resolution:=optional,\ +org.eclipse.jetty.http;version="[9.4,12)";resolution:=optional,\ +org.eclipse.jetty.io;version="[9.4,12)";resolution:=optional,\ +org.eclipse.jetty.security;version="[9.4,12)";resolution:=optional,\ +org.eclipse.jetty.server.handler;version="[9.4,12)";resolution:=optional,\ +org.eclipse.jetty.*;version="[9.4,12)";resolution:=optional,\ +* \ No newline at end of file diff --git a/swt/rap/org.argeo.cms.swt.rap.cli/build.properties b/swt/rap/org.argeo.cms.swt.rap.cli/build.properties new file mode 100644 index 000000000..34d2e4d2d --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap.cli/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/swt/rap/org.argeo.cms.swt.rap.cli/src/org/argeo/cms/swt/rap/cli/CmsRapCli.java b/swt/rap/org.argeo.cms.swt.rap.cli/src/org/argeo/cms/swt/rap/cli/CmsRapCli.java new file mode 100644 index 000000000..8d0fff4f3 --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap.cli/src/org/argeo/cms/swt/rap/cli/CmsRapCli.java @@ -0,0 +1,123 @@ +package org.argeo.cms.swt.rap.cli; + +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.argeo.api.acr.spi.ProvidedRepository; +import org.argeo.api.cli.CommandsCli; +import org.argeo.api.cli.DescribedCommand; +import org.argeo.api.cms.CmsApp; +import org.argeo.api.cms.CmsContext; +import org.argeo.cms.runtime.StaticCms; +import org.argeo.cms.swt.app.CmsUserApp; +import org.argeo.cms.web.CmsWebApp; +import org.argeo.util.register.Component; +import org.argeo.util.register.ComponentRegister; +import org.eclipse.rap.rwt.application.ApplicationConfiguration; + +public class CmsRapCli extends CommandsCli { + + public CmsRapCli(String commandName) { + super(commandName); + addCommand("user", new Launch()); + } + + @Override + public String getDescription() { + return "Argeo CMS utilities."; + } + + public static void main(String[] args) { + mainImpl(new CmsRapCli("web"), args); + } + + static class Launch implements DescribedCommand { + private Option dataOption; + private Option uiOption; + + @Override + public Options getOptions() { + Options options = new Options(); + dataOption = Option.builder().longOpt("data").hasArg().required() + .desc("path to the writable data area (mandatory)").build(); + uiOption = Option.builder().longOpt("ui").desc("open a user interface").build(); + options.addOption(dataOption); + options.addOption(uiOption); + return options; + } + + @Override + public String apply(List args) { + CommandLine cl = toCommandLine(args); + String dataPath = cl.getOptionValue(dataOption); + boolean ui = cl.hasOption(uiOption); + + Path instancePath = Paths.get(dataPath); + System.setProperty("osgi.instance.area", instancePath.toUri().toString()); + + StaticCms staticCms = new StaticCms() { + @Override + protected void addComponents(ComponentRegister register) { + if (ui) { + CmsUserApp cmsApp = new CmsUserApp(); + Component cmsAppC = new Component.Builder<>(cmsApp) // + .addType(CmsApp.class) // + .addType(CmsUserApp.class) // + .addDependency(register.getSingleton(CmsContext.class), cmsApp::setCmsContext, null) // + .addDependency(register.getSingleton(ProvidedRepository.class), + cmsApp::setContentRepository, null) // + .build(register); + + CmsWebApp cmsWebApp = new CmsWebApp(); + Component cmsWebAppC = new Component.Builder<>(cmsWebApp) // + .addType(ApplicationConfiguration.class) // + .addDependency(cmsAppC.getType(CmsApp.class), cmsWebApp::setCmsApp, null) // + .build(register); + + RwtRunner rwtRunner = new RwtRunner(); + Component rwtRunnerC = new Component.Builder<>(rwtRunner) // + .addActivation(rwtRunner::init) // + .addDeactivation(rwtRunner::destroy) // + .addType(RwtRunner.class) // + .addDependency(cmsWebAppC.getType(ApplicationConfiguration.class), + rwtRunner::setApplicationConfiguration, null) // + .build(register); + } + } + + }; + Runtime.getRuntime().addShutdownHook(new Thread(() -> staticCms.stop(), "Static CMS Shutdown")); + staticCms.start(); + + long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime(); + System.out.println("Static CMS available in " + jvmUptime + " ms."); + + if (ui) { + try { + // open browser in app mode + Thread.sleep(2000);// wait for RWT to be ready + Runtime.getRuntime().exec("google-chrome --app=http://localhost:" + + staticCms.getComponentRegister().getObject(RwtRunner.class).getEffectivePort() + "/data"); + } catch (InterruptedException | IOException e) { + e.printStackTrace(); + } + } + + staticCms.waitForStop(); + + return null; + } + + @Override + public String getDescription() { + return "Launch a static CMS."; + } + + } +} diff --git a/swt/rap/org.argeo.cms.swt.rap.cli/src/org/argeo/cms/swt/rap/cli/RwtRunner.java b/swt/rap/org.argeo.cms.swt.rap.cli/src/org/argeo/cms/swt/rap/cli/RwtRunner.java new file mode 100644 index 000000000..6b678a83c --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap.cli/src/org/argeo/cms/swt/rap/cli/RwtRunner.java @@ -0,0 +1,142 @@ +package org.argeo.cms.swt.rap.cli; + +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; + +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; + +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.servlet.DefaultServlet; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.rap.rwt.application.AbstractEntryPoint; +import org.eclipse.rap.rwt.application.ApplicationConfiguration; +import org.eclipse.rap.rwt.application.ApplicationRunner; +import org.eclipse.rap.rwt.engine.RWTServlet; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; + +/** A minimal RWT runner based on embedded Jetty. */ +public class RwtRunner { + + private final Server server; + private final ServerConnector serverConnector; + private Path tempDir; + + private ApplicationConfiguration applicationConfiguration; + + public RwtRunner() { + server = new Server(new QueuedThreadPool(10, 1)); + serverConnector = new ServerConnector(server); + serverConnector.setPort(0); + server.setConnectors(new Connector[] { serverConnector }); + } + + protected Control createUi(Composite parent, Object context) { + return new Label(parent, 0); + } + + public void init() { + Objects.requireNonNull(applicationConfiguration); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/"); + server.setHandler(context); + + String entryPoint = "data"; + + // rwt-resources requires a file system + try { + tempDir = Files.createTempDirectory("argeo-rwtRunner"); + context.setBaseResource(Resource.newResource(tempDir.resolve("www").toString())); + } catch (IOException e) { + throw new IllegalStateException("Cannot create temporary directory", e); + } + context.addEventListener(new ServletContextListener() { + ApplicationRunner applicationRunner; + + @Override + public void contextInitialized(ServletContextEvent sce) { + applicationRunner = new ApplicationRunner(applicationConfiguration, sce.getServletContext()); + applicationRunner.start(); + } + + @Override + public void contextDestroyed(ServletContextEvent sce) { + applicationRunner.stop(); + } + }); + + context.addServlet(new ServletHolder(new RWTServlet()), "/" + entryPoint); + + // Required to serve rwt-resources. It is important that this is last. + ServletHolder holderPwd = new ServletHolder("default", DefaultServlet.class); + context.addServlet(holderPwd, "/"); + + try { + server.start(); + } catch (Exception e) { + throw new IllegalStateException("Cannot start Jetty server", e); + } + Runtime.getRuntime().addShutdownHook(new Thread(() -> destroy(), "Jetty shutdown")); + + long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime(); + System.out.println("RWT App available in " + jvmUptime + " ms, on port " + getEffectivePort()); + } + + public void destroy() { + try { + serverConnector.close(); + server.stop(); + // TODO delete temp dir + } catch (Exception e) { + e.printStackTrace(); + } + } + + public Integer getEffectivePort() { + return serverConnector.getLocalPort(); + } + + public void waitFor() throws InterruptedException { + server.join(); + } + + public void setApplicationConfiguration(ApplicationConfiguration applicationConfiguration) { + this.applicationConfiguration = applicationConfiguration; + } + + public static void main(String[] args) throws Exception { + RwtRunner rwtRunner = new RwtRunner(); + + String entryPoint = "app"; + ApplicationConfiguration applicationConfiguration = (application) -> application.addEntryPoint("/" + entryPoint, + () -> new AbstractEntryPoint() { + private static final long serialVersionUID = 5678385921969090733L; + + @Override + protected void createContents(Composite parent) { + Label label = new Label(parent, 0); + label.setText("Hello world!"); + } + }, null); + + rwtRunner.setApplicationConfiguration(applicationConfiguration); + rwtRunner.init(); + + // open browser in app mode + Thread.sleep(2000);// wait for RWT to be ready + Runtime.getRuntime().exec("google-chrome --app=http://localhost:" + rwtRunner.getEffectivePort() + "/app"); + + rwtRunner.waitFor(); + } +} diff --git a/swt/rap/org.argeo.cms.swt.rap/.classpath b/swt/rap/org.argeo.cms.swt.rap/.classpath new file mode 100644 index 000000000..e801ebfb4 --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/swt/rap/org.argeo.cms.swt.rap/.project b/swt/rap/org.argeo.cms.swt.rap/.project new file mode 100644 index 000000000..630160402 --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap/.project @@ -0,0 +1,33 @@ + + + org.argeo.cms.ui.rap + + + + + + 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/swt/rap/org.argeo.cms.swt.rap/META-INF/.gitignore b/swt/rap/org.argeo.cms.swt.rap/META-INF/.gitignore new file mode 100644 index 000000000..4854a41b9 --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap/META-INF/.gitignore @@ -0,0 +1 @@ +/MANIFEST.MF diff --git a/swt/rap/org.argeo.cms.swt.rap/OSGI-INF/cmsWebAppFactory.xml b/swt/rap/org.argeo.cms.swt.rap/OSGI-INF/cmsWebAppFactory.xml new file mode 100644 index 000000000..aa7e0adca --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap/OSGI-INF/cmsWebAppFactory.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/swt/rap/org.argeo.cms.swt.rap/bnd.bnd b/swt/rap/org.argeo.cms.swt.rap/bnd.bnd new file mode 100644 index 000000000..db2ac0beb --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap/bnd.bnd @@ -0,0 +1,12 @@ +Import-Package:\ +org.argeo.api.acr,\ +org.eclipse.swt,\ +org.argeo.eclipse.ui,\ +javax.jcr.nodetype,\ +javax.jcr.security,\ +org.eclipse.swt.graphics,\ +javax.servlet.*;version="[3,5)",\ +* + +Service-Component: OSGI-INF/cmsWebAppFactory.xml + diff --git a/swt/rap/org.argeo.cms.swt.rap/build.properties b/swt/rap/org.argeo.cms.swt.rap/build.properties new file mode 100644 index 000000000..2416e522a --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap/build.properties @@ -0,0 +1,7 @@ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + OSGI-INF/cmsWebAppFactory.xml +source.. = src/ +additional.bundles = org.argeo.ext.slf4j,\ + org.slf4j.api diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/AppUi.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/AppUi.java new file mode 100644 index 000000000..01ebb237e --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/AppUi.java @@ -0,0 +1,268 @@ +package org.argeo.cms.ui.script; + +import java.util.HashMap; +import java.util.Map; + +import javax.jcr.Node; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.script.Invocable; +import javax.script.ScriptException; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.swt.Selected; +import org.argeo.cms.ui.CmsUiProvider; +import org.argeo.cms.ui.util.CmsPane; +import org.argeo.cms.web.SimpleErgonomics; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.rap.rwt.application.Application; +import org.eclipse.rap.rwt.application.EntryPoint; +import org.eclipse.rap.rwt.application.EntryPointFactory; +import org.eclipse.rap.rwt.client.WebClient; +import org.eclipse.rap.rwt.client.service.JavaScriptExecutor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionEvent; +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.osgi.framework.BundleContext; + +public class AppUi implements CmsUiProvider, Branding { + private final CmsScriptApp app; + + private CmsUiProvider ui; + private String createUi; + private Object impl; + private String script; + // private Branding branding = new Branding(); + + private EntryPointFactory factory; + + // Branding + private String themeId; + private String additionalHeaders; + private String bodyHtml; + private String pageTitle; + private String pageOverflow; + private String favicon; + + public AppUi(CmsScriptApp app) { + this.app = app; + } + + public AppUi(CmsScriptApp app, String scriptPath) { + this.app = app; + this.ui = new ScriptUi((BundleContext) app.getScriptEngine().get(CmsScriptRwtApplication.BC), + app.getScriptEngine(), scriptPath); + } + + public AppUi(CmsScriptApp app, CmsUiProvider uiProvider) { + this.app = app; + this.ui = uiProvider; + } + + public AppUi(CmsScriptApp app, EntryPointFactory factory) { + this.app = app; + this.factory = factory; + } + + public void apply(Repository repository, Application application, Branding appBranding, String path) { + Map factoryProperties = new HashMap<>(); + if (appBranding != null) + appBranding.applyBranding(factoryProperties); + applyBranding(factoryProperties); + if (factory != null) { + application.addEntryPoint("/" + path, factory, factoryProperties); + } else { + EntryPointFactory entryPointFactory = new EntryPointFactory() { + @Override + public EntryPoint create() { + SimpleErgonomics ergonomics = new SimpleErgonomics(repository, CmsConstants.SYS_WORKSPACE, + "/home/root/argeo:keyring", AppUi.this, factoryProperties); +// CmsUiProvider header = app.getHeader(); +// if (header != null) +// ergonomics.setHeader(header); + app.applySides(ergonomics); + Integer headerHeight = app.getHeaderHeight(); + if (headerHeight != null) + ergonomics.setHeaderHeight(headerHeight); + return ergonomics; + } + }; + application.addEntryPoint("/" + path, entryPointFactory, factoryProperties); + } + } + + public void setUi(CmsUiProvider uiProvider) { + this.ui = uiProvider; + } + + public void applyBranding(Map properties) { + if (themeId != null) + properties.put(WebClient.THEME_ID, themeId); + if (additionalHeaders != null) + properties.put(WebClient.HEAD_HTML, additionalHeaders); + if (bodyHtml != null) + properties.put(WebClient.BODY_HTML, bodyHtml); + if (pageTitle != null) + properties.put(WebClient.PAGE_TITLE, pageTitle); + if (pageOverflow != null) + properties.put(WebClient.PAGE_OVERFLOW, pageOverflow); + if (favicon != null) + properties.put(WebClient.FAVICON, favicon); + } + + // public Branding getBranding() { + // return branding; + // } + + @Override + public Control createUi(Composite parent, Node context) throws RepositoryException { + CmsPane cmsPane = new CmsPane(parent, SWT.NONE); + + if (false) { + // QA + CmsSwtUtils.style(cmsPane.getQaArea(), "qa"); + Button reload = new Button(cmsPane.getQaArea(), SWT.FLAT); + CmsSwtUtils.style(reload, "qa"); + reload.setText("Reload"); + reload.addSelectionListener(new Selected() { + private static final long serialVersionUID = 1L; + + @Override + public void widgetSelected(SelectionEvent e) { + new Thread() { + @Override + public void run() { + app.reload(); + } + }.start(); + RWT.getClient().getService(JavaScriptExecutor.class) + .execute("setTimeout('location.reload()',1000)"); + } + }); + + // Support + CmsSwtUtils.style(cmsPane.getSupportArea(), "support"); + Label msg = new Label(cmsPane.getSupportArea(), SWT.NONE); + CmsSwtUtils.style(msg, "support"); + msg.setText("UNSUPPORTED DEVELOPMENT VERSION"); + } + + if (ui != null) { + ui.createUi(cmsPane.getMainArea(), context); + } + if (createUi != null) { + Invocable invocable = (Invocable) app.getScriptEngine(); + try { + invocable.invokeFunction(createUi, cmsPane.getMainArea(), context); + + } catch (NoSuchMethodException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (ScriptException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + if (impl != null) { + Invocable invocable = (Invocable) app.getScriptEngine(); + try { + invocable.invokeMethod(impl, "createUi", cmsPane.getMainArea(), context); + + } catch (NoSuchMethodException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (ScriptException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + // Invocable invocable = (Invocable) app.getScriptEngine(); + // try { + // invocable.invokeMethod(AppUi.this, "initUi", parent, context); + // + // } catch (NoSuchMethodException e) { + // // TODO Auto-generated catch block + // e.printStackTrace(); + // } catch (ScriptException e) { + // // TODO Auto-generated catch block + // e.printStackTrace(); + // } + + return null; + } + + public void setCreateUi(String createUi) { + this.createUi = createUi; + } + + public void setImpl(Object impl) { + this.impl = impl; + } + + public Object getImpl() { + return impl; + } + + public String getScript() { + return script; + } + + public void setScript(String script) { + this.script = script; + } + + // Branding + public String getThemeId() { + return themeId; + } + + public void setThemeId(String themeId) { + this.themeId = themeId; + } + + public String getAdditionalHeaders() { + return additionalHeaders; + } + + public void setAdditionalHeaders(String additionalHeaders) { + this.additionalHeaders = additionalHeaders; + } + + public String getBodyHtml() { + return bodyHtml; + } + + public void setBodyHtml(String bodyHtml) { + this.bodyHtml = bodyHtml; + } + + public String getPageTitle() { + return pageTitle; + } + + public void setPageTitle(String pageTitle) { + this.pageTitle = pageTitle; + } + + public String getPageOverflow() { + return pageOverflow; + } + + public void setPageOverflow(String pageOverflow) { + this.pageOverflow = pageOverflow; + } + + public String getFavicon() { + return favicon; + } + + public void setFavicon(String favicon) { + this.favicon = favicon; + } + +} diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/Branding.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/Branding.java new file mode 100644 index 000000000..f72338ef7 --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/Branding.java @@ -0,0 +1,20 @@ +package org.argeo.cms.ui.script; + +import java.util.Map; + +public interface Branding { + public void applyBranding(Map properties); + + public String getThemeId(); + + public String getAdditionalHeaders(); + + public String getBodyHtml(); + + public String getPageTitle(); + + public String getPageOverflow(); + + public String getFavicon(); + +} diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/CmsScriptApp.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/CmsScriptApp.java new file mode 100644 index 000000000..6b3a670d7 --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/CmsScriptApp.java @@ -0,0 +1,421 @@ +package org.argeo.cms.ui.script; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.PropertyIterator; +import javax.jcr.PropertyType; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.script.ScriptEngine; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.argeo.api.cms.CmsLog; +import org.argeo.api.cms.ux.CmsTheme; +import org.argeo.cms.swt.CmsException; +import org.argeo.cms.ui.CmsUiConstants; +import org.argeo.cms.ui.CmsUiProvider; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.argeo.cms.web.BundleResourceLoader; +import org.argeo.cms.web.SimpleErgonomics; +import org.argeo.cms.web.WebThemeUtils; +import org.eclipse.rap.rwt.application.Application; +import org.eclipse.rap.rwt.application.Application.OperationMode; +import org.eclipse.rap.rwt.application.ApplicationConfiguration; +import org.eclipse.rap.rwt.application.ExceptionHandler; +import org.eclipse.rap.rwt.client.WebClient; +import org.eclipse.rap.rwt.service.ResourceLoader; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.http.HttpContext; +import org.osgi.service.http.HttpService; +import org.osgi.service.http.NamespaceException; + +public class CmsScriptApp implements Branding { + public final static String CONTEXT_NAME = "contextName"; + + ServiceRegistration appConfigReg; + + private ScriptEngine scriptEngine; + + private final static CmsLog log = CmsLog.getLog(CmsScriptApp.class); + + private String webPath; + private String repo = "(cn=node)"; + + // private Branding branding = new Branding(); + private CmsTheme theme; + + private List resources = new ArrayList<>(); + + private Map ui = new HashMap<>(); + + private CmsUiProvider header; + private Integer headerHeight = null; + private CmsUiProvider lead; + private CmsUiProvider end; + private CmsUiProvider footer; + + // Branding + private String themeId; + private String additionalHeaders; + private String bodyHtml; + private String pageTitle; + private String pageOverflow; + private String favicon; + + public CmsScriptApp(ScriptEngine scriptEngine) { + super(); + this.scriptEngine = scriptEngine; + } + + public void apply(BundleContext bundleContext, Repository repository, Application application) { + BundleResourceLoader bundleRL = new BundleResourceLoader(bundleContext.getBundle()); + + application.setOperationMode(OperationMode.SWT_COMPATIBILITY); + // application.setOperationMode(OperationMode.JEE_COMPATIBILITY); + + application.setExceptionHandler(new CmsExceptionHandler()); + + // loading animated gif + application.addResource(CmsUiConstants.LOADING_IMAGE, createResourceLoader(CmsUiConstants.LOADING_IMAGE)); + // empty image + application.addResource(CmsUiConstants.NO_IMAGE, createResourceLoader(CmsUiConstants.NO_IMAGE)); + + for (String resource : resources) { + application.addResource(resource, bundleRL); + if (log.isTraceEnabled()) + log.trace("Resource " + resource); + } + + if (theme != null) { + WebThemeUtils.apply(application, theme); + String themeHeaders = theme.getHtmlHeaders(); + if (themeHeaders != null) { + if (additionalHeaders == null) + additionalHeaders = themeHeaders; + else + additionalHeaders = themeHeaders + "\n" + additionalHeaders; + } + themeId = theme.getThemeId(); + } + + // client JavaScript + Bundle appBundle = bundleRL.getBundle(); + BundleContext bc = appBundle.getBundleContext(); + HttpService httpService = bc.getService(bc.getServiceReference(HttpService.class)); + HttpContext httpContext = new BundleHttpContext(bc); + Enumeration themeResources = appBundle.findEntries("/js/", "*", true); + if (themeResources != null) + bundleResources: while (themeResources.hasMoreElements()) { + try { + String name = themeResources.nextElement().getPath(); + if (name.endsWith("/")) + continue bundleResources; + String alias = "/" + getWebPath() + name; + + httpService.registerResources(alias, name, httpContext); + if (log.isDebugEnabled()) + log.debug("Mapped " + name + " to alias " + alias); + + } catch (NamespaceException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + // App UIs + for (String appUiName : ui.keySet()) { + AppUi appUi = ui.get(appUiName); + appUi.apply(repository, application, this, appUiName); + + } + + } + + public void applySides(SimpleErgonomics simpleErgonomics) { + simpleErgonomics.setHeader(header); + simpleErgonomics.setLead(lead); + simpleErgonomics.setEnd(end); + simpleErgonomics.setFooter(footer); + } + + public void register(BundleContext bundleContext, ApplicationConfiguration appConfig) { + Hashtable props = new Hashtable<>(); + props.put(CONTEXT_NAME, webPath); + appConfigReg = bundleContext.registerService(ApplicationConfiguration.class, appConfig, props); + } + + public void reload() { + BundleContext bundleContext = appConfigReg.getReference().getBundle().getBundleContext(); + ApplicationConfiguration appConfig = bundleContext.getService(appConfigReg.getReference()); + appConfigReg.unregister(); + register(bundleContext, appConfig); + + // BundleContext bundleContext = (BundleContext) + // getScriptEngine().get("bundleContext"); + // try { + // Bundle bundle = bundleContext.getBundle(); + // bundle.stop(); + // bundle.start(); + // } catch (BundleException e) { + // // TODO Auto-generated catch block + // e.printStackTrace(); + // } + } + + private static ResourceLoader createResourceLoader(final String resourceName) { + return new ResourceLoader() { + public InputStream getResourceAsStream(String resourceName) throws IOException { + return getClass().getClassLoader().getResourceAsStream(resourceName); + } + }; + } + + public List getResources() { + return resources; + } + + public AppUi newUi(String name) { + if (ui.containsKey(name)) + throw new IllegalArgumentException("There is already an UI named " + name); + AppUi appUi = new AppUi(this); + // appUi.setApp(this); + ui.put(name, appUi); + return appUi; + } + + public void addUi(String name, AppUi appUi) { + if (ui.containsKey(name)) + throw new IllegalArgumentException("There is already an UI named " + name); + // appUi.setApp(this); + ui.put(name, appUi); + } + + public void applyBranding(Map properties) { + if (themeId != null) + properties.put(WebClient.THEME_ID, themeId); + if (additionalHeaders != null) + properties.put(WebClient.HEAD_HTML, additionalHeaders); + if (bodyHtml != null) + properties.put(WebClient.BODY_HTML, bodyHtml); + if (pageTitle != null) + properties.put(WebClient.PAGE_TITLE, pageTitle); + if (pageOverflow != null) + properties.put(WebClient.PAGE_OVERFLOW, pageOverflow); + if (favicon != null) + properties.put(WebClient.FAVICON, favicon); + } + + class CmsExceptionHandler implements ExceptionHandler { + + @Override + public void handleException(Throwable throwable) { + // TODO be smarter + CmsUiUtils.getCmsView().exception(throwable); + } + + } + + // public Branding getBranding() { + // return branding; + // } + + ScriptEngine getScriptEngine() { + return scriptEngine; + } + + public static String toJson(Node node) { + try { + StringBuilder sb = new StringBuilder(); + sb.append('{'); + PropertyIterator pit = node.getProperties(); + int count = 0; + while (pit.hasNext()) { + Property p = pit.nextProperty(); + int type = p.getType(); + if (type == PropertyType.REFERENCE || type == PropertyType.WEAKREFERENCE || type == PropertyType.PATH) { + Node ref = p.getNode(); + if (count != 0) + sb.append(','); + // TODO limit depth? + sb.append(toJson(ref)); + count++; + } else if (!p.isMultiple()) { + if (count != 0) + sb.append(','); + sb.append('\"').append(p.getName()).append("\":\"").append(p.getString()).append('\"'); + count++; + } + } + sb.append('}'); + return sb.toString(); + } catch (RepositoryException e) { + throw new CmsException("Cannot convert " + node + " to JSON", e); + } + } + + public void fromJson(Node node, String json) { + // TODO + } + + public CmsTheme getTheme() { + return theme; + } + + public void setTheme(CmsTheme theme) { + this.theme = theme; + } + + public String getWebPath() { + return webPath; + } + + public void setWebPath(String context) { + this.webPath = context; + } + + public String getRepo() { + return repo; + } + + public void setRepo(String repo) { + this.repo = repo; + } + + public Map getUi() { + return ui; + } + + public void setUi(Map ui) { + this.ui = ui; + } + + // Branding + public String getThemeId() { + return themeId; + } + + public void setThemeId(String themeId) { + this.themeId = themeId; + } + + public String getAdditionalHeaders() { + return additionalHeaders; + } + + public void setAdditionalHeaders(String additionalHeaders) { + this.additionalHeaders = additionalHeaders; + } + + public String getBodyHtml() { + return bodyHtml; + } + + public void setBodyHtml(String bodyHtml) { + this.bodyHtml = bodyHtml; + } + + public String getPageTitle() { + return pageTitle; + } + + public void setPageTitle(String pageTitle) { + this.pageTitle = pageTitle; + } + + public String getPageOverflow() { + return pageOverflow; + } + + public void setPageOverflow(String pageOverflow) { + this.pageOverflow = pageOverflow; + } + + public String getFavicon() { + return favicon; + } + + public void setFavicon(String favicon) { + this.favicon = favicon; + } + + public CmsUiProvider getHeader() { + return header; + } + + public void setHeader(CmsUiProvider header) { + this.header = header; + } + + public Integer getHeaderHeight() { + return headerHeight; + } + + public void setHeaderHeight(Integer headerHeight) { + this.headerHeight = headerHeight; + } + + public CmsUiProvider getLead() { + return lead; + } + + public void setLead(CmsUiProvider lead) { + this.lead = lead; + } + + public CmsUiProvider getEnd() { + return end; + } + + public void setEnd(CmsUiProvider end) { + this.end = end; + } + + public CmsUiProvider getFooter() { + return footer; + } + + public void setFooter(CmsUiProvider footer) { + this.footer = footer; + } + + static class BundleHttpContext implements HttpContext { + private BundleContext bundleContext; + + public BundleHttpContext(BundleContext bundleContext) { + super(); + this.bundleContext = bundleContext; + } + + @Override + public boolean handleSecurity(HttpServletRequest request, HttpServletResponse response) throws IOException { + // TODO Auto-generated method stub + return true; + } + + @Override + public URL getResource(String name) { + + return bundleContext.getBundle().getEntry(name); + } + + @Override + public String getMimeType(String name) { + return null; + } + + } + +} diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/CmsScriptRwtApplication.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/CmsScriptRwtApplication.java new file mode 100644 index 000000000..9879fb019 --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/CmsScriptRwtApplication.java @@ -0,0 +1,120 @@ +package org.argeo.cms.ui.script; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URL; + +import javax.jcr.Repository; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; + +import org.argeo.api.cms.CmsLog; +import org.argeo.cms.swt.CmsException; +import org.eclipse.rap.rwt.application.Application; +import org.eclipse.rap.rwt.application.ApplicationConfiguration; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleException; +import org.osgi.framework.wiring.BundleWiring; + +public class CmsScriptRwtApplication implements ApplicationConfiguration { + public final static String APP = "APP"; + public final static String BC = "BC"; + + private final CmsLog log = CmsLog.getLog(CmsScriptRwtApplication.class); + + BundleContext bundleContext; + Repository repository; + + ScriptEngine engine; + + public void init(BundleContext bundleContext) { + this.bundleContext = bundleContext; + ClassLoader bundleCl = bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader(); + ClassLoader originalCcl = Thread.currentThread().getContextClassLoader(); + try { +// Thread.currentThread().setContextClassLoader(bundleCl);// GraalVM needs it to be before creating manager +// ScriptEngineManager scriptEngineManager = new ScriptEngineManager(bundleCl); +// engine = scriptEngineManager.getEngineByName("JavaScript"); +// if (engine == null) {// Nashorn +// Thread.currentThread().setContextClassLoader(originalCcl); +// scriptEngineManager = new ScriptEngineManager(); +// Thread.currentThread().setContextClassLoader(bundleCl); +// engine = scriptEngineManager.getEngineByName("JavaScript"); +// } + engine = loadScriptEngine(originalCcl, bundleCl); + + // Load script + URL appUrl = bundleContext.getBundle().getEntry("cms/app.js"); + // System.out.println("Loading " + appUrl); + // System.out.println("Loading " + appUrl.getHost()); + // System.out.println("Loading " + appUrl.getPath()); + + CmsScriptApp app = new CmsScriptApp(engine); + engine.put(APP, app); + engine.put(BC, bundleContext); + try (Reader reader = new InputStreamReader(appUrl.openStream())) { + engine.eval(reader); + } catch (IOException | ScriptException e) { + throw new CmsException("Cannot execute " + appUrl, e); + } + + if (log.isDebugEnabled()) + log.debug("CMS script app initialized from " + appUrl); + + } catch (Exception e) { + e.printStackTrace(); + } finally { + Thread.currentThread().setContextClassLoader(originalCcl); + } + } + + public void destroy(BundleContext bundleContext) { + engine = null; + } + + @Override + public void configure(Application application) { + load(application); + } + + void load(Application application) { + CmsScriptApp app = getApp(); + app.apply(bundleContext, repository, application); + if (log.isDebugEnabled()) + log.debug("CMS script app loaded to " + app.getWebPath()); + } + + CmsScriptApp getApp() { + if (engine == null) + throw new IllegalStateException("CMS script app is not initialized"); + return (CmsScriptApp) engine.get(APP); + } + + void update() { + + try { + bundleContext.getBundle().update(); + } catch (BundleException e) { + e.printStackTrace(); + } + } + + public void setRepository(Repository repository) { + this.repository = repository; + } + + private static ScriptEngine loadScriptEngine(ClassLoader originalCcl, ClassLoader bundleCl) { + Thread.currentThread().setContextClassLoader(bundleCl);// GraalVM needs it to be before creating manager + ScriptEngineManager scriptEngineManager = new ScriptEngineManager(bundleCl); + ScriptEngine engine = scriptEngineManager.getEngineByName("JavaScript"); + if (engine == null) {// Nashorn + Thread.currentThread().setContextClassLoader(originalCcl); + scriptEngineManager = new ScriptEngineManager(); + Thread.currentThread().setContextClassLoader(bundleCl); + engine = scriptEngineManager.getEngineByName("JavaScript"); + } + return engine; + } +} diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/ScriptAppActivator.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/ScriptAppActivator.java new file mode 100644 index 000000000..a55095371 --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/ScriptAppActivator.java @@ -0,0 +1,45 @@ +package org.argeo.cms.ui.script; + +import javax.jcr.Repository; + +import org.argeo.api.cms.CmsLog; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; + +public class ScriptAppActivator implements BundleActivator { + private final static CmsLog log = CmsLog.getLog(ScriptAppActivator.class); + + @Override + public void start(BundleContext context) throws Exception { + try { + CmsScriptRwtApplication appConfig = new CmsScriptRwtApplication(); + appConfig.init(context); + CmsScriptApp app = appConfig.getApp(); + ServiceTracker repoSt = new ServiceTracker(context, + FrameworkUtil.createFilter("(&" + app.getRepo() + "(objectClass=javax.jcr.Repository))"), null) { + + @Override + public Repository addingService(ServiceReference reference) { + Repository repository = super.addingService(reference); + appConfig.setRepository(repository); + CmsScriptApp app = appConfig.getApp(); + app.register(context, appConfig); + return repository; + } + + }; + repoSt.open(); + } catch (Exception e) { + log.error("Cannot initialise script bundle " + context.getBundle().getSymbolicName(), e); + throw e; + } + } + + @Override + public void stop(BundleContext context) throws Exception { + } + +} diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/ScriptUi.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/ScriptUi.java new file mode 100644 index 000000000..0c870e1e8 --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/ScriptUi.java @@ -0,0 +1,115 @@ +package org.argeo.cms.ui.script; + +import java.net.URL; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.script.Invocable; +import javax.script.ScriptEngine; +import javax.script.ScriptException; + +import org.argeo.api.cms.CmsLog; +import org.argeo.cms.ui.CmsUiProvider; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.osgi.framework.BundleContext; + +class ScriptUi implements CmsUiProvider { + private final static CmsLog log = CmsLog.getLog(ScriptUi.class); + + private boolean development = true; + private ScriptEngine scriptEngine; + + private URL appUrl; + // private BundleContext bundleContext; + // private String path; + + // private Bindings bindings; + // private String script; + + public ScriptUi(BundleContext bundleContext,ScriptEngine scriptEngine, String path) { + this.scriptEngine = scriptEngine; +//// ScriptEngineManager scriptEngineManager = new ScriptEngineManager(); +// ClassLoader bundleCl = bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader(); +// ClassLoader originalCcl = Thread.currentThread().getContextClassLoader(); +// try { +//// Thread.currentThread().setContextClassLoader(bundleCl); +//// scriptEngine = scriptEngineManager.getEngineByName("JavaScript"); +//// scriptEngine.put(CmsScriptRwtApplication.BC, bundleContext); +// scriptEngine = CmsScriptRwtApplication.loadScriptEngine(originalCcl, bundleCl); +// +// } catch (Exception e) { +// e.printStackTrace(); +// } finally { +// Thread.currentThread().setContextClassLoader(originalCcl); +// } + this.appUrl = bundleContext.getBundle().getEntry(path); + load(); + } + + private void load() { +// try (Reader reader = new InputStreamReader(appUrl.openStream())) { +// scriptEngine.eval(reader); +// } catch (IOException | ScriptException e) { +// log.warn("Cannot execute " + appUrl, e); +// } + + try { + scriptEngine.eval("load('" + appUrl + "')"); + } catch (ScriptException e) { + log.warn("Cannot execute " + appUrl, e); + } + + } + + // public ScriptUiProvider(ScriptEngine scriptEngine, String script) throws + // ScriptException { + // super(); + // this.scriptEngine = scriptEngine; + // this.script = script; + // bindings = scriptEngine.createBindings(); + // scriptEngine.eval(script, bindings); + // } + + @Override + public Control createUi(Composite parent, Node context) throws RepositoryException { + long begin = System.currentTimeMillis(); + // if (bindings == null) { + // bindings = scriptEngine.createBindings(); + // try { + // scriptEngine.eval(script, bindings); + // } catch (ScriptException e) { + // log.warn("Cannot evaluate script", e); + // } + // } + // Bindings bindings = scriptEngine.createBindings(); + // bindings.put("parent", parent); + // bindings.put("context", context); + // URL appUrl = bundleContext.getBundle().getEntry(path); + // try (Reader reader = new InputStreamReader(appUrl.openStream())) { + // scriptEngine.eval(reader,bindings); + // } catch (IOException | ScriptException e) { + // log.warn("Cannot execute " + appUrl, e); + // } + + if (development) + load(); + + Invocable invocable = (Invocable) scriptEngine; + try { + invocable.invokeFunction("createUi", parent, context); + } catch (NoSuchMethodException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (ScriptException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + long duration = System.currentTimeMillis() - begin; + if (log.isTraceEnabled()) + log.trace(appUrl + " UI in " + duration + " ms"); + return null; + } + +} diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/cms.js b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/cms.js new file mode 100644 index 000000000..be9618dcb --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/cms.js @@ -0,0 +1,90 @@ +// CMS +var ScrolledPage = Java.type('org.argeo.cms.ui.widgets.ScrolledPage'); + +var CmsScriptApp = Java.type('org.argeo.cms.ui.script.CmsScriptApp'); +var AppUi = Java.type('org.argeo.cms.ui.script.AppUi'); +var Theme = Java.type('org.argeo.cms.ui.script.Theme'); +var ScriptUi = Java.type('org.argeo.cms.ui.script.ScriptUi'); +var CmsUtils = Java.type('org.argeo.cms.ui.util.CmsUiUtils'); +var SimpleCmsHeader = Java.type('org.argeo.cms.ui.util.SimpleCmsHeader'); +var CmsLink = Java.type('org.argeo.cms.ui.util.CmsLink'); +var MenuLink = Java.type('org.argeo.cms.ui.util.MenuLink'); +var UserMenuLink = Java.type('org.argeo.cms.ui.util.UserMenuLink'); + +// SWT +var SWT = Java.type('org.eclipse.swt.SWT'); +var Composite = Java.type('org.eclipse.swt.widgets.Composite'); +var Label = Java.type('org.eclipse.swt.widgets.Label'); +var Button = Java.type('org.eclipse.swt.widgets.Button'); +var Text = Java.type('org.eclipse.swt.widgets.Text'); +var Browser = Java.type('org.eclipse.swt.browser.Browser'); + +var FillLayout = Java.type('org.eclipse.swt.layout.FillLayout'); +var GridLayout = Java.type('org.eclipse.swt.layout.GridLayout'); +var RowLayout = Java.type('org.eclipse.swt.layout.RowLayout'); +var FormLayout = Java.type('org.eclipse.swt.layout.FormLayout'); +var GridData = Java.type('org.eclipse.swt.layout.GridData'); + +function loadNode(node) { + var json = CmsScriptApp.toJson(node) + var fromJson = JSON.parse(json) + return fromJson +} + +function newArea(parent, style, layout) { + var control = new Composite(parent, SWT.NONE) + control.setLayout(layout) + CmsUtils.style(control, style) + return control +} + +function newLabel(parent, style, text) { + var control = new Label(parent, SWT.WRAP) + control.setText(text) + CmsUtils.style(control, style) + CmsUtils.markup(control) + return control +} + +function newButton(parent, style, text) { + var control = new Button(parent, SWT.FLAT) + control.setText(text) + CmsUtils.style(control, style) + CmsUtils.markup(control) + return control +} + +function newFormLabel(parent, style, text) { + return newLabel(parent, style, '' + text + '') +} + +function newText(parent, style, msg) { + var control = new Text(parent, SWT.NONE) + control.setMessage(msg) + CmsUtils.style(control, style) + return control +} + +function newScrolledPage(parent) { + var scrolled = new ScrolledPage(parent, SWT.NONE) + scrolled.setLayoutData(CmsUtils.fillAll()) + scrolled.setLayout(CmsUtils.noSpaceGridLayout()) + var page = new Composite(scrolled, SWT.NONE) + page.setLayout(CmsUtils.noSpaceGridLayout()) + page.setBackgroundMode(SWT.INHERIT_NONE) + return page +} + +function gridData(control) { + var gridData = new GridData() + control.setLayoutData(gridData) + return gridData +} + +function gridData(control, hAlign, vAlign) { + var gridData = new GridData(hAlign, vAlign, false, false) + control.setLayoutData(gridData) + return gridData +} + +// print(__FILE__, __LINE__, __DIR__) diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/package-info.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/package-info.java new file mode 100644 index 000000000..7440596ab --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/package-info.java @@ -0,0 +1,2 @@ +/** Argeo CMS user interface scripting. */ +package org.argeo.cms.ui.script; \ No newline at end of file diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/AbstractCmsEntryPoint.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/AbstractCmsEntryPoint.java new file mode 100644 index 000000000..f3269f2c4 --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/AbstractCmsEntryPoint.java @@ -0,0 +1,398 @@ +package org.argeo.cms.web; + +import static org.argeo.util.directory.ldap.SharedSecret.X_SHARED_SECRET; + +import java.io.IOException; +import java.security.PrivilegedAction; +import java.util.HashMap; +import java.util.Map; + +import javax.jcr.Node; +import javax.jcr.PathNotFoundException; +import javax.jcr.Property; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.nodetype.NodeType; +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; +import javax.servlet.http.HttpServletRequest; + +import org.argeo.api.cms.CmsLog; +import org.argeo.api.cms.ux.CmsView; +import org.argeo.api.cms.CmsAuth; +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.auth.RemoteAuthCallback; +import org.argeo.cms.auth.RemoteAuthCallbackHandler; +import org.argeo.cms.servlet.ServletHttpRequest; +import org.argeo.cms.servlet.ServletHttpResponse; +import org.argeo.cms.swt.CmsException; +import org.argeo.cms.swt.CmsStyles; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.eclipse.ui.specific.UiContext; +import org.argeo.jcr.JcrUtils; +import org.argeo.util.directory.ldap.AuthPassword; +import org.argeo.util.directory.ldap.SharedSecret; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.rap.rwt.application.AbstractEntryPoint; +import org.eclipse.rap.rwt.client.WebClient; +import org.eclipse.rap.rwt.client.service.BrowserNavigation; +import org.eclipse.rap.rwt.client.service.BrowserNavigationEvent; +import org.eclipse.rap.rwt.client.service.BrowserNavigationListener; +import org.eclipse.rap.rwt.client.service.JavaScriptExecutor; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +/** Manages history and navigation */ +@Deprecated +public abstract class AbstractCmsEntryPoint extends AbstractEntryPoint implements CmsView { + private static final long serialVersionUID = 906558779562569784L; + + private final CmsLog log = CmsLog.getLog(AbstractCmsEntryPoint.class); + + // private final Subject subject; + private LoginContext loginContext; + + private final Repository repository; + private final String workspace; + private final String defaultPath; + private final Map factoryProperties; + + // Current state + private Session session; + private Node node; + private String nodePath;// useful when changing auth + private String state; + private Throwable exception; + + // Client services + private final JavaScriptExecutor jsExecutor; + private final BrowserNavigation browserNavigation; + + public AbstractCmsEntryPoint(Repository repository, String workspace, String defaultPath, + Map factoryProperties) { + this.repository = repository; + this.workspace = workspace; + this.defaultPath = defaultPath; + this.factoryProperties = new HashMap(factoryProperties); + // subject = new Subject(); + + // Initial login + LoginContext lc; + try { + lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER, + new RemoteAuthCallbackHandler(new ServletHttpRequest(UiContext.getHttpRequest()), + new ServletHttpResponse(UiContext.getHttpResponse()))); + lc.login(); + } catch (LoginException e) { + try { + lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_ANONYMOUS); + lc.login(); + } catch (LoginException e1) { + throw new CmsException("Cannot log in as anonymous", e1); + } + } + authChange(lc); + + jsExecutor = RWT.getClient().getService(JavaScriptExecutor.class); + browserNavigation = RWT.getClient().getService(BrowserNavigation.class); + if (browserNavigation != null) + browserNavigation.addBrowserNavigationListener(new CmsNavigationListener()); + } + + @Override + protected Shell createShell(Display display) { + Shell shell = super.createShell(display); + shell.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_SHELL); + display.disposeExec(new Runnable() { + + @Override + public void run() { + if (log.isTraceEnabled()) + log.trace("Logging out " + session); + JcrUtils.logoutQuietly(session); + } + }); + return shell; + } + + @Override + protected final void createContents(final Composite parent) { + // UiContext.setData(CmsView.KEY, this); + CmsSwtUtils.registerCmsView(parent.getShell(), this); + Subject.doAs(getSubject(), new PrivilegedAction() { + @Override + public Void run() { + try { + initUi(parent); + } catch (Exception e) { + throw new CmsException("Cannot create entrypoint contents", e); + } + return null; + } + }); + } + + /** Create UI */ + protected abstract void initUi(Composite parent); + + /** Recreate UI after navigation or auth change */ + protected abstract void refresh(); + + /** + * The node to return when no node was found (for authenticated users and + * anonymous) + */ +// private Node getDefaultNode(Session session) throws RepositoryException { +// if (!session.hasPermission(defaultPath, "read")) { +// String userId = session.getUserID(); +// if (userId.equals(NodeConstants.ROLE_ANONYMOUS)) +// // TODO throw a special exception +// throw new CmsException("Login required"); +// else +// throw new CmsException("Unauthorized"); +// } +// return session.getNode(defaultPath); +// } + + protected String getBaseTitle() { + return factoryProperties.get(WebClient.PAGE_TITLE); + } + + public void navigateTo(String state) { + exception = null; + String title = setState(state); + doRefresh(); + if (browserNavigation != null) + browserNavigation.pushState(state, title); + } + + // @Override + // public synchronized Subject getSubject() { + // return subject; + // } + + // @Override + // public LoginContext getLoginContext() { + // return loginContext; + // } + protected Subject getSubject() { + return loginContext.getSubject(); + } + + @Override + public boolean isAnonymous() { + return CurrentUser.isAnonymous(getSubject()); + } + + @Override + public synchronized void logout() { + if (loginContext == null) + throw new CmsException("Login context should not be null"); + try { + CurrentUser.logoutCmsSession(loginContext.getSubject()); + loginContext.logout(); + LoginContext anonymousLc = new LoginContext(CmsAuth.LOGIN_CONTEXT_ANONYMOUS); + anonymousLc.login(); + authChange(anonymousLc); + } catch (LoginException e) { + log.error("Cannot logout", e); + } + } + + @Override + public synchronized void authChange(LoginContext lc) { + if (lc == null) + throw new CmsException("Login context cannot be null"); + // logout previous login context + if (this.loginContext != null) + try { + this.loginContext.logout(); + } catch (LoginException e1) { + log.warn("Could not log out: " + e1); + } + this.loginContext = lc; + Subject.doAs(getSubject(), new PrivilegedAction() { + + @Override + public Void run() { + try { + JcrUtils.logoutQuietly(session); + session = repository.login(workspace); + if (nodePath != null) + try { + node = session.getNode(nodePath); + } catch (PathNotFoundException e) { + navigateTo("~"); + } + + // refresh UI + doRefresh(); + } catch (RepositoryException e) { + throw new CmsException("Cannot perform auth change", e); + } + return null; + } + + }); + } + + @Override + public void exception(final Throwable e) { + AbstractCmsEntryPoint.this.exception = e; + log.error("Unexpected exception in CMS", e); + doRefresh(); + } + + protected synchronized void doRefresh() { + Subject.doAs(getSubject(), new PrivilegedAction() { + @Override + public Void run() { + refresh(); + return null; + } + }); + } + + /** Sets the state of the entry point and retrieve the related JCR node. */ + protected synchronized String setState(String newState) { + String previousState = this.state; + + String newNodePath = null; + String prefix = null; + this.state = newState; + if (newState.equals("~")) + this.state = ""; + + try { + int firstSlash = state.indexOf('/'); + if (firstSlash == 0) { + newNodePath = state; + prefix = ""; + } else if (firstSlash > 0) { + prefix = state.substring(0, firstSlash); + newNodePath = state.substring(firstSlash); + } else { + newNodePath = defaultPath; + prefix = state; + + } + + // auth + int colonIndex = prefix.indexOf('$'); + if (colonIndex > 0) { + SharedSecret token = new SharedSecret(new AuthPassword(X_SHARED_SECRET + '$' + prefix)) { + + @Override + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + super.handle(callbacks); + // handle HTTP context + for (Callback callback : callbacks) { + if (callback instanceof RemoteAuthCallback) { + ((RemoteAuthCallback) callback) + .setRequest(new ServletHttpRequest(UiContext.getHttpRequest())); + ((RemoteAuthCallback) callback) + .setResponse(new ServletHttpResponse(UiContext.getHttpResponse())); + } + } + } + }; + LoginContext lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER, token); + lc.login(); + authChange(lc);// sets the node as well + // } else { + // // TODO check consistency + // } + } else { + Node newNode = null; + if (session.nodeExists(newNodePath)) + newNode = session.getNode(newNodePath); + else { +// throw new CmsException("Data " + newNodePath + " does not exist"); + newNode = null; + } + setNode(newNode); + } + String title = publishMetaData(getNode()); + + if (log.isTraceEnabled()) + log.trace("node=" + newNodePath + ", state=" + state + " (prefix=" + prefix + ")"); + + return title; + } catch (Exception e) { + log.error("Cannot set state '" + state + "'", e); + if (state.equals("") || newState.equals("~") || newState.equals(previousState)) + return "Unrecoverable exception : " + e.getClass().getSimpleName(); + if (previousState.equals("")) + previousState = "~"; + navigateTo(previousState); + throw new CmsException("Unexpected issue when accessing #" + newState, e); + } + } + + private String publishMetaData(Node node) throws RepositoryException { + // Title + String title; + if (node != null && node.isNodeType(NodeType.MIX_TITLE) && node.hasProperty(Property.JCR_TITLE)) + title = node.getProperty(Property.JCR_TITLE).getString() + " - " + getBaseTitle(); + else + title = getBaseTitle(); + + HttpServletRequest request = UiContext.getHttpRequest(); + if (request == null) + return null; + + StringBuilder js = new StringBuilder(); + if (title == null) + title = ""; + title = title.replace("'", "\\'");// sanitize + js.append("document.title = '" + title + "';"); + jsExecutor.execute(js.toString()); + return title; + } + + // Simply remove some illegal character + // private String clean(String stringToClean) { + // return stringToClean.replaceAll("'", "").replaceAll("\\n", "") + // .replaceAll("\\t", ""); + // } + + protected synchronized Node getNode() { + return node; + } + + private synchronized void setNode(Node node) throws RepositoryException { + this.node = node; + this.nodePath = node == null ? null : node.getPath(); + } + + protected String getState() { + return state; + } + + protected Throwable getException() { + return exception; + } + + protected void resetException() { + exception = null; + } + + protected Session getSession() { + return session; + } + + private class CmsNavigationListener implements BrowserNavigationListener { + private static final long serialVersionUID = -3591018803430389270L; + + @Override + public void navigated(BrowserNavigationEvent event) { + setState(event.getState()); + doRefresh(); + } + } +} \ No newline at end of file diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/BundleResourceLoader.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/BundleResourceLoader.java new file mode 100644 index 000000000..ca93e625e --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/BundleResourceLoader.java @@ -0,0 +1,34 @@ +package org.argeo.cms.web; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +import org.eclipse.rap.rwt.service.ResourceLoader; +import org.osgi.framework.Bundle; + +/** {@link ResourceLoader} implementation wrapping an {@link Bundle}. */ +public class BundleResourceLoader implements ResourceLoader { + private final Bundle bundle; + + public BundleResourceLoader(Bundle bundle) { + this.bundle = bundle; + } + + @Override + public InputStream getResourceAsStream(String resourceName) throws IOException { + URL res = bundle.getEntry(resourceName); + if (res == null) { + res = bundle.getResource(resourceName); + if (res == null) + throw new IllegalArgumentException( + "Resource " + resourceName + " not found in bundle " + bundle.getSymbolicName()); + } + return res.openStream(); + } + + public Bundle getBundle() { + return bundle; + } + +} diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/CmsThemeResourceLoader.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/CmsThemeResourceLoader.java new file mode 100644 index 000000000..102a4e103 --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/CmsThemeResourceLoader.java @@ -0,0 +1,23 @@ +package org.argeo.cms.web; + +import java.io.IOException; +import java.io.InputStream; + +import org.argeo.api.cms.ux.CmsTheme; +import org.eclipse.rap.rwt.service.ResourceLoader; + +/** A RAP {@link ResourceLoader} based on a {@link CmsTheme}. */ +public class CmsThemeResourceLoader implements ResourceLoader { + private final CmsTheme theme; + + public CmsThemeResourceLoader(CmsTheme theme) { + super(); + this.theme = theme; + } + + @Override + public InputStream getResourceAsStream(String resourceName) throws IOException { + return theme.getResourceAsStream(resourceName); + } + +} diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/CmsWebApp.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/CmsWebApp.java new file mode 100644 index 000000000..0270933c0 --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/CmsWebApp.java @@ -0,0 +1,167 @@ +package org.argeo.cms.web; + +import java.util.Dictionary; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.argeo.api.cms.CmsApp; +import org.argeo.api.cms.CmsAppListener; +import org.argeo.api.cms.CmsLog; +import org.argeo.api.cms.ux.CmsTheme; +import org.argeo.api.cms.ux.CmsView; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.util.LangUtils; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.rap.rwt.application.Application; +import org.eclipse.rap.rwt.application.Application.OperationMode; +import org.eclipse.rap.rwt.application.ApplicationConfiguration; +import org.eclipse.rap.rwt.application.ExceptionHandler; +import org.eclipse.rap.rwt.client.WebClient; +import org.eclipse.swt.widgets.Display; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.event.EventAdmin; + +/** An RWT web app integrating with a {@link CmsApp}. */ +public class CmsWebApp implements ApplicationConfiguration, ExceptionHandler, CmsAppListener { + private final static CmsLog log = CmsLog.getLog(CmsWebApp.class); + + private BundleContext bundleContext; + private CmsApp cmsApp; +// private String cmsAppId; + private EventAdmin eventAdmin; + + private ServiceRegistration rwtAppReg; + + private final static String CONTEXT_NAME = "contextName"; + private String contextName; + + private final static String FAVICON_PNG = "favicon.png"; + + public void init(BundleContext bundleContext, Map properties) { + this.bundleContext = bundleContext; + contextName = properties.get(CONTEXT_NAME); + if (cmsApp != null) { + if (cmsApp.allThemesAvailable()) + publishWebApp(); + } + } + + public void destroy(BundleContext bundleContext, Map properties) { + if (cmsApp != null) { + cmsApp.removeCmsAppListener(this); + cmsApp = null; + } + } + + @Override + public void configure(Application application) { + // TODO make it configurable? + // SWT compatibility is required for: + // - Browser.execute() + // - blocking dialogs + application.setOperationMode(OperationMode.SWT_COMPATIBILITY); + for (String uiName : cmsApp.getUiNames()) { + CmsTheme theme = cmsApp.getTheme(uiName); + if (theme != null) + WebThemeUtils.apply(application, theme); + } + + Map properties = new HashMap<>(); + addEntryPoints(application, properties); + application.setExceptionHandler(this); + } + + @Override + public void handleException(Throwable throwable) { + Display display = Display.getCurrent(); + if (display != null && !display.isDisposed()) { + CmsView cmsView = CmsSwtUtils.getCmsView(display.getActiveShell()); + cmsView.exception(throwable); + } else { + log.error("Unexpected exception outside an UI thread", throwable); + } + + } + + protected void addEntryPoints(Application application, Map commonProperties) { + for (String uiName : cmsApp.getUiNames()) { + Map properties = new HashMap<>(commonProperties); + CmsTheme theme = cmsApp.getTheme(uiName); + if (theme != null) { + properties.put(WebClient.THEME_ID, theme.getThemeId()); + properties.put(WebClient.HEAD_HTML, theme.getHtmlHeaders()); + properties.put(WebClient.BODY_HTML, theme.getBodyHtml()); + Set imagePaths = theme.getImagesPaths(); + if (imagePaths.contains(FAVICON_PNG)) { + properties.put(WebClient.FAVICON, FAVICON_PNG); + } + } else { + properties.put(WebClient.THEME_ID, RWT.DEFAULT_THEME_ID); + } + String entryPointName = !uiName.equals("") ? "/" + uiName : "/"; + application.addEntryPoint(entryPointName, () -> { + CmsWebEntryPoint entryPoint = new CmsWebEntryPoint(this, uiName); + entryPoint.setEventAdmin(eventAdmin); + return entryPoint; + }, properties); + if (log.isDebugEnabled()) + log.info("Added web entry point " + (contextName != null ? "/" + contextName : "") + entryPointName); + } +// if (log.isDebugEnabled()) +// log.debug("Published CMS web app /" + (contextName != null ? contextName : "")); + } + + CmsApp getCmsApp() { + return cmsApp; + } + + BundleContext getBundleContext() { + return bundleContext; + } + + public void setCmsApp(CmsApp cmsApp) { + this.cmsApp = cmsApp; +// this.cmsAppId = properties.get(Constants.SERVICE_PID); + this.cmsApp.addCmsAppListener(this); + } + + public void unsetCmsApp(CmsApp cmsApp, Map properties) { + String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY); + if (!contextName.equals(this.contextName)) + return; + if (this.cmsApp != null) { + this.cmsApp.removeCmsAppListener(this); + } + if (rwtAppReg != null) + rwtAppReg.unregister(); + this.cmsApp = null; + } + + @Override + public void themingUpdated() { + if (cmsApp != null && cmsApp.allThemesAvailable()) + publishWebApp(); + } + + protected void publishWebApp() { + Dictionary regProps = LangUtils.dict(CONTEXT_NAME, contextName); + if (rwtAppReg != null) + rwtAppReg.unregister(); + if (bundleContext != null) { + rwtAppReg = bundleContext.registerService(ApplicationConfiguration.class, this, regProps); + if (log.isDebugEnabled()) + log.debug("Publishing CMS web app /" + (contextName != null ? contextName : "") + " ..."); + } + } + + public void setEventAdmin(EventAdmin eventAdmin) { + this.eventAdmin = eventAdmin; + } + + public void setContextName(String contextName) { + this.contextName = contextName; + } + +} diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/CmsWebEntryPoint.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/CmsWebEntryPoint.java new file mode 100644 index 000000000..159719720 --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/CmsWebEntryPoint.java @@ -0,0 +1,368 @@ +package org.argeo.cms.web; + +import static org.eclipse.rap.rwt.internal.service.ContextProvider.getApplicationContext; + +import java.security.PrivilegedAction; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.UUID; + +import javax.security.auth.Subject; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; + +import org.argeo.api.cms.CmsApp; +import org.argeo.api.cms.CmsAuth; +import org.argeo.api.cms.CmsLog; +import org.argeo.api.cms.CmsSession; +import org.argeo.api.cms.ux.CmsImageManager; +import org.argeo.api.cms.ux.CmsUi; +import org.argeo.api.cms.ux.CmsView; +import org.argeo.api.cms.ux.UxContext; +import org.argeo.cms.LocaleUtils; +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.auth.RemoteAuthCallbackHandler; +import org.argeo.cms.servlet.ServletHttpRequest; +import org.argeo.cms.servlet.ServletHttpResponse; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.swt.SimpleSwtUxContext; +import org.argeo.cms.swt.dialogs.CmsFeedback; +import org.argeo.cms.ui.util.DefaultImageManager; +import org.argeo.eclipse.ui.specific.UiContext; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.rap.rwt.application.EntryPoint; +import org.eclipse.rap.rwt.client.service.BrowserNavigation; +import org.eclipse.rap.rwt.client.service.BrowserNavigationEvent; +import org.eclipse.rap.rwt.client.service.BrowserNavigationListener; +import org.eclipse.rap.rwt.internal.lifecycle.RWTLifeCycle; +import org.eclipse.swt.SWT; +import org.eclipse.swt.SWTError; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.osgi.service.event.Event; +import org.osgi.service.event.EventAdmin; + +/** The {@link CmsView} for a {@link CmsWebApp}. */ +@SuppressWarnings("restriction") +public class CmsWebEntryPoint implements EntryPoint, CmsView, BrowserNavigationListener { + private static final long serialVersionUID = 7733510691684570402L; + private final static CmsLog log = CmsLog.getLog(CmsWebEntryPoint.class); + + private EventAdmin eventAdmin; + + private final CmsWebApp cmsWebApp; + private final String uiName; + + private LoginContext loginContext; + private String state; + private Throwable exception; + private UxContext uxContext; + private CmsImageManager imageManager; + + private Display display; + private CmsUi ui; + + private String uid; + + // Client services + // private final JavaScriptExecutor jsExecutor; + private final BrowserNavigation browserNavigation; + + /** Experimental OS-like multi windows. */ + private boolean multipleShells = false; + + public CmsWebEntryPoint(CmsWebApp cmsWebApp, String uiName) { + assert cmsWebApp != null; + assert uiName != null; + this.cmsWebApp = cmsWebApp; + this.uiName = uiName; + uid = UUID.randomUUID().toString(); + + // Initial login + LoginContext lc; + try { + lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER, + new RemoteAuthCallbackHandler(new ServletHttpRequest(UiContext.getHttpRequest()), + new ServletHttpResponse(UiContext.getHttpResponse()))); + lc.login(); + } catch (LoginException e) { + try { + lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_ANONYMOUS, + new RemoteAuthCallbackHandler(new ServletHttpRequest(UiContext.getHttpRequest()), + new ServletHttpResponse(UiContext.getHttpResponse()))); + lc.login(); + } catch (LoginException e1) { + throw new IllegalStateException("Cannot log in as anonymous", e1); + } + } + authChange(lc); + + // jsExecutor = RWT.getClient().getService(JavaScriptExecutor.class); + browserNavigation = RWT.getClient().getService(BrowserNavigation.class); + if (browserNavigation != null) + browserNavigation.addBrowserNavigationListener(this); + } + + protected void createContents(Composite parent) { + Subject.doAs(loginContext.getSubject(), new PrivilegedAction() { + @Override + public Void run() { + try { + uxContext = new SimpleSwtUxContext(); + imageManager = new DefaultImageManager(); + CmsSession cmsSession = getCmsSession(); + if (cmsSession != null) { + UiContext.setLocale(cmsSession.getLocale()); + LocaleUtils.setThreadLocale(cmsSession.getLocale()); + } else { + Locale rwtLocale = RWT.getUISession().getLocale(); + LocaleUtils.setThreadLocale(rwtLocale); + } + parent.setData(CmsApp.UI_NAME_PROPERTY, uiName); + display = parent.getDisplay(); + ui = cmsWebApp.getCmsApp().initUi(parent); + if (ui instanceof Composite) + ((Composite) ui).setLayoutData(CmsSwtUtils.fillAll()); + // we need ui to be set before refresh so that CmsView can store UI context data + // in it. + cmsWebApp.getCmsApp().refreshUi(ui, null); + } catch (Exception e) { + throw new IllegalStateException("Cannot create entrypoint contents", e); + } + return null; + } + }); + } + + protected Subject getSubject() { + return loginContext.getSubject(); + } + + public T doAs(PrivilegedAction action) { + return Subject.doAs(getSubject(), action); + } + + @Override + public boolean isAnonymous() { + return CurrentUser.isAnonymous(getSubject()); + } + + @Override + public synchronized void logout() { + if (loginContext == null) + throw new IllegalArgumentException("Login context should not be null"); + try { + CurrentUser.logoutCmsSession(loginContext.getSubject()); + loginContext.logout(); + LoginContext anonymousLc = new LoginContext(CmsAuth.LOGIN_CONTEXT_ANONYMOUS, + new RemoteAuthCallbackHandler(new ServletHttpRequest(UiContext.getHttpRequest()), + new ServletHttpResponse(UiContext.getHttpResponse()))); + anonymousLc.login(); + authChange(anonymousLc); + } catch (LoginException e) { + log.error("Cannot logout", e); + } + } + + @Override + public synchronized void authChange(LoginContext lc) { + if (lc == null) + throw new IllegalArgumentException("Login context cannot be null"); + // logout previous login context + if (this.loginContext != null) + try { + this.loginContext.logout(); + } catch (LoginException e1) { + log.warn("Could not log out: " + e1); + } + this.loginContext = lc; + doRefresh(); + } + + @Override + public void exception(final Throwable e) { + if (e instanceof SWTError) { + SWTError swtError = (SWTError) e; + if (swtError.code == SWT.ERROR_FUNCTION_DISPOSED) + return; + } + display.syncExec(() -> { +// CmsFeedback.show("Unexpected exception in CMS", e); + exception = e; + log.error("Unexpected exception in CMS", e); + doRefresh(); + }); + } + + protected synchronized void doRefresh() { + if (ui != null) + Subject.doAs(getSubject(), new PrivilegedAction() { + @Override + public Void run() { + if (exception != null) { + // TODO internationalise + CmsFeedback.show("Unexpected exception", exception); + exception = null; + // TODO report + } + cmsWebApp.getCmsApp().refreshUi(ui, state); + return null; + } + }); + } + + /** Sets the state of the entry point and retrieve the related JCR node. */ + protected String setState(String newState) { + cmsWebApp.getCmsApp().setState(ui, newState); + state = newState; + return null; + } + + @Override + public UxContext getUxContext() { + return uxContext; + } + + @Override + public String getUid() { + return uid; + } + + @Override + public void navigateTo(String state) { + exception = null; + String title = setState(state); + if (title != null) + doRefresh(); + if (browserNavigation != null) + browserNavigation.pushState(state, title); + } + + public CmsImageManager getImageManager() { + return imageManager; + } + + @Override + public void navigated(BrowserNavigationEvent event) { + setState(event.getState()); + // doRefresh(); + } + + @Override + public void sendEvent(String topic, Map properties) { + if (properties == null) + properties = new HashMap<>(); + if (properties.containsKey(CMS_VIEW_UID_PROPERTY) && !properties.get(CMS_VIEW_UID_PROPERTY).equals(uid)) + throw new IllegalArgumentException("Property " + CMS_VIEW_UID_PROPERTY + " is set to another CMS view uid (" + + properties.get(CMS_VIEW_UID_PROPERTY) + ") then " + uid); + properties.put(CMS_VIEW_UID_PROPERTY, uid); + eventAdmin.sendEvent(new Event(topic, properties)); + } + + @Override + public void stateChanged(String state, String title) { + browserNavigation.pushState(state, title); + } + + @Override + public CmsSession getCmsSession() { + CmsSession cmsSession = cmsWebApp.getCmsApp().getCmsContext().getCmsSession(getSubject()); + if (cmsSession == null) + throw new IllegalStateException("No CMS session available for " + getSubject()); + return cmsSession; + } + + @Override + public Object getData(String key) { + if (ui != null) { + return ui.getData(key); + } else { + throw new IllegalStateException("UI is not initialized"); + } + } + + @Override + public void setData(String key, Object value) { + if (ui != null) { + ui.setData(key, value); + } else { + throw new IllegalStateException("UI is not initialized"); + } + } + + /* + * EntryPoint IMPLEMENTATION + */ + + @Override + public int createUI() { + Display display = new Display(); + Shell shell = createShell(display); + shell.setLayout(CmsSwtUtils.noSpaceGridLayout()); + CmsSwtUtils.registerCmsView(shell, this); + createContents(shell); + shell.layout(); +// if (shell.getMaximized()) { +// shell.layout(); +// } else { +//// shell.pack(); +// } + shell.open(); + if (getApplicationContext().getLifeCycleFactory().getLifeCycle() instanceof RWTLifeCycle) { + eventLoop: while (!shell.isDisposed()) { + try { + Subject.doAs(loginContext.getSubject(), new PrivilegedAction() { + @Override + public Void run() { + if (!display.readAndDispatch()) { + display.sleep(); + } + return null; + } + }); + } catch (Throwable e) { + if (e instanceof SWTError) { + SWTError swtError = (SWTError) e; + if (swtError.code == SWT.ERROR_FUNCTION_DISPOSED) { + log.error("Unexpected SWT error in event loop, ignoring it. " + e.getMessage()); + continue eventLoop; + } else { + log.error("Unexpected SWT error in event loop, shutting down...", e); + break eventLoop; + } + } else if (e instanceof ThreadDeath) { + throw (ThreadDeath) e; + } else if (e instanceof Error) { + log.error("Unexpected error in event loop, shutting down...", e); + break eventLoop; + } else { + log.error("Unexpected exception in event loop, ignoring it. " + e.getMessage()); + continue eventLoop; + } + } + } + if (!display.isDisposed()) + display.dispose(); + } + return 0; + } + + protected Shell createShell(Display display) { + Shell shell; + if (!multipleShells) { + shell = new Shell(display, SWT.NO_TRIM); + shell.setMaximized(true); + } else { + shell = new Shell(display, SWT.SHELL_TRIM); + shell.setSize(800, 600); + } + return shell; + } + + public void setEventAdmin(EventAdmin eventAdmin) { + this.eventAdmin = eventAdmin; + } + +} diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/MinimalWebApp.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/MinimalWebApp.java new file mode 100644 index 000000000..2eff71ee8 --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/MinimalWebApp.java @@ -0,0 +1,56 @@ +package org.argeo.cms.web; + +import static org.argeo.cms.osgi.BundleCmsTheme.CMS_THEME_BUNDLE_PROPERTY; + +import java.util.HashMap; +import java.util.Map; + +import org.argeo.cms.osgi.BundleCmsTheme; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.rap.rwt.application.Application; +import org.eclipse.rap.rwt.application.ApplicationConfiguration; +import org.eclipse.rap.rwt.client.WebClient; +import org.osgi.framework.BundleContext; + +/** Lightweight web app using only RWT and not the whole Eclipse platform. */ +public class MinimalWebApp implements ApplicationConfiguration { + + private BundleCmsTheme theme; + + public void init(BundleContext bundleContext, Map properties) { + if (properties.containsKey(CMS_THEME_BUNDLE_PROPERTY)) { + String cmsThemeBundle = properties.get(CMS_THEME_BUNDLE_PROPERTY).toString(); + theme = new BundleCmsTheme(bundleContext, cmsThemeBundle); + } + } + + public void destroy() { + + } + + /** To be overridden. Does nothing by default. */ + protected void addEntryPoints(Application application, Map properties) { + + } + + @Override + public void configure(Application application) { + if (theme != null) + WebThemeUtils.apply(application, theme); + + Map properties = new HashMap<>(); + if (theme != null) { + properties.put(WebClient.THEME_ID, theme.getThemeId()); + properties.put(WebClient.HEAD_HTML, theme.getHtmlHeaders()); + } else { + properties.put(WebClient.THEME_ID, RWT.DEFAULT_THEME_ID); + } + addEntryPoints(application, properties); + + } + + public void setTheme(BundleCmsTheme theme) { + this.theme = theme; + } + +} diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/SimpleApp.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/SimpleApp.java new file mode 100644 index 000000000..38a9b4449 --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/SimpleApp.java @@ -0,0 +1,414 @@ +package org.argeo.cms.web; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.security.Privilege; +import javax.jcr.version.VersionManager; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.api.cms.CmsLog; +import org.argeo.cms.jcr.CmsJcrUtils; +import org.argeo.cms.swt.CmsException; +import org.argeo.cms.ui.CmsUiConstants; +import org.argeo.cms.ui.CmsUiProvider; +import org.argeo.cms.ui.LifeCycleUiProvider; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.argeo.cms.ui.util.StyleSheetResourceLoader; +import org.argeo.jcr.JcrUtils; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.rap.rwt.application.Application; +import org.eclipse.rap.rwt.application.Application.OperationMode; +import org.eclipse.rap.rwt.application.ApplicationConfiguration; +import org.eclipse.rap.rwt.application.EntryPoint; +import org.eclipse.rap.rwt.application.EntryPointFactory; +import org.eclipse.rap.rwt.application.ExceptionHandler; +import org.eclipse.rap.rwt.client.WebClient; +import org.eclipse.rap.rwt.client.service.JavaScriptExecutor; +import org.eclipse.rap.rwt.service.ResourceLoader; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; + +/** A basic generic app based on {@link SimpleErgonomics}. */ +@Deprecated +public class SimpleApp implements CmsUiConstants, ApplicationConfiguration { + private final static CmsLog log = CmsLog.getLog(SimpleApp.class); + + private String contextName = null; + + private Map> branding = new HashMap>(); + private Map> styleSheets = new HashMap>(); + + private List resources = new ArrayList(); + + private BundleContext bundleContext; + + private Repository repository; + private String workspace = null; + private String jcrBasePath = "/"; + private List roPrincipals = Arrays.asList(CmsConstants.ROLE_ANONYMOUS, CmsConstants.ROLE_USER); + private List rwPrincipals = Arrays.asList(CmsConstants.ROLE_USER); + + private CmsUiProvider header; + private Map pages = new LinkedHashMap(); + + private Integer headerHeight = 40; + + private ServiceRegistration appReg; + + public void configure(Application application) { + try { + BundleResourceLoader bundleRL = new BundleResourceLoader(bundleContext.getBundle()); + + application.setOperationMode(OperationMode.SWT_COMPATIBILITY); + // application.setOperationMode(OperationMode.JEE_COMPATIBILITY); + + application.setExceptionHandler(new CmsExceptionHandler()); + + // loading animated gif + application.addResource(LOADING_IMAGE, createResourceLoader(LOADING_IMAGE)); + // empty image + application.addResource(NO_IMAGE, createResourceLoader(NO_IMAGE)); + + for (String resource : resources) { + application.addResource(resource, bundleRL); + if (log.isTraceEnabled()) + log.trace("Resource " + resource); + } + + Map defaultBranding = null; + if (branding.containsKey("*")) + defaultBranding = branding.get("*"); + // String defaultTheme = defaultBranding.get(WebClient.THEME_ID); + + // entry points + for (String page : pages.keySet()) { + Map properties = defaultBranding != null ? new HashMap(defaultBranding) + : new HashMap(); + if (branding.containsKey(page)) { + properties.putAll(branding.get(page)); + } + // favicon + if (properties.containsKey(WebClient.FAVICON)) { + String themeId = defaultBranding.get(WebClient.THEME_ID); + Bundle themeBundle = findThemeBundle(bundleContext, themeId); + String faviconRelPath = properties.get(WebClient.FAVICON); + application.addResource(faviconRelPath, + new BundleResourceLoader(themeBundle != null ? themeBundle : bundleContext.getBundle())); + if (log.isTraceEnabled()) + log.trace("Favicon " + faviconRelPath); + + } + + // page title + if (!properties.containsKey(WebClient.PAGE_TITLE)) { + if (page.length() > 0) + properties.put(WebClient.PAGE_TITLE, Character.toUpperCase(page.charAt(0)) + page.substring(1)); + } + + // default body HTML + if (!properties.containsKey(WebClient.BODY_HTML)) + properties.put(WebClient.BODY_HTML, DEFAULT_LOADING_BODY); + + // + // ADD ENTRY POINT + // + application.addEntryPoint("/" + page, + new CmsEntryPointFactory(pages.get(page), repository, workspace, properties), properties); + log.info("Page /" + page); + } + + // stylesheets and themes + Set themeBundles = new HashSet<>(); + for (String themeId : styleSheets.keySet()) { + Bundle themeBundle = findThemeBundle(bundleContext, themeId); + StyleSheetResourceLoader styleSheetRL = new StyleSheetResourceLoader( + themeBundle != null ? themeBundle : bundleContext.getBundle()); + if (themeBundle != null) + themeBundles.add(themeBundle); + List cssLst = styleSheets.get(themeId); + if (log.isDebugEnabled()) + log.debug("Theme " + themeId); + for (String css : cssLst) { + application.addStyleSheet(themeId, css, styleSheetRL); + if (log.isDebugEnabled()) + log.debug(" CSS " + css); + } + + } + for (Bundle themeBundle : themeBundles) { + BundleResourceLoader themeBRL = new BundleResourceLoader(themeBundle); + SimpleApp.addThemeResources(application, themeBundle, themeBRL, "*.png"); + SimpleApp.addThemeResources(application, themeBundle, themeBRL, "*.gif"); + SimpleApp.addThemeResources(application, themeBundle, themeBRL, "*.jpg"); + } + } catch (RuntimeException e) { + // Easier access to initialisation errors + log.error("Unexpected exception when configuring RWT application.", e); + throw e; + } + } + + public void init() throws RepositoryException { + Session session = null; + try { + session = CmsJcrUtils.openDataAdminSession(repository, workspace); + // session = JcrUtils.loginOrCreateWorkspace(repository, workspace); + VersionManager vm = session.getWorkspace().getVersionManager(); + JcrUtils.mkdirs(session, jcrBasePath); + session.save(); + if (!vm.isCheckedOut(jcrBasePath)) + vm.checkout(jcrBasePath); + for (String principal : rwPrincipals) + JcrUtils.addPrivilege(session, jcrBasePath, principal, Privilege.JCR_WRITE); + for (String principal : roPrincipals) + JcrUtils.addPrivilege(session, jcrBasePath, principal, Privilege.JCR_READ); + + for (String pageName : pages.keySet()) { + try { + initPage(session, pages.get(pageName)); + session.save(); + } catch (Exception e) { + throw new CmsException("Cannot initialize page " + pageName, e); + } + } + + } finally { + JcrUtils.logoutQuietly(session); + } + + // publish to OSGi + register(); + } + + protected void initPage(Session adminSession, CmsUiProvider page) throws RepositoryException { + if (page instanceof LifeCycleUiProvider) + ((LifeCycleUiProvider) page).init(adminSession); + } + + public void destroy() { + for (String pageName : pages.keySet()) { + try { + CmsUiProvider page = pages.get(pageName); + if (page instanceof LifeCycleUiProvider) + ((LifeCycleUiProvider) page).destroy(); + } catch (Exception e) { + log.error("Cannot destroy page " + pageName, e); + } + } + } + + protected void register() { + Hashtable props = new Hashtable(); + if (contextName != null) + props.put("contextName", contextName); + appReg = bundleContext.registerService(ApplicationConfiguration.class, this, props); + if (log.isDebugEnabled()) + log.debug("Registered " + (contextName == null ? "/" : contextName)); + } + + protected void unregister() { + appReg.unregister(); + if (log.isDebugEnabled()) + log.debug("Unregistered " + (contextName == null ? "/" : contextName)); + } + + public void setRepository(Repository repository) { + this.repository = repository; + } + + public void setWorkspace(String workspace) { + this.workspace = workspace; + } + + public void setHeader(CmsUiProvider header) { + this.header = header; + } + + public void setPages(Map pages) { + this.pages = pages; + } + + public void setJcrBasePath(String basePath) { + this.jcrBasePath = basePath; + } + + public void setRoPrincipals(List roPrincipals) { + this.roPrincipals = roPrincipals; + } + + public void setRwPrincipals(List rwPrincipals) { + this.rwPrincipals = rwPrincipals; + } + + public void setHeaderHeight(Integer headerHeight) { + this.headerHeight = headerHeight; + } + + public void setBranding(Map> branding) { + this.branding = branding; + } + + public void setStyleSheets(Map> styleSheets) { + this.styleSheets = styleSheets; + } + + public void setBundleContext(BundleContext bundleContext) { + this.bundleContext = bundleContext; + } + + public void setResources(List resources) { + this.resources = resources; + } + + public void setContextName(String contextName) { + this.contextName = contextName; + } + + private static void addThemeResources(Application application, Bundle themeBundle, BundleResourceLoader themeBRL, + String pattern) { + Enumeration themeResources = themeBundle.findEntries("/", pattern, true); + if (themeResources == null) + return; + while (themeResources.hasMoreElements()) { + String resource = themeResources.nextElement().getPath(); + // remove first '/' so that RWT registers it + resource = resource.substring(1); + if (!resource.endsWith("/")) { + application.addResource(resource, themeBRL); + if (log.isTraceEnabled()) + log.trace("Registered " + resource + " from theme " + themeBundle); + } + + } + + } + + private static Bundle findThemeBundle(BundleContext bundleContext, String themeId) { + if (themeId == null) + return null; + // TODO optimize + // TODO deal with multiple versions + Bundle themeBundle = null; + if (themeId != null) { + for (Bundle bundle : bundleContext.getBundles()) + if (themeId.equals(bundle.getSymbolicName())) { + themeBundle = bundle; + break; + } + } + return themeBundle; + } + + class CmsExceptionHandler implements ExceptionHandler { + + @Override + public void handleException(Throwable throwable) { + // TODO be smarter + CmsUiUtils.getCmsView().exception(throwable); + } + + } + + private class CmsEntryPointFactory implements EntryPointFactory { + private final CmsUiProvider page; + private final Repository repository; + private final String workspace; + private final Map properties; + + public CmsEntryPointFactory(CmsUiProvider page, Repository repository, String workspace, + Map properties) { + this.page = page; + this.repository = repository; + this.workspace = workspace; + this.properties = properties; + } + + @Override + public EntryPoint create() { + SimpleErgonomics entryPoint = new SimpleErgonomics(repository, workspace, jcrBasePath, page, properties) { + private static final long serialVersionUID = -637940404865527290L; + + @Override + protected void createAdminArea(Composite parent) { + Composite adminArea = new Composite(parent, SWT.NONE); + adminArea.setLayout(new FillLayout()); + Button refresh = new Button(adminArea, SWT.PUSH); + refresh.setText("Reload App"); + refresh.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = -7671999525536351366L; + + @Override + public void widgetSelected(SelectionEvent e) { + long timeBeforeReload = 1000; + RWT.getClient().getService(JavaScriptExecutor.class).execute( + "setTimeout(function() { " + "location.reload();" + "}," + timeBeforeReload + ");"); + reloadApp(); + } + }); + } + }; + // entryPoint.setState(""); + entryPoint.setHeader(header); + entryPoint.setHeaderHeight(headerHeight); + // CmsSession.current.set(entryPoint); + return entryPoint; + } + + private void reloadApp() { + new Thread("Refresh app") { + @Override + public void run() { + unregister(); + register(); + } + }.start(); + } + } + + private static ResourceLoader createResourceLoader(final String resourceName) { + return new ResourceLoader() { + public InputStream getResourceAsStream(String resourceName) throws IOException { + return getClass().getClassLoader().getResourceAsStream(resourceName); + } + }; + } + + // private static ResourceLoader createUrlResourceLoader(final URL url) { + // return new ResourceLoader() { + // public InputStream getResourceAsStream(String resourceName) + // throws IOException { + // return url.openStream(); + // } + // }; + // } + + /* + * TEXTS + */ + private static String DEFAULT_LOADING_BODY = "" + + "" + ""; +} diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/SimpleErgonomics.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/SimpleErgonomics.java new file mode 100644 index 000000000..783f6eb73 --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/SimpleErgonomics.java @@ -0,0 +1,238 @@ +package org.argeo.cms.web; + +import java.util.Map; +import java.util.UUID; + +import javax.jcr.Node; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; + +import org.argeo.api.cms.CmsLog; +import org.argeo.api.cms.ux.CmsImageManager; +import org.argeo.api.cms.ux.UxContext; +import org.argeo.cms.swt.CmsException; +import org.argeo.cms.swt.CmsStyles; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.swt.SimpleSwtUxContext; +import org.argeo.cms.ui.CmsUiProvider; +import org.argeo.cms.ui.util.DefaultImageManager; +import org.argeo.cms.ui.util.SystemNotifications; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +/** Simple header/body ergonomics. */ +@Deprecated +public class SimpleErgonomics extends AbstractCmsEntryPoint { + private static final long serialVersionUID = 8743413921359548523L; + + private final static CmsLog log = CmsLog.getLog(SimpleErgonomics.class); + + private boolean uiInitialized = false; + private Composite headerArea; + private Composite leftArea; + private Composite rightArea; + private Composite footerArea; + private Composite bodyArea; + private final CmsUiProvider uiProvider; + + private CmsUiProvider header; + private Integer headerHeight = 0; + private Integer footerHeight = 0; + private CmsUiProvider lead; + private CmsUiProvider end; + private CmsUiProvider footer; + + private CmsImageManager imageManager = new DefaultImageManager(); + private UxContext uxContext = null; + private String uid; + + public SimpleErgonomics(Repository repository, String workspace, String defaultPath, CmsUiProvider uiProvider, + Map factoryProperties) { + super(repository, workspace, defaultPath, factoryProperties); + this.uiProvider = uiProvider; + } + + @Override + protected void initUi(Composite parent) { + uid = UUID.randomUUID().toString(); + parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + parent.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(3, false))); + + uxContext = new SimpleSwtUxContext(); + if (!getUxContext().isMasterData()) + createAdminArea(parent); + headerArea = new Composite(parent, SWT.NONE); + headerArea.setLayout(new FillLayout()); + GridData headerData = new GridData(SWT.FILL, SWT.FILL, false, false, 3, 1); + headerData.heightHint = headerHeight; + headerArea.setLayoutData(headerData); + + // TODO: bi-directional + leftArea = new Composite(parent, SWT.NONE); + leftArea.setLayoutData(new GridData(SWT.LEAD, SWT.TOP, false, false)); + leftArea.setLayout(CmsSwtUtils.noSpaceGridLayout()); + + bodyArea = new Composite(parent, SWT.NONE); + bodyArea.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_BODY); + bodyArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + bodyArea.setLayout(CmsSwtUtils.noSpaceGridLayout()); + + // TODO: bi-directional + rightArea = new Composite(parent, SWT.NONE); + rightArea.setLayoutData(new GridData(SWT.END, SWT.TOP, false, false)); + rightArea.setLayout(CmsSwtUtils.noSpaceGridLayout()); + + footerArea = new Composite(parent, SWT.NONE); + // footerArea.setLayout(new FillLayout()); + GridData footerData = new GridData(SWT.FILL, SWT.FILL, false, false, 3, 1); + footerData.heightHint = footerHeight; + footerArea.setLayoutData(footerData); + + uiInitialized = true; + refresh(); + } + + @Override + protected void refresh() { + if (!uiInitialized) + return; + if (getState() == null) + setState(""); + refreshSides(); + refreshBody(); + if (log.isTraceEnabled()) + log.trace("UI refreshed " + getNode()); + } + + protected void createAdminArea(Composite parent) { + } + + @Deprecated + protected void refreshHeader() { + if (header == null) + return; + + for (Control child : headerArea.getChildren()) + child.dispose(); + try { + header.createUi(headerArea, getNode()); + } catch (RepositoryException e) { + throw new CmsException("Cannot refresh header", e); + } + headerArea.layout(true, true); + } + + protected void refreshSides() { + refresh(headerArea, header, CmsStyles.CMS_HEADER); + refresh(leftArea, lead, CmsStyles.CMS_LEAD); + refresh(rightArea, end, CmsStyles.CMS_END); + refresh(footerArea, footer, CmsStyles.CMS_FOOTER); + } + + private void refresh(Composite area, CmsUiProvider uiProvider, String style) { + if (uiProvider == null) + return; + + for (Control child : area.getChildren()) + child.dispose(); + CmsSwtUtils.style(area, style); + try { + uiProvider.createUi(area, getNode()); + } catch (RepositoryException e) { + throw new CmsException("Cannot refresh header", e); + } + area.layout(true, true); + } + + protected void refreshBody() { + // Exception + Throwable exception = getException(); + if (exception != null) { + SystemNotifications systemNotifications = new SystemNotifications(bodyArea); + systemNotifications.notifyException(exception); + resetException(); + return; + // TODO report + } + + // clear + for (Control child : bodyArea.getChildren()) + child.dispose(); + bodyArea.setLayout(CmsSwtUtils.noSpaceGridLayout()); + + try { + Node node = getNode(); +// if (node == null) +// log.error("Context cannot be null"); +// else + uiProvider.createUi(bodyArea, node); + } catch (RepositoryException e) { + throw new CmsException("Cannot refresh body", e); + } + + bodyArea.layout(true, true); + } + + @Override + public UxContext getUxContext() { + return uxContext; + } + @Override + public String getUid() { + return uid; + } + + public CmsImageManager getImageManager() { + return imageManager; + } + + public void setHeader(CmsUiProvider header) { + this.header = header; + } + + public void setHeaderHeight(Integer headerHeight) { + this.headerHeight = headerHeight; + } + + public void setImageManager(CmsImageManager imageManager) { + this.imageManager = imageManager; + } + + public CmsUiProvider getLead() { + return lead; + } + + public void setLead(CmsUiProvider lead) { + this.lead = lead; + } + + public CmsUiProvider getEnd() { + return end; + } + + public void setEnd(CmsUiProvider end) { + this.end = end; + } + + public CmsUiProvider getFooter() { + return footer; + } + + public void setFooter(CmsUiProvider footer) { + this.footer = footer; + } + + public CmsUiProvider getHeader() { + return header; + } + + public void setFooterHeight(Integer footerHeight) { + this.footerHeight = footerHeight; + } + +} diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/WebThemeUtils.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/WebThemeUtils.java new file mode 100644 index 000000000..e51644b9f --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/WebThemeUtils.java @@ -0,0 +1,28 @@ +package org.argeo.cms.web; + +import org.argeo.api.cms.CmsLog; +import org.argeo.api.cms.ux.CmsTheme; +import org.eclipse.rap.rwt.application.Application; +import org.eclipse.rap.rwt.service.ResourceLoader; + +/** Web specific utilities around theming. */ +public class WebThemeUtils { + private final static CmsLog log = CmsLog.getLog(WebThemeUtils.class); + + public static void apply(Application application, CmsTheme theme) { + ResourceLoader resourceLoader = new CmsThemeResourceLoader(theme); + resources: for (String path : theme.getImagesPaths()) { + if (path.startsWith("target/")) + continue resources; // skip maven output + application.addResource(path, resourceLoader); + if (log.isTraceEnabled()) + log.trace("Theme " + theme.getThemeId() + ": added resource " + path); + } + for (String path : theme.getRapCssPaths()) { + application.addStyleSheet(theme.getThemeId(), path, resourceLoader); + if (log.isDebugEnabled()) + log.debug("Theme " + theme.getThemeId() + ": added RAP CSS " + path); + } + } + +} diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/osgi/CmsWebAppFactory.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/osgi/CmsWebAppFactory.java new file mode 100644 index 000000000..19b9fe80d --- /dev/null +++ b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/osgi/CmsWebAppFactory.java @@ -0,0 +1,54 @@ +package org.argeo.cms.web.osgi; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; + +import org.argeo.api.cms.CmsApp; +import org.argeo.cms.web.CmsWebApp; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.service.event.EventAdmin; + +/** Publish a CmsApp as a RAP application. */ +public class CmsWebAppFactory { + private BundleContext bundleContext = FrameworkUtil.getBundle(CmsWebAppFactory.class).getBundleContext(); + private final static String CONTEXT_NAME = "contextName"; + + private EventAdmin eventAdmin; + + private Map registrations = Collections.synchronizedMap(new HashMap<>()); + + public void addCmsApp(CmsApp cmsApp, Map properties) { + String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY); + if (contextName != null) { + CmsWebApp cmsWebApp = new CmsWebApp(); + cmsWebApp.setEventAdmin(eventAdmin); + cmsWebApp.setCmsApp(cmsApp); + Hashtable serviceProperties = new Hashtable<>(); + if (!contextName.equals("")) + serviceProperties.put(CONTEXT_NAME, contextName); + cmsWebApp.init(bundleContext, serviceProperties); + registrations.put(contextName, cmsWebApp); + } + } + + public void removeCmsApp(CmsApp cmsApp, Map properties) { + String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY); + if (contextName != null) { + CmsWebApp cmsWebApp = registrations.get(contextName); + if (cmsWebApp != null) { + cmsWebApp.destroy(bundleContext, new HashMap<>()); + cmsWebApp.unsetCmsApp(cmsApp, properties); + } else { + // TODO log warning + } + } + } + + public void setEventAdmin(EventAdmin eventAdmin) { + this.eventAdmin = eventAdmin; + } + +} diff --git a/swt/rap/org.argeo.swt.specific.rap/.classpath b/swt/rap/org.argeo.swt.specific.rap/.classpath new file mode 100644 index 000000000..e03d341b1 --- /dev/null +++ b/swt/rap/org.argeo.swt.specific.rap/.classpath @@ -0,0 +1,9 @@ + + + + + + + diff --git a/swt/rap/org.argeo.swt.specific.rap/.project b/swt/rap/org.argeo.swt.specific.rap/.project new file mode 100644 index 000000000..53d797685 --- /dev/null +++ b/swt/rap/org.argeo.swt.specific.rap/.project @@ -0,0 +1,28 @@ + + + org.argeo.swt.specific.rap + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/swt/rap/org.argeo.swt.specific.rap/META-INF/.gitignore b/swt/rap/org.argeo.swt.specific.rap/META-INF/.gitignore new file mode 100644 index 000000000..4854a41b9 --- /dev/null +++ b/swt/rap/org.argeo.swt.specific.rap/META-INF/.gitignore @@ -0,0 +1 @@ +/MANIFEST.MF diff --git a/swt/rap/org.argeo.swt.specific.rap/bnd.bnd b/swt/rap/org.argeo.swt.specific.rap/bnd.bnd new file mode 100644 index 000000000..bcd9b195f --- /dev/null +++ b/swt/rap/org.argeo.swt.specific.rap/bnd.bnd @@ -0,0 +1,5 @@ +Import-Package: org.eclipse.swt,\ +org.eclipse.jface.dialogs,\ +org.eclipse.swt.events,\ +javax.servlet.http;version="[3,5)",\ +* diff --git a/swt/rap/org.argeo.swt.specific.rap/build.properties b/swt/rap/org.argeo.swt.specific.rap/build.properties new file mode 100644 index 000000000..fd806ca05 --- /dev/null +++ b/swt/rap/org.argeo.swt.specific.rap/build.properties @@ -0,0 +1,2 @@ +source.. = src/ +output.. = bin/ diff --git a/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/BufferedImageDisplay.java b/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/BufferedImageDisplay.java new file mode 100644 index 000000000..ac4e0dfb1 --- /dev/null +++ b/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/BufferedImageDisplay.java @@ -0,0 +1,20 @@ +package org.argeo.eclipse.ui.specific; + +import java.awt.image.BufferedImage; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; + +public class BufferedImageDisplay extends Composite { + private static final long serialVersionUID = 4541163690514461514L; + private BufferedImage image; + + public BufferedImageDisplay(Composite parent, int style) { + super(parent, SWT.NO_BACKGROUND); + } + + public void setImage(BufferedImage image) { + this.image = image; + } + +} diff --git a/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/CmsFileDialog.java b/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/CmsFileDialog.java new file mode 100644 index 000000000..6100c1a83 --- /dev/null +++ b/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/CmsFileDialog.java @@ -0,0 +1,17 @@ +package org.argeo.eclipse.ui.specific; + +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.Shell; + +public class CmsFileDialog extends FileDialog { + private static final long serialVersionUID = -7540791204102318801L; + + public CmsFileDialog(Shell parent, int style) { + super(parent, style); + } + + public CmsFileDialog(Shell parent) { + super(parent); + } + +} diff --git a/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/CmsFileUpload.java b/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/CmsFileUpload.java new file mode 100644 index 000000000..3f30bde25 --- /dev/null +++ b/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/CmsFileUpload.java @@ -0,0 +1,34 @@ +package org.argeo.eclipse.ui.specific; + +import org.eclipse.rap.rwt.widgets.FileUpload; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.widgets.Composite; + +public class CmsFileUpload extends FileUpload { + private static final long serialVersionUID = 8027963992680980549L; + + public CmsFileUpload(Composite parent, int style) { + super(parent, style); + } + + @Override + public void setText(String text) { + super.setText(text); + } + + @Override + public String getFileName() { + return super.getFileName(); + } + + @Override + public String[] getFileNames() { + return super.getFileNames(); + } + + @Override + public void addSelectionListener(SelectionListener listener) { + super.addSelectionListener(listener); + } + +} diff --git a/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java b/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java new file mode 100644 index 000000000..a89b921cd --- /dev/null +++ b/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java @@ -0,0 +1,39 @@ +package org.argeo.eclipse.ui.specific; + +import org.eclipse.jface.viewers.AbstractTableViewer; +import org.eclipse.jface.viewers.ColumnViewer; +import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.swt.widgets.Widget; + +/** Static utilities to bridge differences between RCP and RAP */ +public class EclipseUiSpecificUtils { + + public static void setStyleData(Widget widget, Object data) { + widget.setData(RWT.CUSTOM_VARIANT, data); + } + + public static Object getStyleData(Widget widget) { + return widget.getData(RWT.CUSTOM_VARIANT); + } + + public static void setMarkupData(Widget widget) { + widget.setData(RWT.MARKUP_ENABLED, true); + } + + public static void setMarkupValidationDisabledData(Widget widget) { + widget.setData("org.eclipse.rap.rwt.markupValidationDisabled", Boolean.TRUE); + } + + /** + * TootlTip support is supported only for {@link AbstractTableViewer} in RAP + */ + public static void enableToolTipSupport(Viewer viewer) { + if (viewer instanceof ColumnViewer) + ColumnViewerToolTipSupport.enableFor((ColumnViewer) viewer); + } + + private EclipseUiSpecificUtils() { + } +} diff --git a/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/FileDropAdapter.java b/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/FileDropAdapter.java new file mode 100644 index 000000000..f9ca81682 --- /dev/null +++ b/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/FileDropAdapter.java @@ -0,0 +1,73 @@ +package org.argeo.eclipse.ui.specific; + +import java.io.IOException; +import java.io.InputStream; + +import org.eclipse.rap.fileupload.FileDetails; +import org.eclipse.rap.fileupload.FileUploadHandler; +import org.eclipse.rap.fileupload.FileUploadReceiver; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.rap.rwt.client.ClientFile; +import org.eclipse.rap.rwt.client.service.ClientFileUploader; +import org.eclipse.rap.rwt.dnd.ClientFileTransfer; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.DropTarget; +import org.eclipse.swt.dnd.DropTargetAdapter; +import org.eclipse.swt.dnd.DropTargetEvent; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.widgets.Control; + +/** Configures a {@link Control} to receive files drop from the client OS. */ +public class FileDropAdapter { + + public void prepareDropTarget(Control control, DropTarget dropTarget) { + dropTarget.setTransfer(new Transfer[] { ClientFileTransfer.getInstance() }); + dropTarget.addDropListener(new DropTargetAdapter() { + private static final long serialVersionUID = 5361645765549463168L; + + @Override + public void dropAccept(DropTargetEvent event) { + if (!ClientFileTransfer.getInstance().isSupportedType(event.currentDataType)) { + event.detail = DND.DROP_NONE; + } + } + + @Override + public void drop(DropTargetEvent event) { + handleFileDrop(control, event); + } + }); + } + + public void handleFileDrop(Control control, DropTargetEvent event) { + ClientFile[] clientFiles = (ClientFile[]) event.data; + ClientFileUploader service = RWT.getClient().getService(ClientFileUploader.class); +// DiskFileUploadReceiver receiver = new DiskFileUploadReceiver(); + FileUploadReceiver receiver = new FileUploadReceiver() { + + @Override + public void receive(InputStream stream, FileDetails details) throws IOException { + control.getDisplay().syncExec(() -> { + try { + processUpload(stream, details.getFileName(), details.getContentType()); + } catch (IOException e) { + throw new IllegalStateException("Cannot process upload of " + details.getFileName(), e); + } + }); + } + }; + FileUploadHandler handler = new FileUploadHandler(receiver); +// handler.setMaxFileSize( sizeLimit ); +// handler.setUploadTimeLimit( timeLimit ); + service.submit(handler.getUploadUrl(), clientFiles); +// for (File file : receiver.getTargetFiles()) { +// paths.add(file.toPath()); +// } + } + + /** Executed in UI thread */ + protected void processUpload(InputStream in, String fileName, String contentType) throws IOException { + + } + +} diff --git a/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/UiContext.java b/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/UiContext.java new file mode 100644 index 000000000..72e17a22d --- /dev/null +++ b/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/UiContext.java @@ -0,0 +1,59 @@ +package org.argeo.eclipse.ui.specific; + +import java.util.Locale; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.rap.rwt.RWT; +import org.eclipse.swt.widgets.Display; + +/** Singleton class providing single sources infos about the UI context. */ +public class UiContext { + /** Can be null, thus indicating that we are not in a web context. */ + public static HttpServletRequest getHttpRequest() { + return RWT.getRequest(); + } + + public static HttpServletResponse getHttpResponse() { + return RWT.getResponse(); + } + + public static Locale getLocale() { + if (Display.getCurrent() != null) + return RWT.getUISession().getLocale(); + else + return Locale.getDefault(); + } + + public static void setLocale(Locale locale) { + if (Display.getCurrent() != null) + RWT.getUISession().setLocale(locale); + else + Locale.setDefault(locale); + } + + /** Can always be null */ + @SuppressWarnings("unchecked") + public static T getData(String key) { + Display display = getDisplay(); + if (display == null) + return null; + return (T) display.getData(key); + } + + public static void setData(String key, Object value) { + Display display = getDisplay(); + if (display == null) + throw new IllegalStateException("Not display available"); + display.setData(key, value); + } + + private static Display getDisplay() { + return Display.getCurrent(); + } + + private UiContext() { + } + +} diff --git a/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/package-info.java b/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/package-info.java new file mode 100644 index 000000000..4ec451f8a --- /dev/null +++ b/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/package-info.java @@ -0,0 +1,2 @@ +/** Eclipse RAP-specific SWT/JFace utilities, to simplify single-sourcing. */ +package org.argeo.eclipse.ui.specific; \ No newline at end of file diff --git a/swt/rcp/org.argeo.cms.e4.rcp/.classpath b/swt/rcp/org.argeo.cms.e4.rcp/.classpath new file mode 100644 index 000000000..eca7bdba8 --- /dev/null +++ b/swt/rcp/org.argeo.cms.e4.rcp/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/swt/rcp/org.argeo.cms.e4.rcp/.gitignore b/swt/rcp/org.argeo.cms.e4.rcp/.gitignore new file mode 100644 index 000000000..710cd6893 --- /dev/null +++ b/swt/rcp/org.argeo.cms.e4.rcp/.gitignore @@ -0,0 +1,3 @@ +/bin/ +/target/ +/exec diff --git a/swt/rcp/org.argeo.cms.e4.rcp/.project b/swt/rcp/org.argeo.cms.e4.rcp/.project new file mode 100644 index 000000000..64d561913 --- /dev/null +++ b/swt/rcp/org.argeo.cms.e4.rcp/.project @@ -0,0 +1,28 @@ + + + org.argeo.cms.e4.rcp + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/swt/rcp/org.argeo.cms.e4.rcp/.settings/org.eclipse.jdt.core.prefs b/swt/rcp/org.argeo.cms.e4.rcp/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..0c68a61dc --- /dev/null +++ b/swt/rcp/org.argeo.cms.e4.rcp/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/swt/rcp/org.argeo.cms.e4.rcp/.settings/org.eclipse.pde.core.prefs b/swt/rcp/org.argeo.cms.e4.rcp/.settings/org.eclipse.pde.core.prefs new file mode 100644 index 000000000..f29e940a0 --- /dev/null +++ b/swt/rcp/org.argeo.cms.e4.rcp/.settings/org.eclipse.pde.core.prefs @@ -0,0 +1,3 @@ +eclipse.preferences.version=1 +pluginProject.extensions=false +resolve.requirebundle=false diff --git a/swt/rcp/org.argeo.cms.e4.rcp/META-INF/.gitignore b/swt/rcp/org.argeo.cms.e4.rcp/META-INF/.gitignore new file mode 100644 index 000000000..4854a41b9 --- /dev/null +++ b/swt/rcp/org.argeo.cms.e4.rcp/META-INF/.gitignore @@ -0,0 +1 @@ +/MANIFEST.MF diff --git a/swt/rcp/org.argeo.cms.e4.rcp/argeo-companion.e4xmi b/swt/rcp/org.argeo.cms.e4.rcp/argeo-companion.e4xmi new file mode 100644 index 000000000..5b250eebf --- /dev/null +++ b/swt/rcp/org.argeo.cms.e4.rcp/argeo-companion.e4xmi @@ -0,0 +1,26 @@ + + + + + + + + + + + + editorArea + + + + + + + + + + + + + + diff --git a/swt/rcp/org.argeo.cms.e4.rcp/argeo-companion.properties b/swt/rcp/org.argeo.cms.e4.rcp/argeo-companion.properties new file mode 100644 index 000000000..0a0da7581 --- /dev/null +++ b/swt/rcp/org.argeo.cms.e4.rcp/argeo-companion.properties @@ -0,0 +1,23 @@ +argeo.osgi.start.2.node=\ +org.eclipse.equinox.http.servlet,\ +org.eclipse.equinox.metatype,\ +org.eclipse.equinox.cm,\ +org.argeo.init + +argeo.osgi.start.3.node=\ +org.argeo.cms,\ +org.argeo.cms.jcr,\ + +applicationXMI=org.argeo.cms.e4.rcp/argeo-companion.e4xmi +lifeCycleURI=bundleclass://org.argeo.cms.e4.rcp/org.argeo.cms.e4.rcp.CmsRcpLifeCycle +clearPersistedState=true +#argeo.cms.desktop.inTray=true + +# Remote node: +#argeo.node.repo.labeledUri=http://root:demo@localhost:7070/jcr/node + +# Logging +log.org.argeo=DEBUG + +argeo.node.useradmin.uris=os:/// +eclipse.application=org.argeo.cms.e4.rcp.CmsE4Application diff --git a/swt/rcp/org.argeo.cms.e4.rcp/bnd.bnd b/swt/rcp/org.argeo.cms.e4.rcp/bnd.bnd new file mode 100644 index 000000000..ff79c8041 --- /dev/null +++ b/swt/rcp/org.argeo.cms.e4.rcp/bnd.bnd @@ -0,0 +1,7 @@ +Bundle-SymbolicName: org.argeo.cms.e4.rcp;singleton=true + +Require-Bundle: org.eclipse.core.runtime + +Import-Package: !org.eclipse.core.runtime,\ +org.eclipse.swt,\ +* diff --git a/swt/rcp/org.argeo.cms.e4.rcp/build.properties b/swt/rcp/org.argeo.cms.e4.rcp/build.properties new file mode 100644 index 000000000..355413e4f --- /dev/null +++ b/swt/rcp/org.argeo.cms.e4.rcp/build.properties @@ -0,0 +1,5 @@ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + argeo-companion.e4xmi +source.. = src/ diff --git a/swt/rcp/org.argeo.cms.e4.rcp/log4j.properties b/swt/rcp/org.argeo.cms.e4.rcp/log4j.properties new file mode 100644 index 000000000..13f949ff5 --- /dev/null +++ b/swt/rcp/org.argeo.cms.e4.rcp/log4j.properties @@ -0,0 +1,32 @@ +log4j.rootLogger=WARN, development + +## Levels +log4j.logger.org.argeo=DEBUG +log4j.logger.org.argeo.jackrabbit.remote.ExtendedDispatcherServlet=WARN +log4j.logger.org.argeo.server.webextender.TomcatDeployer=INFO + +#log4j.logger.org.springframework.security=DEBUG +#log4j.logger.org.apache.commons.exec=DEBUG +#log4j.logger.org.apache.jackrabbit.webdav=DEBUG +#log4j.logger.org.apache.jackrabbit.remote=DEBUG +#log4j.logger.org.apache.jackrabbit.core.observation=DEBUG + +log4j.logger.org.apache.catalina=INFO +log4j.logger.org.apache.coyote=INFO + +log4j.logger.org.apache.directory=INFO +log4j.logger.org.apache.directory.server=ERROR +log4j.logger.org.apache.jackrabbit.core.query.lucene=ERROR + +## Appenders +# console is set to be a ConsoleAppender. +log4j.appender.console=org.apache.log4j.ConsoleAppender + +# console uses PatternLayout. +log4j.appender.console.layout=org.apache.log4j.PatternLayout +log4j.appender.console.layout.ConversionPattern= %-5p %d{ISO8601} %m - %c - [%t]%n + +# development appender (slow!) +log4j.appender.development=org.apache.log4j.ConsoleAppender +log4j.appender.development.layout=org.apache.log4j.PatternLayout +log4j.appender.development.layout.ConversionPattern=%d{HH:mm:ss} [%16.16t] %5p %m (%F:%L) %c%n diff --git a/swt/rcp/org.argeo.cms.e4.rcp/plugin.xml b/swt/rcp/org.argeo.cms.e4.rcp/plugin.xml new file mode 100644 index 000000000..3e6896beb --- /dev/null +++ b/swt/rcp/org.argeo.cms.e4.rcp/plugin.xml @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/swt/rcp/org.argeo.cms.e4.rcp/src/org/argeo/cms/e4/rcp/CmsE4Application.java b/swt/rcp/org.argeo.cms.e4.rcp/src/org/argeo/cms/e4/rcp/CmsE4Application.java new file mode 100644 index 000000000..b37a76587 --- /dev/null +++ b/swt/rcp/org.argeo.cms.e4.rcp/src/org/argeo/cms/e4/rcp/CmsE4Application.java @@ -0,0 +1,207 @@ +package org.argeo.cms.e4.rcp; + +import java.security.PrivilegedExceptionAction; +import java.util.UUID; + +import javax.security.auth.Subject; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; + +import org.argeo.api.cms.CmsAuth; +import org.argeo.api.cms.ux.CmsImageManager; +import org.argeo.api.cms.ux.CmsView; +import org.argeo.api.cms.ux.UxContext; +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.swt.CmsException; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.swt.SimpleSwtUxContext; +import org.argeo.cms.swt.auth.CmsLoginShell; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtension; +import org.eclipse.core.runtime.Platform; +import org.eclipse.equinox.app.IApplication; +import org.eclipse.equinox.app.IApplicationContext; +import org.eclipse.swt.widgets.Display; + +public class CmsE4Application implements IApplication, CmsView { + private LoginContext loginContext; + private IApplication e4Application; + private UxContext uxContext; + private String uid; + + @Override + public Object start(IApplicationContext context) throws Exception { + // TODO wait for CMS to be ready + Thread.sleep(5000); + + uid = UUID.randomUUID().toString(); + Subject subject = new Subject(); + Display display = createDisplay(); + CmsLoginShell loginShell = new CmsLoginShell(this, null); + // TODO customize CmsLoginShell to be smaller and centered + loginShell.setSubject(subject); + try { + // try pre-auth + loginContext = new LoginContext(CmsAuth.LOGIN_CONTEXT_SINGLE_USER, subject, loginShell); + loginContext.login(); + } catch (LoginException e) { + e.printStackTrace(); + loginShell.createUi(); + loginShell.open(); + + while (!loginShell.getShell().isDisposed()) { + if (!display.readAndDispatch()) + display.sleep(); + } + } + if (CurrentUser.getUsername(getSubject()) == null) + throw new IllegalStateException("Cannot log in"); + + // try { + // CallbackHandler callbackHandler = new DefaultLoginDialog( + // display.getActiveShell()); + // loginContext = new LoginContext( + // NodeConstants.LOGIN_CONTEXT_SINGLE_USER, subject, + // callbackHandler); + // } catch (LoginException e1) { + // throw new CmsException("Cannot initialize login context", e1); + // } + // + // // login + // try { + // loginContext.login(); + // subject = loginContext.getSubject(); + // } catch (LoginException e) { + // e.printStackTrace(); + // display.dispose(); + // try { + // Thread.sleep(2000); + // } catch (InterruptedException e1) { + // // silent + // } + // return null; + // } + + uxContext = new SimpleSwtUxContext(); + // UiContext.setData(CmsView.KEY, this); + CmsSwtUtils.registerCmsView(loginShell.getShell(), this); + e4Application = getApplication(null); + Object res = Subject.doAs(subject, new PrivilegedExceptionAction() { + + @Override + public Object run() throws Exception { + return e4Application.start(context); + } + + }); + return res; + } + + @Override + public void stop() { + if (e4Application != null) + e4Application.stop(); + } + + static IApplication getApplication(String[] args) { + IExtension extension = Platform.getExtensionRegistry().getExtension(Platform.PI_RUNTIME, + Platform.PT_APPLICATIONS, "org.eclipse.e4.ui.workbench.swt.E4Application"); + try { + IConfigurationElement[] elements = extension.getConfigurationElements(); + if (elements.length > 0) { + IConfigurationElement[] runs = elements[0].getChildren("run"); + if (runs.length > 0) { + Object runnable; + runnable = runs[0].createExecutableExtension("class"); + if (runnable instanceof IApplication) + return (IApplication) runnable; + } + } + } catch (Exception e) { + throw new IllegalStateException("Cannot find e4 application", e); + } + throw new IllegalStateException("Cannot find e4 application"); + } + + public static Display createDisplay() { + Display.setAppName("Argeo CMS RCP"); + + // create the display + Display newDisplay = Display.getCurrent(); + if (newDisplay == null) { + newDisplay = new Display(); + } + // Set the priority higher than normal so as to be higher + // than the JobManager. + Thread.currentThread().setPriority(Math.min(Thread.MAX_PRIORITY, Thread.NORM_PRIORITY + 1)); + return newDisplay; + } + + // + // CMS VIEW + // + + @Override + public UxContext getUxContext() { + return uxContext; + } + + @Override + public void navigateTo(String state) { + // TODO Auto-generated method stub + + } + + @Override + public void authChange(LoginContext loginContext) { + if (loginContext == null) + throw new CmsException("Login context cannot be null"); + // logout previous login context + // if (this.loginContext != null) + // try { + // this.loginContext.logout(); + // } catch (LoginException e1) { + // System.err.println("Could not log out: " + e1); + // } + this.loginContext = loginContext; + } + + @Override + public void logout() { + if (loginContext == null) + throw new CmsException("Login context should not bet null"); + try { + CurrentUser.logoutCmsSession(loginContext.getSubject()); + loginContext.logout(); + } catch (LoginException e) { + throw new CmsException("Cannot log out", e); + } + } + + @Override + public void exception(Throwable e) { + // TODO Auto-generated method stub + + } + + @Override + public CmsImageManager getImageManager() { + // TODO Auto-generated method stub + return null; + } + + protected Subject getSubject() { + return loginContext.getSubject(); + } + + @Override + public boolean isAnonymous() { + return CurrentUser.isAnonymous(getSubject()); + } + + @Override + public String getUid() { + return uid; + } + +} diff --git a/swt/rcp/org.argeo.cms.e4.rcp/src/org/argeo/cms/e4/rcp/CmsRcpLifeCycle.java b/swt/rcp/org.argeo.cms.e4.rcp/src/org/argeo/cms/e4/rcp/CmsRcpLifeCycle.java new file mode 100644 index 000000000..1d38fe718 --- /dev/null +++ b/swt/rcp/org.argeo.cms.e4.rcp/src/org/argeo/cms/e4/rcp/CmsRcpLifeCycle.java @@ -0,0 +1,27 @@ +package org.argeo.cms.e4.rcp; + +import org.eclipse.e4.core.contexts.IEclipseContext; +import org.eclipse.e4.ui.workbench.lifecycle.PostContextCreate; +import org.eclipse.e4.ui.workbench.lifecycle.PreSave; +import org.eclipse.e4.ui.workbench.lifecycle.ProcessAdditions; +import org.eclipse.e4.ui.workbench.lifecycle.ProcessRemovals; + +@SuppressWarnings("restriction") +public class CmsRcpLifeCycle { + + @PostContextCreate + void postContextCreate(IEclipseContext workbenchContext) { + } + + @PreSave + void preSave(IEclipseContext workbenchContext) { + } + + @ProcessAdditions + void processAdditions(IEclipseContext workbenchContext) { + } + + @ProcessRemovals + void processRemovals(IEclipseContext workbenchContext) { + } +} diff --git a/swt/rcp/org.argeo.cms.swt.rcp.cli/.classpath b/swt/rcp/org.argeo.cms.swt.rcp.cli/.classpath new file mode 100644 index 000000000..81fe078c2 --- /dev/null +++ b/swt/rcp/org.argeo.cms.swt.rcp.cli/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/swt/rcp/org.argeo.cms.swt.rcp.cli/.project b/swt/rcp/org.argeo.cms.swt.rcp.cli/.project new file mode 100644 index 000000000..07c8c5d91 --- /dev/null +++ b/swt/rcp/org.argeo.cms.swt.rcp.cli/.project @@ -0,0 +1,28 @@ + + + org.argeo.cms.swt.rcp.cli + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/swt/rcp/org.argeo.cms.swt.rcp.cli/bnd.bnd b/swt/rcp/org.argeo.cms.swt.rcp.cli/bnd.bnd new file mode 100644 index 000000000..e69de29bb diff --git a/swt/rcp/org.argeo.cms.swt.rcp.cli/build.properties b/swt/rcp/org.argeo.cms.swt.rcp.cli/build.properties new file mode 100644 index 000000000..34d2e4d2d --- /dev/null +++ b/swt/rcp/org.argeo.cms.swt.rcp.cli/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/swt/rcp/org.argeo.cms.swt.rcp.cli/src/org/argeo/cms/swt/rcp/cli/CmsCli.java b/swt/rcp/org.argeo.cms.swt.rcp.cli/src/org/argeo/cms/swt/rcp/cli/CmsCli.java new file mode 100644 index 000000000..187f5b03e --- /dev/null +++ b/swt/rcp/org.argeo.cms.swt.rcp.cli/src/org/argeo/cms/swt/rcp/cli/CmsCli.java @@ -0,0 +1,110 @@ +package org.argeo.cms.swt.rcp.cli; + +import java.lang.management.ManagementFactory; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.concurrent.ForkJoinPool; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.argeo.api.acr.spi.ProvidedRepository; +import org.argeo.api.cli.CommandsCli; +import org.argeo.api.cli.DescribedCommand; +import org.argeo.api.cms.CmsApp; +import org.argeo.cms.runtime.StaticCms; +import org.argeo.cms.swt.app.CmsUserApp; +import org.argeo.cms.ui.rcp.CmsRcpDisplayFactory; +import org.argeo.util.register.Component; +import org.argeo.util.register.ComponentRegister; + +public class CmsCli extends CommandsCli { + + public CmsCli(String commandName) { + super(commandName); + addCommand("static", new Launch()); + } + + @Override + public String getDescription() { + return "Argeo CMS utilities."; + } + + static class Launch implements DescribedCommand { + private Option dataOption; + private Option uiOption; + + @Override + public Options getOptions() { + Options options = new Options(); + dataOption = Option.builder().longOpt("data").hasArg().required() + .desc("path to the writable data area (mandatory)").build(); + uiOption = Option.builder().longOpt("ui").desc("open a user interface").build(); + options.addOption(dataOption); + options.addOption(uiOption); + return options; + } + + @Override + public String apply(List args) { + CommandLine cl = toCommandLine(args); + String dataPath = cl.getOptionValue(dataOption); + boolean ui = cl.hasOption(uiOption); + + Path instancePath = Paths.get(dataPath); + System.setProperty("osgi.instance.area", instancePath.toUri().toString()); + + StaticCms staticCms = new StaticCms() { + @Override + protected void addComponents(ComponentRegister register) { + if (ui) { + Component contentRepositoryC = register + .find(ProvidedRepository.class, null).first(); + CmsUserApp cmsApp = new CmsUserApp(); + Component cmsAppC = new Component.Builder<>(cmsApp) // + .addType(CmsApp.class) // + .addType(CmsUserApp.class) // + .addDependency(contentRepositoryC.getType(ProvidedRepository.class), + cmsApp::setContentRepository, null) // + .build(register); + + CmsRcpDisplayFactory displayFactory = new CmsRcpDisplayFactory(); + Component displayFactoryC = new Component.Builder<>(displayFactory) // + .addActivation(displayFactory::init) // + .addDeactivation(displayFactory::destroy) // + .build(register); + + } + } + + @Override + protected void postActivation(ComponentRegister register) { + if (ui) { + Component cmsAppC = register.find(CmsUserApp.class, null).first(); + CmsRcpDisplayFactory.openCmsApp(null, cmsAppC.get(), "data", (e) -> { + // asynchronous in order to avoid deadlock in UI thread + ForkJoinPool.commonPool().execute(() -> stop()); + }); + } + } + + }; + Runtime.getRuntime().addShutdownHook(new Thread(() -> staticCms.stop(), "Static CMS Shutdown")); + staticCms.start(); + + long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime(); + System.out.println("Static CMS available in " + jvmUptime + " ms."); + + staticCms.waitForStop(); + + return null; + } + + @Override + public String getDescription() { + return "Launch a static CMS."; + } + + } +} diff --git a/swt/rcp/org.argeo.cms.swt.rcp/.classpath b/swt/rcp/org.argeo.cms.swt.rcp/.classpath new file mode 100644 index 000000000..e801ebfb4 --- /dev/null +++ b/swt/rcp/org.argeo.cms.swt.rcp/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/swt/rcp/org.argeo.cms.swt.rcp/.gitignore b/swt/rcp/org.argeo.cms.swt.rcp/.gitignore new file mode 100644 index 000000000..09e3bc9b2 --- /dev/null +++ b/swt/rcp/org.argeo.cms.swt.rcp/.gitignore @@ -0,0 +1,2 @@ +/bin/ +/target/ diff --git a/swt/rcp/org.argeo.cms.swt.rcp/.project b/swt/rcp/org.argeo.cms.swt.rcp/.project new file mode 100644 index 000000000..c9c2a44bf --- /dev/null +++ b/swt/rcp/org.argeo.cms.swt.rcp/.project @@ -0,0 +1,33 @@ + + + org.argeo.cms.ui.rcp + + + + + + 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/swt/rcp/org.argeo.cms.swt.rcp/META-INF/.gitignore b/swt/rcp/org.argeo.cms.swt.rcp/META-INF/.gitignore new file mode 100644 index 000000000..4854a41b9 --- /dev/null +++ b/swt/rcp/org.argeo.cms.swt.rcp/META-INF/.gitignore @@ -0,0 +1 @@ +/MANIFEST.MF diff --git a/swt/rcp/org.argeo.cms.swt.rcp/OSGI-INF/cmsRcpDisplayFactory.xml b/swt/rcp/org.argeo.cms.swt.rcp/OSGI-INF/cmsRcpDisplayFactory.xml new file mode 100644 index 000000000..a0c0f0f5c --- /dev/null +++ b/swt/rcp/org.argeo.cms.swt.rcp/OSGI-INF/cmsRcpDisplayFactory.xml @@ -0,0 +1,4 @@ + + + + diff --git a/swt/rcp/org.argeo.cms.swt.rcp/OSGI-INF/cmsRcpServletFactory.xml b/swt/rcp/org.argeo.cms.swt.rcp/OSGI-INF/cmsRcpServletFactory.xml new file mode 100644 index 000000000..a1f0b3a2a --- /dev/null +++ b/swt/rcp/org.argeo.cms.swt.rcp/OSGI-INF/cmsRcpServletFactory.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/swt/rcp/org.argeo.cms.swt.rcp/bnd.bnd b/swt/rcp/org.argeo.cms.swt.rcp/bnd.bnd new file mode 100644 index 000000000..91f0a8a37 --- /dev/null +++ b/swt/rcp/org.argeo.cms.swt.rcp/bnd.bnd @@ -0,0 +1,12 @@ + +Import-Package:\ +org.argeo.cms.auth,\ +org.eclipse.swt,\ +org.eclipse.swt.graphics,\ +org.w3c.css.sac,\ +* + +Service-Component:\ +OSGI-INF/cmsRcpDisplayFactory.xml,\ +OSGI-INF/cmsRcpServletFactory.xml + diff --git a/swt/rcp/org.argeo.cms.swt.rcp/build.properties b/swt/rcp/org.argeo.cms.swt.rcp/build.properties new file mode 100644 index 000000000..5eef7058e --- /dev/null +++ b/swt/rcp/org.argeo.cms.swt.rcp/build.properties @@ -0,0 +1,6 @@ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + OSGI-INF/,\ + OSGI-INF/cmsRcpServletFactory.xml +source.. = src/ diff --git a/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/CmsRcpApp.java b/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/CmsRcpApp.java new file mode 100644 index 000000000..e25a9f711 --- /dev/null +++ b/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/CmsRcpApp.java @@ -0,0 +1,230 @@ +package org.argeo.cms.ui.rcp; + +import java.io.IOException; +import java.io.InputStream; +import java.security.PrivilegedAction; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import javax.security.auth.Subject; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; + +import org.argeo.api.cms.CmsApp; +import org.argeo.api.cms.CmsAuth; +import org.argeo.api.cms.CmsLog; +import org.argeo.api.cms.CmsSession; +import org.argeo.api.cms.ux.CmsImageManager; +import org.argeo.api.cms.ux.CmsTheme; +import org.argeo.api.cms.ux.CmsUi; +import org.argeo.api.cms.ux.CmsView; +import org.argeo.api.cms.ux.UxContext; +import org.argeo.cms.swt.CmsSwtUtils; +import org.eclipse.e4.ui.css.core.engine.CSSEngine; +import org.eclipse.e4.ui.css.core.engine.CSSErrorHandler; +import org.eclipse.e4.ui.css.swt.engine.CSSSWTEngineImpl; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.osgi.service.event.Event; +import org.osgi.service.event.EventAdmin; + +/** Runs a {@link CmsApp} as an SWT desktop application. */ +@SuppressWarnings("restriction") +public class CmsRcpApp implements CmsView { + private final static CmsLog log = CmsLog.getLog(CmsRcpApp.class); + + // private BundleContext bundleContext = + // FrameworkUtil.getBundle(CmsRcpApp.class).getBundleContext(); + + private Shell shell; + private CmsApp cmsApp; + + // CMS View + private String uid; + private LoginContext loginContext; + + private EventAdmin eventAdmin; + + private CSSEngine cssEngine; + + private CmsUi ui; + // TODO make it configurable + private String uiName = "desktop"; + + public CmsRcpApp(String uiName) { + uid = UUID.randomUUID().toString(); + this.uiName = uiName; + } + + public void initRcpApp() { + Display display = Display.getCurrent(); + shell = new Shell(display); + shell.setText("Argeo CMS"); + Composite parent = shell; + parent.setLayout(CmsSwtUtils.noSpaceGridLayout()); + CmsSwtUtils.registerCmsView(shell, CmsRcpApp.this); + + try { + loginContext = new LoginContext(CmsAuth.SINGLE_USER.getLoginContextName()); + loginContext.login(); + } catch (LoginException e) { + throw new IllegalStateException("Could not log in.", e); + } + if (log.isDebugEnabled()) + log.debug("Logged in to desktop: " + loginContext.getSubject()); + + Subject.doAs(loginContext.getSubject(), (PrivilegedAction) () -> { + + // TODO factorise with web app + parent.setData(CmsApp.UI_NAME_PROPERTY, uiName); + ui = cmsApp.initUi(parent); + if (ui instanceof Composite) + ((Composite) ui).setLayoutData(CmsSwtUtils.fillAll()); + // we need ui to be set before refresh so that CmsView can store UI context data + // in it. + cmsApp.refreshUi(ui, null); + + // Styling + CmsTheme theme = CmsSwtUtils.getCmsTheme(parent); + if (theme != null) { + cssEngine = new CSSSWTEngineImpl(display); + for (String path : theme.getSwtCssPaths()) { + try (InputStream in = theme.loadPath(path)) { + cssEngine.parseStyleSheet(in); + } catch (IOException e) { + throw new IllegalStateException("Cannot load stylesheet " + path, e); + } + } + cssEngine.setErrorHandler(new CSSErrorHandler() { + public void error(Exception e) { + log.error("SWT styling error: ", e); + } + }); + applyStyles(shell); + } + shell.layout(true, true); + + shell.open(); + return null; + }); + } + + /* + * CMS VIEW + */ + + @Override + public String getUid() { + return uid; + } + + @Override + public UxContext getUxContext() { + throw new UnsupportedOperationException(); + } + + @Override + public void navigateTo(String state) { + throw new UnsupportedOperationException(); + } + + @Override + public void authChange(LoginContext loginContext) { + } + + @Override + public void logout() { + if (loginContext != null) + try { + loginContext.logout(); + } catch (LoginException e) { + log.error("Cannot log out", e); + } + } + + @Override + public void exception(Throwable e) { + log.error("Unexpected exception in CMS RCP", e); + } + + @Override + public CmsImageManager getImageManager() { + throw new UnsupportedOperationException(); + } + + @Override + public CmsSession getCmsSession() { + CmsSession cmsSession = cmsApp.getCmsContext().getCmsSession(getSubject()); + return cmsSession; + } + + @Override + public Object getData(String key) { + if (ui != null) { + return ui.getData(key); + } else { + throw new IllegalStateException("UI is not initialized"); + } + } + + @Override + public void setData(String key, Object value) { + if (ui != null) { + ui.setData(key, value); + } else { + throw new IllegalStateException("UI is not initialized"); + } + } + + @Override + public boolean isAnonymous() { + return false; + } + + @Override + public void applyStyles(Object node) { + if (cssEngine != null) + cssEngine.applyStyles(node, true); + } + + @Override + public void sendEvent(String topic, Map properties) { + if (properties == null) + properties = new HashMap<>(); + if (properties.containsKey(CMS_VIEW_UID_PROPERTY) && !properties.get(CMS_VIEW_UID_PROPERTY).equals(uid)) + throw new IllegalArgumentException("Property " + CMS_VIEW_UID_PROPERTY + " is set to another CMS view uid (" + + properties.get(CMS_VIEW_UID_PROPERTY) + ") then " + uid); + properties.put(CMS_VIEW_UID_PROPERTY, uid); + eventAdmin.sendEvent(new Event(topic, properties)); + } + + public T doAs(PrivilegedAction action) { + return Subject.doAs(getSubject(), action); + } + + protected Subject getSubject() { + return loginContext.getSubject(); + } + + public Shell getShell() { + return shell; + } + + /* + * DEPENDENCY INJECTION + */ + public void setCmsApp(CmsApp cmsApp, Map properties) { + this.cmsApp = cmsApp; + } + + public void unsetCmsApp(CmsApp cmsApp, Map properties) { + this.cmsApp = null; + } + + public void setEventAdmin(EventAdmin eventAdmin) { + this.eventAdmin = eventAdmin; + } + +} diff --git a/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/CmsRcpDisplayFactory.java b/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/CmsRcpDisplayFactory.java new file mode 100644 index 000000000..ec471c021 --- /dev/null +++ b/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/CmsRcpDisplayFactory.java @@ -0,0 +1,90 @@ +package org.argeo.cms.ui.rcp; + +import java.nio.file.Path; + +import org.argeo.api.cms.CmsApp; +import org.argeo.util.OS; +import org.eclipse.swt.widgets.Display; +import org.osgi.service.event.EventAdmin; +import org.eclipse.swt.events.DisposeListener; + +/** Creates the SWT {@link Display} in a dedicated thread. */ +public class CmsRcpDisplayFactory { + /** File name in a run directory */ + private final static String ARGEO_RCP_URL = "argeo.rcp.url"; + + /** There is only one display in RCP mode */ + private static Display display; + + private CmsUiThread uiThread; + + private boolean shutdown = false; + + public void init() { + uiThread = new CmsUiThread(); + uiThread.start(); + while (display == null) + try { + Thread.sleep(100); + } catch (InterruptedException e) { + // silent + } + } + + public void destroy() { + shutdown = true; + display.wake(); + try { + uiThread.join(); + } catch (InterruptedException e) { + // silent + } finally { + uiThread = null; + } + } + + class CmsUiThread extends Thread { + + public CmsUiThread() { + super("CMS UI"); + } + + @Override + public void run() { + display = Display.getDefault(); + display.setRuntimeExceptionHandler((e) -> e.printStackTrace()); + display.setErrorHandler((e) -> e.printStackTrace()); + +// for (String contextName : cmsApps.keySet()) { +// openCmsApp(contextName); +// } + + while (!shutdown) { + if (!display.readAndDispatch()) + display.sleep(); + } + display.dispose(); + display = null; + } + } + + public static Display getDisplay() { + return display; + } + + public static void openCmsApp(EventAdmin eventAdmin, CmsApp cmsApp, String uiName, + DisposeListener disposeListener) { + CmsRcpDisplayFactory.getDisplay().syncExec(() -> { + CmsRcpApp cmsRcpApp = new CmsRcpApp(uiName); + cmsRcpApp.setEventAdmin(eventAdmin); + cmsRcpApp.setCmsApp(cmsApp, null); + cmsRcpApp.initRcpApp(); + if (disposeListener != null) + cmsRcpApp.getShell().addDisposeListener(disposeListener); + }); + } + + public static Path getUrlRunFile() { + return OS.getRunDir().resolve(CmsRcpDisplayFactory.ARGEO_RCP_URL); + } +} diff --git a/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServlet.java b/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServlet.java new file mode 100644 index 000000000..f8aecd39b --- /dev/null +++ b/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServlet.java @@ -0,0 +1,40 @@ +package org.argeo.cms.ui.rcp.servlet; + +import java.io.IOException; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; +import java.util.Objects; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.argeo.api.cms.CmsApp; +import org.argeo.cms.ui.rcp.CmsRcpDisplayFactory; +import org.osgi.service.event.EventAdmin; + +/** Open the related app when called. */ +public class CmsRcpServlet extends HttpServlet { + private static final long serialVersionUID = -3944472431354848923L; + private final static Logger logger = System.getLogger(CmsRcpServlet.class.getName()); + + private CmsApp cmsApp; + private EventAdmin eventAdmin; + + public CmsRcpServlet(EventAdmin eventAdmin, CmsApp cmsApp) { + Objects.requireNonNull(eventAdmin); + Objects.requireNonNull(cmsApp); + this.cmsApp = cmsApp; + this.eventAdmin = eventAdmin; + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String path = req.getPathInfo(); + String uiName = path != null ? path.substring(path.lastIndexOf('/') + 1) : ""; + CmsRcpDisplayFactory.openCmsApp(eventAdmin, cmsApp, uiName, null); + logger.log(Level.DEBUG, "Opened RCP UI " + uiName + " of CMS App " + req.getServletPath()); + } + +} diff --git a/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServletFactory.java b/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServletFactory.java new file mode 100644 index 000000000..7c24f87d9 --- /dev/null +++ b/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServletFactory.java @@ -0,0 +1,131 @@ +package org.argeo.cms.ui.rcp.servlet; + +import java.io.IOException; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; +import java.net.DatagramSocket; +import java.net.ServerSocket; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import javax.servlet.Servlet; + +import org.argeo.api.cms.CmsApp; +import org.argeo.cms.ui.rcp.CmsRcpDisplayFactory; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.event.EventAdmin; +import org.osgi.service.http.HttpService; + +/** Publishes one {@link CmsRcpServlet} per {@link CmsApp}. */ +public class CmsRcpServletFactory { + private final static Logger logger = System.getLogger(CmsRcpServletFactory.class.getName()); + + private BundleContext bundleContext = FrameworkUtil.getBundle(CmsRcpServletFactory.class).getBundleContext(); + + private CompletableFuture eventAdmin = new CompletableFuture<>(); + + private Map> registrations = Collections.synchronizedMap(new HashMap<>()); + + public void init() { + + } + + public void destroy() { + Path runFile = CmsRcpDisplayFactory.getUrlRunFile(); + try { + if (Files.exists(runFile)) { + Files.delete(runFile); + } + } catch (IOException e) { + logger.log(Level.ERROR, "Cannot delete " + runFile, e); + } + } + + public void addCmsApp(CmsApp cmsApp, Map properties) { + String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY); + if (contextName != null) { + eventAdmin.thenAccept((eventAdmin) -> { + CmsRcpServlet servlet = new CmsRcpServlet(eventAdmin, cmsApp); + Hashtable serviceProperties = new Hashtable<>(); + serviceProperties.put("osgi.http.whiteboard.servlet.pattern", "/" + contextName + "/*"); + ServiceRegistration sr = bundleContext.registerService(Servlet.class, servlet, + serviceProperties); + registrations.put(contextName, sr); + }); + } + } + + public void removeCmsApp(CmsApp cmsApp, Map properties) { + String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY); + if (contextName != null) { + ServiceRegistration sr = registrations.get(contextName); + sr.unregister(); + } + } + + public void setEventAdmin(EventAdmin eventAdmin) { + this.eventAdmin.complete(eventAdmin); + } + + public void setHttpService(HttpService httpService, Map properties) { + Integer httpPort = Integer.parseInt(properties.get("http.port").toString()); + String baseUrl = "http://localhost:" + httpPort + "/"; + Path runFile = CmsRcpDisplayFactory.getUrlRunFile(); + try { + if (!Files.exists(runFile)) { + Files.createDirectories(runFile.getParent()); + // TODO give read permission only to the owner + Files.createFile(runFile); + } else { + URI uri = URI.create(Files.readString(runFile)); + if (!httpPort.equals(uri.getPort())) + if (!isPortAvailable(uri.getPort())) { + throw new IllegalStateException("Another CMS is running on " + runFile); + } else { + logger.log(Level.WARNING, + "Run file " + runFile + " found but port of " + uri + " is available. Overwriting..."); + } + } + Files.writeString(runFile, baseUrl, StandardCharsets.UTF_8); + } catch (IOException e) { + throw new RuntimeException("Cannot write run file to " + runFile, e); + } + logger.log(Level.DEBUG, "RCP available under " + baseUrl + ", written to " + runFile); + } + + protected boolean isPortAvailable(int port) { + ServerSocket ss = null; + DatagramSocket ds = null; + try { + ss = new ServerSocket(port); + ss.setReuseAddress(true); + ds = new DatagramSocket(port); + ds.setReuseAddress(true); + return true; + } catch (IOException e) { + } finally { + if (ds != null) { + ds.close(); + } + + if (ss != null) { + try { + ss.close(); + } catch (IOException e) { + /* should not be thrown */ + } + } + } + + return false; + } +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/.classpath b/swt/rcp/org.argeo.swt.specific.rcp/.classpath new file mode 100644 index 000000000..457b11571 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/.classpath @@ -0,0 +1,9 @@ + + + + + + + diff --git a/swt/rcp/org.argeo.swt.specific.rcp/.gitignore b/swt/rcp/org.argeo.swt.specific.rcp/.gitignore new file mode 100644 index 000000000..5e77890b2 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/.gitignore @@ -0,0 +1,3 @@ +/target/ +/bin/ +*.log \ No newline at end of file diff --git a/swt/rcp/org.argeo.swt.specific.rcp/.project b/swt/rcp/org.argeo.swt.specific.rcp/.project new file mode 100644 index 000000000..c79ee3fc9 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/.project @@ -0,0 +1,28 @@ + + + org.argeo.swt.specific.rcp + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/swt/rcp/org.argeo.swt.specific.rcp/META-INF/.gitignore b/swt/rcp/org.argeo.swt.specific.rcp/META-INF/.gitignore new file mode 100644 index 000000000..4854a41b9 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/META-INF/.gitignore @@ -0,0 +1 @@ +/MANIFEST.MF diff --git a/swt/rcp/org.argeo.swt.specific.rcp/bnd.bnd b/swt/rcp/org.argeo.swt.specific.rcp/bnd.bnd new file mode 100644 index 000000000..bb88efda7 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/bnd.bnd @@ -0,0 +1,20 @@ +Import-Package: \ +!java.*,\ +org.apache.commons.io,\ +org.eclipse.core.commands,\ +!org.eclipse.core.runtime,\ +!org.eclipse.ui.plugin,\ +org.eclipse.swt,\ +javax.servlet.http;version="[3,5)",\ +javax.servlet;version="[3,5)",\ +* + +Export-Package: org.argeo.*,\ +org.eclipse.rap.fileupload.*;version="3.10",\ +org.eclipse.rap.rwt.*;version="3.10" + +# Was !org.eclipse.core.commands,\ why ? + +#Bundle-Activator: org.argeo.eclipse.ui.ArgeoUiPlugin +#Bundle-ActivationPolicy: lazy +#Ignore-Package: org.eclipse.core.commands \ No newline at end of file diff --git a/swt/rcp/org.argeo.swt.specific.rcp/build.properties b/swt/rcp/org.argeo.swt.specific.rcp/build.properties new file mode 100644 index 000000000..c6b651a59 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/build.properties @@ -0,0 +1,3 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/ diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/rcp/internal/rwt/RcpClient.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/rcp/internal/rwt/RcpClient.java new file mode 100644 index 000000000..0d9ce481d --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/rcp/internal/rwt/RcpClient.java @@ -0,0 +1,44 @@ +package org.argeo.eclipse.ui.rcp.internal.rwt; + +import org.eclipse.rap.rwt.client.Client; +import org.eclipse.rap.rwt.client.service.BrowserNavigation; +import org.eclipse.rap.rwt.client.service.BrowserNavigationListener; +import org.eclipse.rap.rwt.client.service.ClientService; +import org.eclipse.rap.rwt.client.service.JavaScriptExecutor; + +public class RcpClient implements Client { + + @Override + public T getService(Class type) { + if (type.isAssignableFrom(JavaScriptExecutor.class)) + return (T) javaScriptExecutor; + else if (type.isAssignableFrom(BrowserNavigation.class)) + return (T) browserNavigation; + else + return null; + } + + private JavaScriptExecutor javaScriptExecutor = new JavaScriptExecutor() { + + @Override + public void execute(String code) { + // TODO Auto-generated method stub + + } + }; + private BrowserNavigation browserNavigation = new BrowserNavigation() { + + @Override + public void pushState(String state, String title) { + // TODO Auto-generated method stub + + } + + @Override + public void addBrowserNavigationListener( + BrowserNavigationListener listener) { + // TODO Auto-generated method stub + + } + }; +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/rcp/internal/rwt/RcpResourceManager.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/rcp/internal/rwt/RcpResourceManager.java new file mode 100644 index 000000000..91109a9de --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/rcp/internal/rwt/RcpResourceManager.java @@ -0,0 +1,46 @@ +package org.argeo.eclipse.ui.rcp.internal.rwt; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Collections; +import java.util.Map; +import java.util.TreeMap; + +import org.apache.commons.io.IOUtils; +import org.eclipse.rap.rwt.service.ResourceManager; + +public class RcpResourceManager implements ResourceManager { + private Map register = Collections + .synchronizedMap(new TreeMap()); + + @Override + public void register(String name, InputStream in) { + try { + register.put(name, IOUtils.toByteArray(in)); + } catch (IOException e) { + throw new RuntimeException("Cannot register " + name, e); + } + } + + @Override + public boolean unregister(String name) { + return register.remove(name) != null; + } + + @Override + public InputStream getRegisteredContent(String name) { + return new ByteArrayInputStream(register.get(name)); + } + + @Override + public String getLocation(String name) { + return name; + } + + @Override + public boolean isRegistered(String name) { + return register.containsKey(name); + } + +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/BufferedImageDisplay.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/BufferedImageDisplay.java new file mode 100644 index 000000000..7fd2db1d1 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/BufferedImageDisplay.java @@ -0,0 +1,38 @@ +package org.argeo.eclipse.ui.specific; + +import java.awt.BorderLayout; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.image.BufferedImage; + +import javax.swing.JPanel; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.awt.SWT_AWT; +import org.eclipse.swt.widgets.Composite; + +public class BufferedImageDisplay extends Composite { + private BufferedImage image; + + public BufferedImageDisplay(Composite parent, int style) { + super(parent, SWT.EMBEDDED | SWT.NO_BACKGROUND); + Frame frame = SWT_AWT.new_Frame(this); + frame.setLayout(new BorderLayout()); + frame.add(new JPanel() { + private static final long serialVersionUID = 8924410573598922364L; + + public void paintComponent(Graphics g) { + super.paintComponent(g); + if (image != null) + g.drawImage(image, 0, 0, this); + } + + }, BorderLayout.CENTER); + frame.setVisible(true); + } + + public void setImage(BufferedImage image) { + this.image = image; + } + +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/CmsFileDialog.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/CmsFileDialog.java new file mode 100644 index 000000000..0c5d34699 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/CmsFileDialog.java @@ -0,0 +1,15 @@ +package org.argeo.eclipse.ui.specific; + +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.Shell; + +public class CmsFileDialog extends FileDialog { + public CmsFileDialog(Shell parent, int style) { + super(parent, style); + } + + public CmsFileDialog(Shell parent) { + super(parent); + } + +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/CmsFileUpload.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/CmsFileUpload.java new file mode 100644 index 000000000..638859a85 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/CmsFileUpload.java @@ -0,0 +1,32 @@ +package org.argeo.eclipse.ui.specific; + +import org.eclipse.rap.rwt.widgets.FileUpload; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.widgets.Composite; + +public class CmsFileUpload extends FileUpload { + public CmsFileUpload(Composite parent, int style) { + super(parent, style); + } + + @Override + public void setText(String text) { + super.setText(text); + } + + @Override + public String getFileName() { + return super.getFileName(); + } + + @Override + public String[] getFileNames() { + return super.getFileNames(); + } + + @Override + public void addSelectionListener(SelectionListener listener) { + super.addSelectionListener(listener); + } + +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/DefaultNLS.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/DefaultNLS.java new file mode 100644 index 000000000..fbb4fbf83 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/DefaultNLS.java @@ -0,0 +1,14 @@ +package org.argeo.eclipse.ui.specific; + +/** RCP specific {@link NLS} to be extended */ +public class DefaultNLS {// extends NLS { +// public final static String DEFAULT_BUNDLE_LOCATION = "/properties/plugin"; +// +// public DefaultNLS() { +// this(DEFAULT_BUNDLE_LOCATION); +// } +// +// public DefaultNLS(String bundleName) { +// NLS.initializeMessages(bundleName, getClass()); +// } +} \ No newline at end of file diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/EclipseUiConstants.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/EclipseUiConstants.java new file mode 100644 index 000000000..ac862d794 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/EclipseUiConstants.java @@ -0,0 +1,7 @@ +package org.argeo.eclipse.ui.specific; + +/** Constants which are specific to RWT.*/ +public interface EclipseUiConstants { + final static String CSS_CLASS = "org.eclipse.e4.ui.css.CssClassName"; + final static String MARKUP_SUPPORT = null; +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java new file mode 100644 index 000000000..d1acbcfc0 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java @@ -0,0 +1,40 @@ +package org.argeo.eclipse.ui.specific; + +import org.eclipse.jface.viewers.ColumnViewer; +import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.widgets.Widget; + +/** Static utilities to bridge differences between RCP and RAP */ +public class EclipseUiSpecificUtils { + private final static String CSS_CLASS = "org.eclipse.e4.ui.css.CssClassName"; + + public static void setStyleData(Widget widget, Object data) { + widget.setData(CSS_CLASS, data); + } + + public static Object getStyleData(Widget widget) { + return widget.getData(CSS_CLASS); + } + + public static void setMarkupData(Widget widget) { + // does nothing + } + + public static void setMarkupValidationDisabledData(Widget widget) { + // does nothing + } + + /** + * TootlTip support is supported for {@link ColumnViewer} in RCP + * + * @see ColumnViewerToolTipSupport#enableFor(Viewer) + */ + public static void enableToolTipSupport(Viewer viewer) { + if (viewer instanceof ColumnViewer) + ColumnViewerToolTipSupport.enableFor((ColumnViewer) viewer); + } + + private EclipseUiSpecificUtils() { + } +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/FileDropAdapter.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/FileDropAdapter.java new file mode 100644 index 000000000..524447ed0 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/FileDropAdapter.java @@ -0,0 +1,48 @@ +package org.argeo.eclipse.ui.specific; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; + +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.DropTarget; +import org.eclipse.swt.dnd.DropTargetAdapter; +import org.eclipse.swt.dnd.DropTargetEvent; +import org.eclipse.swt.dnd.FileTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.widgets.Control; + +public class FileDropAdapter { + + public void prepareDropTarget(Control control, DropTarget dropTarget) { + dropTarget.setTransfer(new Transfer[] { FileTransfer.getInstance() }); + dropTarget.addDropListener(new DropTargetAdapter() { + @Override + public void dropAccept(DropTargetEvent event) { + if (!FileTransfer.getInstance().isSupportedType(event.currentDataType)) { + event.detail = DND.DROP_NONE; + } + } + + @Override + public void drop(DropTargetEvent event) { + handleFileDrop(control, event); + } + }); + } + + public void handleFileDrop(Control control, DropTargetEvent event) { + String fileList[] = null; + FileTransfer ft = FileTransfer.getInstance(); + if (ft.isSupportedType(event.currentDataType)) { + fileList = (String[]) event.data; + } + System.out.println(Arrays.toString(fileList)); + } + + /** Executed in UI thread */ + protected void processUpload(InputStream in, String fileName, String contentType) throws IOException { + + } + +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/UiContext.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/UiContext.java new file mode 100644 index 000000000..20163cffa --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/UiContext.java @@ -0,0 +1,52 @@ +package org.argeo.eclipse.ui.specific; + +import java.util.Locale; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.swt.widgets.Display; + +/** Singleton class providing single sources infos about the UI context. */ +public class UiContext { + + public static HttpServletRequest getHttpRequest() { + return null; + } + + public static HttpServletResponse getHttpResponse() { + return null; + } + + public static Locale getLocale() { + return Locale.getDefault(); + } + + public static void setLocale(Locale locale) { + Locale.setDefault(locale); + } + + /** Can always be null */ + @SuppressWarnings("unchecked") + public static T getData(String key) { + Display display = getDisplay(); + if (display == null) + return null; + return (T) display.getData(key); + } + + public static void setData(String key, Object value) { + Display display = getDisplay(); + if (display == null) + throw new IllegalStateException("Not display available"); + display.setData(key, value); + } + + private static Display getDisplay() { + return Display.getCurrent(); + } + + private UiContext() { + } + +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileDetails.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileDetails.java new file mode 100644 index 000000000..fbb36ddd4 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileDetails.java @@ -0,0 +1,9 @@ +package org.eclipse.rap.fileupload; + +public interface FileDetails { + String getContentType(); + + long getContentLength(); + + String getFileName(); +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadEvent.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadEvent.java new file mode 100644 index 000000000..a7452806a --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadEvent.java @@ -0,0 +1,21 @@ +package org.eclipse.rap.fileupload; + +import java.util.EventObject; + +public abstract class FileUploadEvent extends EventObject { + + private static final long serialVersionUID = 1L; + + protected FileUploadEvent(FileUploadHandler source) { + super(source); + } + + public abstract FileDetails[] getFileDetails(); + + public abstract long getContentLength(); + + public abstract long getBytesRead(); + + public abstract Exception getException(); + +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadHandler.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadHandler.java new file mode 100644 index 000000000..7d89300f3 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadHandler.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2011, 2012 EclipseSource and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * EclipseSource - initial API and implementation + ******************************************************************************/ +package org.eclipse.rap.fileupload; + +/** + * A file upload handler is used to accept file uploads from a client. After + * creating a file upload handler, the server will accept file uploads to the + * URL returned by getUploadUrl(). Upload listeners can be attached + * to react on progress. When the upload has finished, a FileUploadHandler has + * to be disposed of by calling its dispose() method. + * + * @noextend This class is not intended to be subclassed by clients. + */ +public class FileUploadHandler { + + public FileUploadHandler(FileUploadReceiver fileUploadReceiver) { + } + + public void dispose() { + + } + + public void addUploadListener(FileUploadListener listener) { + + } + + public String getUploadUrl() { + return null; + } +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadListener.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadListener.java new file mode 100644 index 000000000..b59fd39ea --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadListener.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2011, 2012 EclipseSource and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * EclipseSource - initial API and implementation + ******************************************************************************/ +package org.eclipse.rap.fileupload; + +import org.eclipse.swt.widgets.Display; + + +/** + * Listener to react on progress and completion of a file upload. + *

+ * Note: This listener will be called from a different thread than the UI thread. + * Implementations must use {@link Display#asyncExec(Runnable)} to access the UI. + *

+ * + * @see FileUploadEvent + */ +public interface FileUploadListener { + + /** + * Called when new information about an in-progress upload is available. + * + * @param event event object that contains information about the uploaded file + * @see FileUploadEvent#getBytesRead() + */ + void uploadProgress( FileUploadEvent event ); + + /** + * Called when a file upload has finished successfully. + * + * @param event event object that contains information about the uploaded file + * @see FileUploadEvent + */ + void uploadFinished( FileUploadEvent event ); + + /** + * Called when a file upload failed. + * + * @param event event object that contains information about the uploaded file + * @see FileUploadEvent#getErrorMessage() + */ + void uploadFailed( FileUploadEvent event ); + +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadReceiver.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadReceiver.java new file mode 100644 index 000000000..3f4cf47c4 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadReceiver.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2011, 2013 EclipseSource and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * EclipseSource - initial API and implementation + ******************************************************************************/ +package org.eclipse.rap.fileupload; + +import java.io.IOException; +import java.io.InputStream; + + +/** + * Instances of this interface are responsible for reading and processing the data from a file + * upload. + */ +public abstract class FileUploadReceiver { + + /** + * Reads and processes all data from the provided input stream. + * + * @param stream the stream to read from + * @param details the details of the uploaded file like file name, content-type and size + * @throws IOException if an input / output error occurs + */ + public abstract void receive( InputStream stream, FileDetails details ) throws IOException; + +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/RWT.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/RWT.java new file mode 100644 index 000000000..1688594bb --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/RWT.java @@ -0,0 +1,45 @@ +package org.eclipse.rap.rwt; + +import java.util.Locale; + +import javax.servlet.http.HttpServletRequest; + +import org.argeo.eclipse.ui.rcp.internal.rwt.RcpClient; +import org.argeo.eclipse.ui.rcp.internal.rwt.RcpResourceManager; +import org.eclipse.rap.rwt.client.Client; +import org.eclipse.rap.rwt.service.ResourceManager; + +public class RWT { + public final static String CUSTOM_VARIANT = "argeo-rcp:CUSTOM_VARIANT"; + public final static String MARKUP_ENABLED = "argeo-rcp:MARKUP_ENABLED"; + public static final String TOOLTIP_MARKUP_ENABLED = "argeo-rcp:TOOLTIP_MARKUP_ENABLED"; + public final static String CUSTOM_ITEM_HEIGHT = "argeo-rcp:CUSTOM_ITEM_HEIGHT"; + public final static String ACTIVE_KEYS = "argeo-rcp:ACTIVE_KEYS"; + public final static String CANCEL_KEYS = "argeo-rcp:CANCEL_KEYS"; + public final static String DEFAULT_THEME_ID = "argeo-rcp:DEFAULT_THEME_ID"; + + public final static int HYPERLINK = 0; + + private static Locale locale = Locale.getDefault(); + private static RcpClient client = new RcpClient(); + private static ResourceManager resourceManager = new RcpResourceManager(); + static { + + } + + public static Locale getLocale() { + return locale; + } + + public static HttpServletRequest getRequest() { + return null; + } + + public static ResourceManager getResourceManager() { + return resourceManager; + } + + public static Client getClient() { + return client; + } +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/SingletonUtil.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/SingletonUtil.java new file mode 100644 index 000000000..6e30aa635 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/SingletonUtil.java @@ -0,0 +1,7 @@ +package org.eclipse.rap.rwt; + +public class SingletonUtil { + public static T getSessionInstance(Class clss) { + return null; + } +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/AbstractEntryPoint.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/AbstractEntryPoint.java new file mode 100644 index 000000000..980a81854 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/AbstractEntryPoint.java @@ -0,0 +1,43 @@ +package org.eclipse.rap.rwt.application; + +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +public abstract class AbstractEntryPoint implements EntryPoint { + private Display display; + private Shell shell; + + protected Shell createShell(Display display) { + return new Shell(display); + } + + protected void createContents(Composite parent) { + + } + + public int createUI() { + display = new Display(); + shell = createShell(display); + shell.setLayout(new GridLayout(1, false)); + createContents(shell); + if (shell.getMaximized()) { + shell.layout(); + } else { + shell.pack(); + } + shell.open(); + while (!shell.isDisposed()) { + if (!display.readAndDispatch()) { + display.sleep(); + } + } + display.dispose(); + return 0; + } + + protected Shell getShell() { + return shell; + } +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/Application.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/Application.java new file mode 100644 index 000000000..6cb5f29d2 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/Application.java @@ -0,0 +1,27 @@ +package org.eclipse.rap.rwt.application; + +import java.util.Map; + +import org.eclipse.rap.rwt.service.ResourceLoader; + +public interface Application { + public static enum OperationMode { + JEE_COMPATIBILITY, SWT_COMPATIBILITY, + } + + void setOperationMode(OperationMode operationMode); + + void addResource(String name, ResourceLoader resourceLoader); + + void setExceptionHandler(ExceptionHandler exceptionHandler); + + void addEntryPoint(String path, EntryPointFactory entryPointFactory, + Map properties); + + void addEntryPoint(String path, Class entryPoint, + Map properties); + + void addStyleSheet(String themeId, String styleSheetLocation, + ResourceLoader resourceLoader); + +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/ApplicationConfiguration.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/ApplicationConfiguration.java new file mode 100644 index 000000000..961ad70f6 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/ApplicationConfiguration.java @@ -0,0 +1,5 @@ +package org.eclipse.rap.rwt.application; + +public interface ApplicationConfiguration { + void configure(Application application); +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/EntryPoint.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/EntryPoint.java new file mode 100644 index 000000000..c0d559a2b --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/EntryPoint.java @@ -0,0 +1,5 @@ +package org.eclipse.rap.rwt.application; + +public interface EntryPoint { + int createUI(); +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/EntryPointFactory.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/EntryPointFactory.java new file mode 100644 index 000000000..d5b24d8fe --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/EntryPointFactory.java @@ -0,0 +1,5 @@ +package org.eclipse.rap.rwt.application; + +public interface EntryPointFactory { + public EntryPoint create(); +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/ExceptionHandler.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/ExceptionHandler.java new file mode 100644 index 000000000..13daf2195 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/ExceptionHandler.java @@ -0,0 +1,5 @@ +package org.eclipse.rap.rwt.application; + +public interface ExceptionHandler { + public void handleException(Throwable throwable); +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/Client.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/Client.java new file mode 100644 index 000000000..934feaea6 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/Client.java @@ -0,0 +1,18 @@ +package org.eclipse.rap.rwt.client; + +import java.io.Serializable; + +import org.eclipse.rap.rwt.client.service.ClientService; + +public interface Client extends Serializable { + + /** + * Returns this client's implementation of a given service, if available. + * + * @param type the type of the requested service, must be a subtype of ClientService + * @return the requested service if provided by this client, otherwise null + * @see ClientService + */ + T getService( Class type ); + +} \ No newline at end of file diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/WebClient.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/WebClient.java new file mode 100644 index 000000000..1f19bdd7c --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/WebClient.java @@ -0,0 +1,10 @@ +package org.eclipse.rap.rwt.client; + +public interface WebClient { + public final static String FAVICON = "rcp:FAVICON"; + public final static String PAGE_TITLE = "rcp:PAGE_TITLE"; + public final static String BODY_HTML = "rcp:BODY_HTML"; + public final static String THEME_ID = "rcp:THEME_ID"; + public final static String HEAD_HTML = "rcp:HEAD_HTML"; + public final static String PAGE_OVERFLOW = "rcp:PAGE_OVERFLOW"; +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigation.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigation.java new file mode 100644 index 000000000..ffba4e43e --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigation.java @@ -0,0 +1,7 @@ +package org.eclipse.rap.rwt.client.service; + +public interface BrowserNavigation extends ClientService { + void pushState(String state, String title); + + void addBrowserNavigationListener(BrowserNavigationListener listener); +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigationEvent.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigationEvent.java new file mode 100644 index 000000000..3e1b3eb72 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigationEvent.java @@ -0,0 +1,10 @@ +package org.eclipse.rap.rwt.client.service; + +public class BrowserNavigationEvent { + private String state; + + public String getState() { + return state; + } + +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigationListener.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigationListener.java new file mode 100644 index 000000000..8319c03f7 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigationListener.java @@ -0,0 +1,5 @@ +package org.eclipse.rap.rwt.client.service; + +public interface BrowserNavigationListener { + public void navigated(BrowserNavigationEvent event); +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/ClientService.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/ClientService.java new file mode 100644 index 000000000..9f479d139 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/ClientService.java @@ -0,0 +1,6 @@ +package org.eclipse.rap.rwt.client.service; + +import java.io.Serializable; + +public interface ClientService extends Serializable { +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/JavaScriptExecutor.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/JavaScriptExecutor.java new file mode 100644 index 000000000..6c44c729c --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/JavaScriptExecutor.java @@ -0,0 +1,5 @@ +package org.eclipse.rap.rwt.client.service; + +public interface JavaScriptExecutor extends ClientService { + public void execute( String code ); +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/UrlLauncher.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/UrlLauncher.java new file mode 100644 index 000000000..9dae811c7 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/UrlLauncher.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2012 EclipseSource and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * EclipseSource - initial API and implementation + ******************************************************************************/ +package org.eclipse.rap.rwt.client.service; + +/** + * The UrlLauncher service allows loading an URL in an external window, application or save dialog. + * + * @since 2.0 + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface UrlLauncher extends ClientService { + + /** + * Opens the given URL. + * + * Any HTTP URL or relative URL will be opened in a new window. + * Modern browser may block any attempt to open new windows, but will usually prompt the user to + * accept or ignore. Even if accepted, the decision may be applied to only this attempt, or only + * to future attempts. It could also trigger a document reload, causing a session restart. + * + * Non-HTTP URLs like "mailto" will not create a new browser window, but require the client + * to have a matching protocol handler registered. + * + * @param url the URL to open + */ + void openURL( String url ); + +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ResourceLoader.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ResourceLoader.java new file mode 100644 index 000000000..7e7116cf3 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ResourceLoader.java @@ -0,0 +1,9 @@ +package org.eclipse.rap.rwt.service; + +import java.io.IOException; +import java.io.InputStream; + +public interface ResourceLoader { + public InputStream getResourceAsStream(String resourceName) + throws IOException; +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ResourceManager.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ResourceManager.java new file mode 100644 index 000000000..c3379ea66 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ResourceManager.java @@ -0,0 +1,15 @@ +package org.eclipse.rap.rwt.service; + +import java.io.InputStream; + +public interface ResourceManager { + public void register(String name, InputStream in); + + boolean unregister(String name); + + public InputStream getRegisteredContent(String name); + + public String getLocation(String name); + + public boolean isRegistered(String name); +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ServerPushSession.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ServerPushSession.java new file mode 100644 index 000000000..bed194f31 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ServerPushSession.java @@ -0,0 +1,12 @@ +package org.eclipse.rap.rwt.service; + +/** Mock, does nothing as this is irrelevant for RCP. */ +public class ServerPushSession { + public void start() { + + } + + public void stop() { + + } +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/widgets/DropDown.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/widgets/DropDown.java new file mode 100644 index 000000000..b2a2005e7 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/widgets/DropDown.java @@ -0,0 +1,33 @@ +package org.eclipse.rap.rwt.widgets; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Widget; + +public class DropDown { + private boolean visible=false; + + public DropDown(Widget parent, int style) { + // FIXME implement a shell + } + + public DropDown(Widget parent) { + this(parent, SWT.NONE); + } + + public void setVisible(boolean visible) { + this.visible = visible; + } + + public boolean isVisible() { + return visible; + } + + public void setItems( String[] items ) { + + } + + public void setSelectionIndex( int selection ) { + + } + +} diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/widgets/FileUpload.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/widgets/FileUpload.java new file mode 100644 index 000000000..cbf1449e0 --- /dev/null +++ b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/widgets/FileUpload.java @@ -0,0 +1,37 @@ +package org.eclipse.rap.rwt.widgets; + +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Composite; + +public class FileUpload extends Composite { + + public FileUpload(Composite parent, int style) { + super(parent, style); + } + + public void addSelectionListener(SelectionListener listener) { + + } + + public void submit(String url) { + + } + + public void setImage(Image image) { + + } + + public void setText(String text) { + + } + + public String getFileName() { + return null; + } + + public String[] getFileNames() { + return null; + } + +}