From: Mathieu Baudier Date: Wed, 5 Jan 2022 06:32:11 +0000 (+0100) Subject: Move RAP specific to subdirectory X-Git-Tag: argeo-commons-2.3.5~105 X-Git-Url: https://git.argeo.org/?a=commitdiff_plain;h=f9efbe5228615951dd8482a4582aa24e00c10ce5;p=lgpl%2Fargeo-commons.git Move RAP specific to subdirectory --- diff --git a/org.argeo.cms.e4.rap/.classpath b/org.argeo.cms.e4.rap/.classpath deleted file mode 100644 index e801ebfb4..000000000 --- a/org.argeo.cms.e4.rap/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/org.argeo.cms.e4.rap/.project b/org.argeo.cms.e4.rap/.project deleted file mode 100644 index 40c9e013f..000000000 --- a/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/org.argeo.cms.e4.rap/META-INF/.gitignore b/org.argeo.cms.e4.rap/META-INF/.gitignore deleted file mode 100644 index 4854a41b9..000000000 --- a/org.argeo.cms.e4.rap/META-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/MANIFEST.MF diff --git a/org.argeo.cms.e4.rap/OSGI-INF/cms-admin-rap.xml b/org.argeo.cms.e4.rap/OSGI-INF/cms-admin-rap.xml deleted file mode 100644 index 1f688baa6..000000000 --- a/org.argeo.cms.e4.rap/OSGI-INF/cms-admin-rap.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/org.argeo.cms.e4.rap/bnd.bnd b/org.argeo.cms.e4.rap/bnd.bnd deleted file mode 100644 index 90dc8d42b..000000000 --- a/org.argeo.cms.e4.rap/bnd.bnd +++ /dev/null @@ -1,10 +0,0 @@ -Bundle-ActivationPolicy: lazy -Service-Component: OSGI-INF/cms-admin-rap.xml - -Import-Package: org.argeo.api,\ -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/org.argeo.cms.e4.rap/build.properties b/org.argeo.cms.e4.rap/build.properties deleted file mode 100644 index c58ea2178..000000000 --- a/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/org.argeo.cms.e4.rap/pom.xml b/org.argeo.cms.e4.rap/pom.xml deleted file mode 100644 index 61b47c38f..000000000 --- a/org.argeo.cms.e4.rap/pom.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - 4.0.0 - - org.argeo.commons - argeo-commons - 2.3-SNAPSHOT - .. - - org.argeo.cms.e4.rap - CMS E4 RAP - jar - - - org.argeo.commons - org.argeo.cms.ui.rap - 2.3-SNAPSHOT - - - org.argeo.commons - org.argeo.cms.e4 - 2.3-SNAPSHOT - - - - org.argeo.commons - org.argeo.swt.specific.rap - 2.3-SNAPSHOT - provided - - - - - org.argeo.tp - argeo-tp-rap-e4 - ${version.argeo-tp} - pom - provided - - - \ No newline at end of file diff --git a/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/AbstractRapE4App.java b/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/AbstractRapE4App.java deleted file mode 100644 index 5fe22ae33..000000000 --- a/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/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4AdminApp.java b/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4AdminApp.java deleted file mode 100644 index 66be1e8e9..000000000 --- a/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/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4EntryPointFactory.java b/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4EntryPointFactory.java deleted file mode 100644 index a5a32348e..000000000 --- a/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/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsLoginLifecycle.java b/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsLoginLifecycle.java deleted file mode 100644 index 3ee8df1ef..000000000 --- a/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.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.api.NodeConstants; -import org.argeo.api.cms.CmsImageManager; -import org.argeo.api.cms.CmsView; -import org.argeo.api.cms.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 Log log = LogFactory.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); - CmsLoginShell loginShell = new CmsLoginShell(this); - CmsSwtUtils.registerCmsView(loginShell.getShell(), this); - loginShell.setSubject(subject); - try { - // try pre-auth - loginContext = new LoginContext(NodeConstants.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/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/SimpleRapE4App.java b/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/SimpleRapE4App.java deleted file mode 100644 index 12c4f6336..000000000 --- a/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/SimpleRapE4App.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.argeo.cms.e4.rap; - -import java.util.Enumeration; - -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -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 Log log = LogFactory.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/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/package-info.java b/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/package-info.java deleted file mode 100644 index 1122f1922..000000000 --- a/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/org.argeo.cms.ui.rap/.classpath b/org.argeo.cms.ui.rap/.classpath deleted file mode 100644 index e801ebfb4..000000000 --- a/org.argeo.cms.ui.rap/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/org.argeo.cms.ui.rap/.project b/org.argeo.cms.ui.rap/.project deleted file mode 100644 index 1a37a6771..000000000 --- a/org.argeo.cms.ui.rap/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - org.argeo.cms.ui.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/org.argeo.cms.ui.rap/META-INF/.gitignore b/org.argeo.cms.ui.rap/META-INF/.gitignore deleted file mode 100644 index 4854a41b9..000000000 --- a/org.argeo.cms.ui.rap/META-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/MANIFEST.MF diff --git a/org.argeo.cms.ui.rap/bnd.bnd b/org.argeo.cms.ui.rap/bnd.bnd deleted file mode 100644 index 30b21b019..000000000 --- a/org.argeo.cms.ui.rap/bnd.bnd +++ /dev/null @@ -1,15 +0,0 @@ -Import-Package:\ -org.eclipse.swt,\ -org.argeo.api,\ -org.argeo.eclipse.ui,\ -javax.jcr.nodetype,\ -javax.jcr.security,\ -org.eclipse.swt.graphics,\ -org.eclipse.jetty.util.component;resolution:=optional,\ -org.eclipse.jetty.http;resolution:=optional,\ -org.eclipse.jetty.io;resolution:=optional,\ -org.eclipse.jetty.security;resolution:=optional,\ -org.eclipse.jetty.server.handler;resolution:=optional,\ -org.eclipse.jetty.*;resolution:=optional,\ -* - diff --git a/org.argeo.cms.ui.rap/build.properties b/org.argeo.cms.ui.rap/build.properties deleted file mode 100644 index 34d2e4d2d..000000000 --- a/org.argeo.cms.ui.rap/build.properties +++ /dev/null @@ -1,4 +0,0 @@ -source.. = src/ -output.. = bin/ -bin.includes = META-INF/,\ - . diff --git a/org.argeo.cms.ui.rap/pom.xml b/org.argeo.cms.ui.rap/pom.xml deleted file mode 100644 index 243d2c441..000000000 --- a/org.argeo.cms.ui.rap/pom.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - 4.0.0 - - org.argeo.commons - argeo-commons - 2.3-SNAPSHOT - .. - - org.argeo.cms.ui.rap - CMS UI RAP - jar - - - org.argeo.commons - org.argeo.cms.ui - 2.3-SNAPSHOT - - - - org.argeo.commons - org.argeo.swt.specific.rap - 2.3-SNAPSHOT - provided - - - - - org.argeo.tp.rap.e4 - org.eclipse.rap.rwt - provided - - - org.argeo.tp.rap.e4 - org.eclipse.core.commands - provided - - - org.argeo.tp.rap.e4 - org.eclipse.rap.jface - provided - - - - - org.argeo.tp.rap.e4 - org.eclipse.rap.filedialog - provided - - - org.argeo.tp.rap.e4 - org.eclipse.rap.fileupload - provided - - - - \ No newline at end of file diff --git a/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/AppUi.java b/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/AppUi.java deleted file mode 100644 index 30cff8f81..000000000 --- a/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.NodeConstants; -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, NodeConstants.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/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/Branding.java b/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/Branding.java deleted file mode 100644 index f72338ef7..000000000 --- a/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/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/CmsScriptApp.java b/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/CmsScriptApp.java deleted file mode 100644 index 9dd1509be..000000000 --- a/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/CmsScriptApp.java +++ /dev/null @@ -1,422 +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.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.api.cms.CmsTheme; -import org.argeo.cms.CmsException; -import org.argeo.cms.ui.CmsConstants; -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 Log log = LogFactory.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(CmsConstants.LOADING_IMAGE, createResourceLoader(CmsConstants.LOADING_IMAGE)); - // empty image - application.addResource(CmsConstants.NO_IMAGE, createResourceLoader(CmsConstants.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/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/CmsScriptRwtApplication.java b/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/CmsScriptRwtApplication.java deleted file mode 100644 index d7c1a63ce..000000000 --- a/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/CmsScriptRwtApplication.java +++ /dev/null @@ -1,121 +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.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.cms.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 Log log = LogFactory.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/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/ScriptAppActivator.java b/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/ScriptAppActivator.java deleted file mode 100644 index 7813156ec..000000000 --- a/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/ScriptAppActivator.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.argeo.cms.ui.script; - -import javax.jcr.Repository; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -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 Log log = LogFactory.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/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/ScriptUi.java b/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/ScriptUi.java deleted file mode 100644 index bf68fc299..000000000 --- a/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/ScriptUi.java +++ /dev/null @@ -1,116 +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.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -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 Log log = LogFactory.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/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/cms.js b/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/cms.js deleted file mode 100644 index be9618dcb..000000000 --- a/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/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/package-info.java b/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/package-info.java deleted file mode 100644 index 7440596ab..000000000 --- a/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/org.argeo.cms.ui.rap/src/org/argeo/cms/web/AbstractCmsEntryPoint.java b/org.argeo.cms.ui.rap/src/org/argeo/cms/web/AbstractCmsEntryPoint.java deleted file mode 100644 index c20068fa7..000000000 --- a/org.argeo.cms.ui.rap/src/org/argeo/cms/web/AbstractCmsEntryPoint.java +++ /dev/null @@ -1,399 +0,0 @@ -package org.argeo.cms.web; - -import static org.argeo.naming.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.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.api.NodeConstants; -import org.argeo.api.cms.CmsView; -import org.argeo.cms.CmsException; -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.auth.HttpRequestCallback; -import org.argeo.cms.auth.HttpRequestCallbackHandler; -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.argeo.jcr.JcrUtils; -import org.argeo.naming.AuthPassword; -import org.argeo.naming.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 Log log = LogFactory.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(NodeConstants.LOGIN_CONTEXT_USER, - new HttpRequestCallbackHandler(new ServletHttpRequest(UiContext.getHttpRequest()), - new ServletHttpResponse(UiContext.getHttpResponse()))); - lc.login(); - } catch (LoginException e) { - try { - lc = new LoginContext(NodeConstants.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(NodeConstants.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 HttpRequestCallback) { - ((HttpRequestCallback) callback) - .setRequest(new ServletHttpRequest(UiContext.getHttpRequest())); - ((HttpRequestCallback) callback) - .setResponse(new ServletHttpResponse(UiContext.getHttpResponse())); - } - } - } - }; - LoginContext lc = new LoginContext(NodeConstants.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/org.argeo.cms.ui.rap/src/org/argeo/cms/web/BundleResourceLoader.java b/org.argeo.cms.ui.rap/src/org/argeo/cms/web/BundleResourceLoader.java deleted file mode 100644 index ca93e625e..000000000 --- a/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/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsThemeResourceLoader.java b/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsThemeResourceLoader.java deleted file mode 100644 index 5de0f9103..000000000 --- a/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.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/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsWebApp.java b/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsWebApp.java deleted file mode 100644 index e5b6c7efc..000000000 --- a/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsWebApp.java +++ /dev/null @@ -1,165 +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.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.api.cms.CmsApp; -import org.argeo.api.cms.CmsAppListener; -import org.argeo.api.cms.CmsTheme; -import org.argeo.api.cms.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.Constants; -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 Log log = LogFactory.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 cmsAppId = properties.get(Constants.SERVICE_PID); - if (!cmsAppId.equals(this.cmsAppId)) - 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/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsWebEntryPoint.java b/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsWebEntryPoint.java deleted file mode 100644 index d7050e954..000000000 --- a/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsWebEntryPoint.java +++ /dev/null @@ -1,358 +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.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.api.NodeConstants; -import org.argeo.api.cms.CmsApp; -import org.argeo.api.cms.CmsImageManager; -import org.argeo.api.cms.CmsSession; -import org.argeo.api.cms.CmsUi; -import org.argeo.api.cms.CmsView; -import org.argeo.api.cms.UxContext; -import org.argeo.cms.LocaleUtils; -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.auth.HttpRequestCallbackHandler; -import org.argeo.cms.osgi.CmsOsgiUtils; -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 Log log = LogFactory.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(NodeConstants.LOGIN_CONTEXT_USER, - new HttpRequestCallbackHandler(new ServletHttpRequest(UiContext.getHttpRequest()), - new ServletHttpResponse(UiContext.getHttpResponse()))); - lc.login(); - } catch (LoginException e) { - try { - lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_ANONYMOUS); - 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(NodeConstants.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 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 = CmsOsgiUtils.getCmsSession(cmsWebApp.getBundleContext(), 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 { - if (!display.readAndDispatch()) { - display.sleep(); - } - } 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/org.argeo.cms.ui.rap/src/org/argeo/cms/web/MinimalWebApp.java b/org.argeo.cms.ui.rap/src/org/argeo/cms/web/MinimalWebApp.java deleted file mode 100644 index 2eff71ee8..000000000 --- a/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/org.argeo.cms.ui.rap/src/org/argeo/cms/web/SimpleApp.java b/org.argeo.cms.ui.rap/src/org/argeo/cms/web/SimpleApp.java deleted file mode 100644 index 4cd6874d0..000000000 --- a/org.argeo.cms.ui.rap/src/org/argeo/cms/web/SimpleApp.java +++ /dev/null @@ -1,415 +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.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.api.NodeConstants; -import org.argeo.cms.CmsException; -import org.argeo.cms.jcr.CmsJcrUtils; -import org.argeo.cms.ui.CmsConstants; -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 CmsConstants, ApplicationConfiguration { - private final static Log log = LogFactory.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(NodeConstants.ROLE_ANONYMOUS, NodeConstants.ROLE_USER); - private List rwPrincipals = Arrays.asList(NodeConstants.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/org.argeo.cms.ui.rap/src/org/argeo/cms/web/SimpleErgonomics.java b/org.argeo.cms.ui.rap/src/org/argeo/cms/web/SimpleErgonomics.java deleted file mode 100644 index 6e1165cf3..000000000 --- a/org.argeo.cms.ui.rap/src/org/argeo/cms/web/SimpleErgonomics.java +++ /dev/null @@ -1,239 +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.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.api.cms.CmsImageManager; -import org.argeo.api.cms.UxContext; -import org.argeo.cms.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 Log log = LogFactory.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/org.argeo.cms.ui.rap/src/org/argeo/cms/web/WebThemeUtils.java b/org.argeo.cms.ui.rap/src/org/argeo/cms/web/WebThemeUtils.java deleted file mode 100644 index a28b13fc6..000000000 --- a/org.argeo.cms.ui.rap/src/org/argeo/cms/web/WebThemeUtils.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.argeo.cms.web; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.api.cms.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 Log log = LogFactory.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/org.argeo.cms.ui.rap/src/org/argeo/eclipse/ui/jetty/RwtRunner.java b/org.argeo.cms.ui.rap/src/org/argeo/eclipse/ui/jetty/RwtRunner.java deleted file mode 100644 index 29165a4ef..000000000 --- a/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/org.argeo.swt.specific.rap/.classpath b/org.argeo.swt.specific.rap/.classpath deleted file mode 100644 index e03d341b1..000000000 --- a/org.argeo.swt.specific.rap/.classpath +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - diff --git a/org.argeo.swt.specific.rap/.project b/org.argeo.swt.specific.rap/.project deleted file mode 100644 index 53d797685..000000000 --- a/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/org.argeo.swt.specific.rap/META-INF/.gitignore b/org.argeo.swt.specific.rap/META-INF/.gitignore deleted file mode 100644 index 4854a41b9..000000000 --- a/org.argeo.swt.specific.rap/META-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/MANIFEST.MF diff --git a/org.argeo.swt.specific.rap/bnd.bnd b/org.argeo.swt.specific.rap/bnd.bnd deleted file mode 100644 index 76bb82c80..000000000 --- a/org.argeo.swt.specific.rap/bnd.bnd +++ /dev/null @@ -1,5 +0,0 @@ -Import-Package: org.eclipse.swt,\ -org.eclipse.jface.dialogs,\ -javax.servlet.http,\ -org.eclipse.swt.events,\ -* diff --git a/org.argeo.swt.specific.rap/build.properties b/org.argeo.swt.specific.rap/build.properties deleted file mode 100644 index fd806ca05..000000000 --- a/org.argeo.swt.specific.rap/build.properties +++ /dev/null @@ -1,2 +0,0 @@ -source.. = src/ -output.. = bin/ diff --git a/org.argeo.swt.specific.rap/pom.xml b/org.argeo.swt.specific.rap/pom.xml deleted file mode 100644 index cb73fcd32..000000000 --- a/org.argeo.swt.specific.rap/pom.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - 4.0.0 - - org.argeo.commons - 2.3-SNAPSHOT - argeo-commons - .. - - org.argeo.swt.specific.rap - SWT RAP Specific - - - - org.argeo.tp.rap.e4 - org.eclipse.rap.rwt - provided - - - org.argeo.tp.rap.e4 - org.eclipse.core.commands - provided - - - org.argeo.tp.rap.e4 - org.eclipse.rap.jface - provided - - - - - org.argeo.tp.rap.e4 - org.eclipse.rap.filedialog - provided - - - org.argeo.tp.rap.e4 - org.eclipse.rap.fileupload - provided - - - - \ No newline at end of file diff --git a/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/CmsFileDialog.java b/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/CmsFileDialog.java deleted file mode 100644 index 6100c1a83..000000000 --- a/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/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/CmsFileUpload.java b/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/CmsFileUpload.java deleted file mode 100644 index 3f30bde25..000000000 --- a/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/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java b/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java deleted file mode 100644 index a89b921cd..000000000 --- a/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/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/FileDropAdapter.java b/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/FileDropAdapter.java deleted file mode 100644 index f9ca81682..000000000 --- a/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/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/UiContext.java b/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/UiContext.java deleted file mode 100644 index 72e17a22d..000000000 --- a/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/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/package-info.java b/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/package-info.java deleted file mode 100644 index 4ec451f8a..000000000 --- a/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/pom.xml b/pom.xml index 8ec1f0c83..619b9a73e 100644 --- a/pom.xml +++ b/pom.xml @@ -23,19 +23,18 @@ org.argeo.init org.argeo.enterprise - org.argeo.swt.specific.rap org.argeo.api org.argeo.cms.tp org.argeo.cms org.argeo.cms.servlet - org.argeo.cms.swt org.argeo.cms.jcr + + org.argeo.cms.swt org.argeo.cms.ui - org.argeo.cms.ui.rap - org.argeo.cms.e4 - org.argeo.cms.e4.rap + + rap dep demo diff --git a/rap/cnf/maven.bnd b/rap/cnf/maven.bnd new file mode 100644 index 000000000..4bd5c0cfe --- /dev/null +++ b/rap/cnf/maven.bnd @@ -0,0 +1 @@ +-include: ../../cnf/maven.bnd \ No newline at end of file diff --git a/rap/org.argeo.cms.e4.rap/.classpath b/rap/org.argeo.cms.e4.rap/.classpath new file mode 100644 index 000000000..e801ebfb4 --- /dev/null +++ b/rap/org.argeo.cms.e4.rap/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/rap/org.argeo.cms.e4.rap/.project b/rap/org.argeo.cms.e4.rap/.project new file mode 100644 index 000000000..40c9e013f --- /dev/null +++ b/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/rap/org.argeo.cms.e4.rap/META-INF/.gitignore b/rap/org.argeo.cms.e4.rap/META-INF/.gitignore new file mode 100644 index 000000000..4854a41b9 --- /dev/null +++ b/rap/org.argeo.cms.e4.rap/META-INF/.gitignore @@ -0,0 +1 @@ +/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 new file mode 100644 index 000000000..1f688baa6 --- /dev/null +++ b/rap/org.argeo.cms.e4.rap/OSGI-INF/cms-admin-rap.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/rap/org.argeo.cms.e4.rap/bnd.bnd b/rap/org.argeo.cms.e4.rap/bnd.bnd new file mode 100644 index 000000000..90dc8d42b --- /dev/null +++ b/rap/org.argeo.cms.e4.rap/bnd.bnd @@ -0,0 +1,10 @@ +Bundle-ActivationPolicy: lazy +Service-Component: OSGI-INF/cms-admin-rap.xml + +Import-Package: org.argeo.api,\ +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 new file mode 100644 index 000000000..c58ea2178 --- /dev/null +++ b/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/rap/org.argeo.cms.e4.rap/pom.xml b/rap/org.argeo.cms.e4.rap/pom.xml new file mode 100644 index 000000000..49b40c4d0 --- /dev/null +++ b/rap/org.argeo.cms.e4.rap/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + org.argeo.commons + rap + 2.3-SNAPSHOT + .. + + org.argeo.cms.e4.rap + CMS E4 RAP + jar + + + org.argeo.commons + org.argeo.cms.ui.rap + 2.3-SNAPSHOT + + + org.argeo.commons + org.argeo.cms.e4 + 2.3-SNAPSHOT + + + + org.argeo.commons + org.argeo.swt.specific.rap + 2.3-SNAPSHOT + provided + + + + + org.argeo.tp + argeo-tp-rap-e4 + ${version.argeo-tp} + pom + provided + + + \ No newline at end of file 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 new file mode 100644 index 000000000..5fe22ae33 --- /dev/null +++ b/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/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 new file mode 100644 index 000000000..66be1e8e9 --- /dev/null +++ b/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/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 new file mode 100644 index 000000000..a5a32348e --- /dev/null +++ b/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/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 new file mode 100644 index 000000000..3ee8df1ef --- /dev/null +++ b/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.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.api.NodeConstants; +import org.argeo.api.cms.CmsImageManager; +import org.argeo.api.cms.CmsView; +import org.argeo.api.cms.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 Log log = LogFactory.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); + CmsLoginShell loginShell = new CmsLoginShell(this); + CmsSwtUtils.registerCmsView(loginShell.getShell(), this); + loginShell.setSubject(subject); + try { + // try pre-auth + loginContext = new LoginContext(NodeConstants.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 new file mode 100644 index 000000000..12c4f6336 --- /dev/null +++ b/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/SimpleRapE4App.java @@ -0,0 +1,33 @@ +package org.argeo.cms.e4.rap; + +import java.util.Enumeration; + +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +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 Log log = LogFactory.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 new file mode 100644 index 000000000..1122f1922 --- /dev/null +++ b/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/rap/org.argeo.cms.ui.rap/.classpath b/rap/org.argeo.cms.ui.rap/.classpath new file mode 100644 index 000000000..e801ebfb4 --- /dev/null +++ b/rap/org.argeo.cms.ui.rap/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/rap/org.argeo.cms.ui.rap/.project b/rap/org.argeo.cms.ui.rap/.project new file mode 100644 index 000000000..1a37a6771 --- /dev/null +++ b/rap/org.argeo.cms.ui.rap/.project @@ -0,0 +1,28 @@ + + + org.argeo.cms.ui.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.cms.ui.rap/META-INF/.gitignore b/rap/org.argeo.cms.ui.rap/META-INF/.gitignore new file mode 100644 index 000000000..4854a41b9 --- /dev/null +++ b/rap/org.argeo.cms.ui.rap/META-INF/.gitignore @@ -0,0 +1 @@ +/MANIFEST.MF diff --git a/rap/org.argeo.cms.ui.rap/bnd.bnd b/rap/org.argeo.cms.ui.rap/bnd.bnd new file mode 100644 index 000000000..30b21b019 --- /dev/null +++ b/rap/org.argeo.cms.ui.rap/bnd.bnd @@ -0,0 +1,15 @@ +Import-Package:\ +org.eclipse.swt,\ +org.argeo.api,\ +org.argeo.eclipse.ui,\ +javax.jcr.nodetype,\ +javax.jcr.security,\ +org.eclipse.swt.graphics,\ +org.eclipse.jetty.util.component;resolution:=optional,\ +org.eclipse.jetty.http;resolution:=optional,\ +org.eclipse.jetty.io;resolution:=optional,\ +org.eclipse.jetty.security;resolution:=optional,\ +org.eclipse.jetty.server.handler;resolution:=optional,\ +org.eclipse.jetty.*;resolution:=optional,\ +* + diff --git a/rap/org.argeo.cms.ui.rap/build.properties b/rap/org.argeo.cms.ui.rap/build.properties new file mode 100644 index 000000000..34d2e4d2d --- /dev/null +++ b/rap/org.argeo.cms.ui.rap/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/rap/org.argeo.cms.ui.rap/pom.xml b/rap/org.argeo.cms.ui.rap/pom.xml new file mode 100644 index 000000000..093c20852 --- /dev/null +++ b/rap/org.argeo.cms.ui.rap/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + + org.argeo.commons + rap + 2.3-SNAPSHOT + .. + + org.argeo.cms.ui.rap + CMS UI RAP + jar + + + org.argeo.commons + org.argeo.cms.ui + 2.3-SNAPSHOT + + + + org.argeo.commons + org.argeo.swt.specific.rap + 2.3-SNAPSHOT + provided + + + + + org.argeo.tp.rap.e4 + org.eclipse.rap.rwt + provided + + + org.argeo.tp.rap.e4 + org.eclipse.core.commands + provided + + + org.argeo.tp.rap.e4 + org.eclipse.rap.jface + provided + + + + + org.argeo.tp.rap.e4 + org.eclipse.rap.filedialog + provided + + + org.argeo.tp.rap.e4 + org.eclipse.rap.fileupload + provided + + + + \ No newline at end of file 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 new file mode 100644 index 000000000..30cff8f81 --- /dev/null +++ b/rap/org.argeo.cms.ui.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.NodeConstants; +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, NodeConstants.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 new file mode 100644 index 000000000..f72338ef7 --- /dev/null +++ b/rap/org.argeo.cms.ui.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/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 new file mode 100644 index 000000000..9dd1509be --- /dev/null +++ b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/CmsScriptApp.java @@ -0,0 +1,422 @@ +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.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.api.cms.CmsTheme; +import org.argeo.cms.CmsException; +import org.argeo.cms.ui.CmsConstants; +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 Log log = LogFactory.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(CmsConstants.LOADING_IMAGE, createResourceLoader(CmsConstants.LOADING_IMAGE)); + // empty image + application.addResource(CmsConstants.NO_IMAGE, createResourceLoader(CmsConstants.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 new file mode 100644 index 000000000..d7c1a63ce --- /dev/null +++ b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/CmsScriptRwtApplication.java @@ -0,0 +1,121 @@ +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.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.cms.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 Log log = LogFactory.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 new file mode 100644 index 000000000..7813156ec --- /dev/null +++ b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/ScriptAppActivator.java @@ -0,0 +1,46 @@ +package org.argeo.cms.ui.script; + +import javax.jcr.Repository; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +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 Log log = LogFactory.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 new file mode 100644 index 000000000..bf68fc299 --- /dev/null +++ b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/ScriptUi.java @@ -0,0 +1,116 @@ +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.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +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 Log log = LogFactory.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 new file mode 100644 index 000000000..be9618dcb --- /dev/null +++ b/rap/org.argeo.cms.ui.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/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 new file mode 100644 index 000000000..7440596ab --- /dev/null +++ b/rap/org.argeo.cms.ui.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/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 new file mode 100644 index 000000000..c20068fa7 --- /dev/null +++ b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/AbstractCmsEntryPoint.java @@ -0,0 +1,399 @@ +package org.argeo.cms.web; + +import static org.argeo.naming.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.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.api.NodeConstants; +import org.argeo.api.cms.CmsView; +import org.argeo.cms.CmsException; +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.auth.HttpRequestCallback; +import org.argeo.cms.auth.HttpRequestCallbackHandler; +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.argeo.jcr.JcrUtils; +import org.argeo.naming.AuthPassword; +import org.argeo.naming.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 Log log = LogFactory.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(NodeConstants.LOGIN_CONTEXT_USER, + new HttpRequestCallbackHandler(new ServletHttpRequest(UiContext.getHttpRequest()), + new ServletHttpResponse(UiContext.getHttpResponse()))); + lc.login(); + } catch (LoginException e) { + try { + lc = new LoginContext(NodeConstants.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(NodeConstants.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 HttpRequestCallback) { + ((HttpRequestCallback) callback) + .setRequest(new ServletHttpRequest(UiContext.getHttpRequest())); + ((HttpRequestCallback) callback) + .setResponse(new ServletHttpResponse(UiContext.getHttpResponse())); + } + } + } + }; + LoginContext lc = new LoginContext(NodeConstants.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 new file mode 100644 index 000000000..ca93e625e --- /dev/null +++ b/rap/org.argeo.cms.ui.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/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 new file mode 100644 index 000000000..5de0f9103 --- /dev/null +++ b/rap/org.argeo.cms.ui.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.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 new file mode 100644 index 000000000..e5b6c7efc --- /dev/null +++ b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsWebApp.java @@ -0,0 +1,165 @@ +package org.argeo.cms.web; + +import java.util.Dictionary; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.api.cms.CmsApp; +import org.argeo.api.cms.CmsAppListener; +import org.argeo.api.cms.CmsTheme; +import org.argeo.api.cms.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.Constants; +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 Log log = LogFactory.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 cmsAppId = properties.get(Constants.SERVICE_PID); + if (!cmsAppId.equals(this.cmsAppId)) + 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 new file mode 100644 index 000000000..d7050e954 --- /dev/null +++ b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsWebEntryPoint.java @@ -0,0 +1,358 @@ +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.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.api.NodeConstants; +import org.argeo.api.cms.CmsApp; +import org.argeo.api.cms.CmsImageManager; +import org.argeo.api.cms.CmsSession; +import org.argeo.api.cms.CmsUi; +import org.argeo.api.cms.CmsView; +import org.argeo.api.cms.UxContext; +import org.argeo.cms.LocaleUtils; +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.auth.HttpRequestCallbackHandler; +import org.argeo.cms.osgi.CmsOsgiUtils; +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 Log log = LogFactory.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(NodeConstants.LOGIN_CONTEXT_USER, + new HttpRequestCallbackHandler(new ServletHttpRequest(UiContext.getHttpRequest()), + new ServletHttpResponse(UiContext.getHttpResponse()))); + lc.login(); + } catch (LoginException e) { + try { + lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_ANONYMOUS); + 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(NodeConstants.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 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 = CmsOsgiUtils.getCmsSession(cmsWebApp.getBundleContext(), 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 { + if (!display.readAndDispatch()) { + display.sleep(); + } + } 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 new file mode 100644 index 000000000..2eff71ee8 --- /dev/null +++ b/rap/org.argeo.cms.ui.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/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 new file mode 100644 index 000000000..4cd6874d0 --- /dev/null +++ b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/SimpleApp.java @@ -0,0 +1,415 @@ +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.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.api.NodeConstants; +import org.argeo.cms.CmsException; +import org.argeo.cms.jcr.CmsJcrUtils; +import org.argeo.cms.ui.CmsConstants; +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 CmsConstants, ApplicationConfiguration { + private final static Log log = LogFactory.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(NodeConstants.ROLE_ANONYMOUS, NodeConstants.ROLE_USER); + private List rwPrincipals = Arrays.asList(NodeConstants.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 new file mode 100644 index 000000000..6e1165cf3 --- /dev/null +++ b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/SimpleErgonomics.java @@ -0,0 +1,239 @@ +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.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.api.cms.CmsImageManager; +import org.argeo.api.cms.UxContext; +import org.argeo.cms.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 Log log = LogFactory.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 new file mode 100644 index 000000000..a28b13fc6 --- /dev/null +++ b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/WebThemeUtils.java @@ -0,0 +1,29 @@ +package org.argeo.cms.web; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.api.cms.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 Log log = LogFactory.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/eclipse/ui/jetty/RwtRunner.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/eclipse/ui/jetty/RwtRunner.java new file mode 100644 index 000000000..29165a4ef --- /dev/null +++ b/rap/org.argeo.cms.ui.rap/src/org/argeo/eclipse/ui/jetty/RwtRunner.java @@ -0,0 +1,135 @@ +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 new file mode 100644 index 000000000..e03d341b1 --- /dev/null +++ b/rap/org.argeo.swt.specific.rap/.classpath @@ -0,0 +1,9 @@ + + + + + + + diff --git a/rap/org.argeo.swt.specific.rap/.project b/rap/org.argeo.swt.specific.rap/.project new file mode 100644 index 000000000..53d797685 --- /dev/null +++ b/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/rap/org.argeo.swt.specific.rap/META-INF/.gitignore b/rap/org.argeo.swt.specific.rap/META-INF/.gitignore new file mode 100644 index 000000000..4854a41b9 --- /dev/null +++ b/rap/org.argeo.swt.specific.rap/META-INF/.gitignore @@ -0,0 +1 @@ +/MANIFEST.MF diff --git a/rap/org.argeo.swt.specific.rap/bnd.bnd b/rap/org.argeo.swt.specific.rap/bnd.bnd new file mode 100644 index 000000000..76bb82c80 --- /dev/null +++ b/rap/org.argeo.swt.specific.rap/bnd.bnd @@ -0,0 +1,5 @@ +Import-Package: org.eclipse.swt,\ +org.eclipse.jface.dialogs,\ +javax.servlet.http,\ +org.eclipse.swt.events,\ +* diff --git a/rap/org.argeo.swt.specific.rap/build.properties b/rap/org.argeo.swt.specific.rap/build.properties new file mode 100644 index 000000000..fd806ca05 --- /dev/null +++ b/rap/org.argeo.swt.specific.rap/build.properties @@ -0,0 +1,2 @@ +source.. = src/ +output.. = bin/ diff --git a/rap/org.argeo.swt.specific.rap/pom.xml b/rap/org.argeo.swt.specific.rap/pom.xml new file mode 100644 index 000000000..b889b95d3 --- /dev/null +++ b/rap/org.argeo.swt.specific.rap/pom.xml @@ -0,0 +1,43 @@ + + + 4.0.0 + + org.argeo.commons + 2.3-SNAPSHOT + rap + .. + + org.argeo.swt.specific.rap + SWT RAP Specific + + + + org.argeo.tp.rap.e4 + org.eclipse.rap.rwt + provided + + + org.argeo.tp.rap.e4 + org.eclipse.core.commands + provided + + + org.argeo.tp.rap.e4 + org.eclipse.rap.jface + provided + + + + + org.argeo.tp.rap.e4 + org.eclipse.rap.filedialog + provided + + + org.argeo.tp.rap.e4 + org.eclipse.rap.fileupload + provided + + + + \ No newline at end of file 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 new file mode 100644 index 000000000..6100c1a83 --- /dev/null +++ b/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/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 new file mode 100644 index 000000000..3f30bde25 --- /dev/null +++ b/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/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 new file mode 100644 index 000000000..a89b921cd --- /dev/null +++ b/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/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 new file mode 100644 index 000000000..f9ca81682 --- /dev/null +++ b/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/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 new file mode 100644 index 000000000..72e17a22d --- /dev/null +++ b/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/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 new file mode 100644 index 000000000..4ec451f8a --- /dev/null +++ b/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/rap/pom.xml b/rap/pom.xml new file mode 100644 index 000000000..60373a270 --- /dev/null +++ b/rap/pom.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + + org.argeo.commons + argeo-commons + 2.3-SNAPSHOT + .. + + rap + Eclipse RAP Specific + pom + + org.argeo.swt.specific.rap + org.argeo.cms.ui.rap + org.argeo.cms.e4.rap + + \ No newline at end of file