From: Mathieu Baudier Date: Wed, 14 Oct 2020 05:14:35 +0000 (+0200) Subject: Introduce separate CMS UI RAP bundle. X-Git-Tag: argeo-commons-2.1.89~68 X-Git-Url: https://git.argeo.org/?p=lgpl%2Fargeo-commons.git;a=commitdiff_plain;h=94bd4b03ff3ca9d1875bf6424383d648e604b93a Introduce separate CMS UI RAP bundle. --- diff --git a/dep/org.argeo.dep.cms.ui.rap/pom.xml b/dep/org.argeo.dep.cms.ui.rap/pom.xml index c9b195d0a..2fb8cd5d7 100644 --- a/dep/org.argeo.dep.cms.ui.rap/pom.xml +++ b/dep/org.argeo.dep.cms.ui.rap/pom.xml @@ -61,6 +61,11 @@ org.argeo.cms.ui 2.1.89-SNAPSHOT + + org.argeo.commons + org.argeo.cms.ui.rap + 2.1.89-SNAPSHOT + org.argeo.commons org.argeo.cms.ui.theme diff --git a/org.argeo.cms.ui.rap/.classpath b/org.argeo.cms.ui.rap/.classpath new file mode 100644 index 000000000..e801ebfb4 --- /dev/null +++ b/org.argeo.cms.ui.rap/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/org.argeo.cms.ui.rap/.gitignore b/org.argeo.cms.ui.rap/.gitignore new file mode 100644 index 000000000..09e3bc9b2 --- /dev/null +++ b/org.argeo.cms.ui.rap/.gitignore @@ -0,0 +1,2 @@ +/bin/ +/target/ diff --git a/org.argeo.cms.ui.rap/.project b/org.argeo.cms.ui.rap/.project new file mode 100644 index 000000000..1a37a6771 --- /dev/null +++ b/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/org.argeo.cms.ui.rap/META-INF/.gitignore b/org.argeo.cms.ui.rap/META-INF/.gitignore new file mode 100644 index 000000000..4854a41b9 --- /dev/null +++ b/org.argeo.cms.ui.rap/META-INF/.gitignore @@ -0,0 +1 @@ +/MANIFEST.MF diff --git a/org.argeo.cms.ui.rap/bnd.bnd b/org.argeo.cms.ui.rap/bnd.bnd new file mode 100644 index 000000000..7f5c929e5 --- /dev/null +++ b/org.argeo.cms.ui.rap/bnd.bnd @@ -0,0 +1,7 @@ +Import-Package:\ +org.eclipse.swt,\ +org.argeo.eclipse.ui,\ +javax.jcr.nodetype,\ +javax.jcr.security,\ +org.eclipse.swt.graphics,\ +* diff --git a/org.argeo.cms.ui.rap/build.properties b/org.argeo.cms.ui.rap/build.properties new file mode 100644 index 000000000..34d2e4d2d --- /dev/null +++ b/org.argeo.cms.ui.rap/build.properties @@ -0,0 +1,4 @@ +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 new file mode 100644 index 000000000..5a65d6dca --- /dev/null +++ b/org.argeo.cms.ui.rap/pom.xml @@ -0,0 +1,64 @@ + + + 4.0.0 + + org.argeo.commons + argeo-commons + 2.1.89-SNAPSHOT + .. + + org.argeo.cms.ui.rap + CMS UI RAP + jar + + + org.argeo.commons + org.argeo.cms.ui + 2.1.89-SNAPSHOT + + + + org.argeo.commons + org.argeo.eclipse.ui.rap + 2.1.89-SNAPSHOT + provided + + + + + org.argeo.commons + org.argeo.cms.ui.theme + 2.1.89-SNAPSHOT + + + + + 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 new file mode 100644 index 000000000..ad7cadc13 --- /dev/null +++ b/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/AppUi.java @@ -0,0 +1,266 @@ +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.cms.ui.CmsUiProvider; +import org.argeo.cms.ui.util.CmsPane; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.argeo.cms.web.SimpleErgonomics; +import org.argeo.eclipse.ui.Selected; +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, "main", "/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 + CmsUiUtils.style(cmsPane.getQaArea(), "qa"); + Button reload = new Button(cmsPane.getQaArea(), SWT.FLAT); + CmsUiUtils.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 + CmsUiUtils.style(cmsPane.getSupportArea(), "support"); + Label msg = new Label(cmsPane.getSupportArea(), SWT.NONE); + CmsUiUtils.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 new file mode 100644 index 000000000..f72338ef7 --- /dev/null +++ b/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/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 new file mode 100644 index 000000000..3f0987199 --- /dev/null +++ b/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.cms.CmsException; +import org.argeo.cms.ui.CmsConstants; +import org.argeo.cms.ui.CmsTheme; +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 new file mode 100644 index 000000000..d7c1a63ce --- /dev/null +++ b/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/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 new file mode 100644 index 000000000..7813156ec --- /dev/null +++ b/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/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 new file mode 100644 index 000000000..bf68fc299 --- /dev/null +++ b/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/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 new file mode 100644 index 000000000..be9618dcb --- /dev/null +++ b/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/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 new file mode 100644 index 000000000..7440596ab --- /dev/null +++ b/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/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 new file mode 100644 index 000000000..bdc4f24bb --- /dev/null +++ b/org.argeo.cms.ui.rap/src/org/argeo/cms/web/AbstractCmsEntryPoint.java @@ -0,0 +1,392 @@ +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.cms.CmsException; +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.auth.HttpRequestCallback; +import org.argeo.cms.auth.HttpRequestCallbackHandler; +import org.argeo.cms.ui.CmsStyles; +import org.argeo.cms.ui.CmsView; +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 */ +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(UiContext.getHttpRequest(), 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); + CmsView.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(UiContext.getHttpRequest()); + ((HttpRequestCallback) callback).setResponse(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 new file mode 100644 index 000000000..e6ad61db6 --- /dev/null +++ b/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.argeo.cms.CmsException; +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 CmsException("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 new file mode 100644 index 000000000..0cf9a7269 --- /dev/null +++ b/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.cms.ui.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 new file mode 100644 index 000000000..03ac353df --- /dev/null +++ b/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsWebApp.java @@ -0,0 +1,137 @@ +package org.argeo.cms.web; + +import java.util.Dictionary; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.cms.ui.CmsApp; +import org.argeo.cms.ui.CmsAppListener; +import org.argeo.cms.ui.CmsTheme; +import org.argeo.util.LangUtils; +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; +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, CmsAppListener { + private final static Log log = LogFactory.getLog(CmsWebApp.class); + + private BundleContext bundleContext; + private CmsApp cmsApp; + private EventAdmin eventAdmin; + + private ServiceRegistration rwtAppReg; + + private final static String CONTEXT_NAME = "contextName"; + private String contextName; + + public void init(BundleContext bundleContext, Map properties) { + this.bundleContext = bundleContext; + contextName = properties.get(CONTEXT_NAME); + if (cmsApp != null) + themingUpdated(); +// registerIfAllThemesAvailable(); + } + + public void destroy(BundleContext bundleContext, Map properties) { + if (cmsApp != null) + cmsApp.removeCmsAppListener(this); + } + + @Override + public void configure(Application application) { + for (String uiName : cmsApp.getUiNames()) { + CmsTheme theme = cmsApp.getTheme(uiName); + if (theme != null) + WebThemeUtils.apply(application, theme); + } +// for (CmsTheme theme : themes.values()) +// WebThemeUtils.apply(application, theme); + + Map properties = new HashMap<>(); + addEntryPoints(application, properties); + + } + + 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()); + } else { + properties.put(WebClient.THEME_ID, RWT.DEFAULT_THEME_ID); +// if (themeId != null) +// log.warn("Theme id " + themeId + " was specified but it was not found, using default RWT theme."); + } + application.addEntryPoint("/" + uiName, () -> { + CmsWebEntryPoint entryPoint = new CmsWebEntryPoint(this, uiName); + entryPoint.setEventAdmin(eventAdmin); + return entryPoint; + }, properties); + if (log.isDebugEnabled()) + log.info("Added web entry point /" + (contextName != null ? contextName : "") + "/" + uiName); + } + log.debug("Published CMS web app /" + (contextName != null ? contextName : "")); + } + +// private void registerIfAllThemesAvailable() { +// boolean themeMissing = false; +// uiNames: for (String uiName : cmsApp.getUiNames()) { +// String themeId = cmsApp.getThemeId(uiName); +// if (RWT.DEFAULT_THEME_ID.equals(themeId)) +// continue uiNames; +// if (!themes.containsKey(themeId)) { +// themeMissing = true; +// break uiNames; +// } +// } +// if (!themeMissing) { +// Dictionary regProps = LangUtils.dict(CONTEXT_NAME, contextName); +// if (bundleContext != null) { +// rwtAppReg = bundleContext.registerService(ApplicationConfiguration.class, this, regProps); +// log.info("Published CMS web app /" + (contextName != null ? contextName : "")); +// } +// } +// } + + CmsApp getCmsApp() { + return cmsApp; + } + + public void setCmsApp(CmsApp cmsApp, Map properties) { + this.cmsApp = cmsApp; + this.cmsApp.addCmsAppListener(this); +// registerIfAllThemesAvailable(); + } + + public void unsetCmsApp(CmsApp cmsApp, Map properties) { + if (rwtAppReg != null) + rwtAppReg.unregister(); + this.cmsApp = null; + } + + @Override + public void themingUpdated() { + 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 new file mode 100644 index 000000000..2961eead9 --- /dev/null +++ b/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsWebEntryPoint.java @@ -0,0 +1,271 @@ +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.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.cms.auth.CurrentUser; +import org.argeo.cms.auth.HttpRequestCallbackHandler; +import org.argeo.cms.ui.CmsApp; +import org.argeo.cms.ui.CmsImageManager; +import org.argeo.cms.ui.CmsView; +import org.argeo.cms.ui.UxContext; +import org.argeo.cms.ui.dialogs.CmsFeedback; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.argeo.cms.ui.util.DefaultImageManager; +import org.argeo.cms.ui.util.SimpleUxContext; +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.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}. */ +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 Composite 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(UiContext.getHttpRequest(), 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 SimpleUxContext(); + imageManager = new DefaultImageManager(); + ui = cmsWebApp.getCmsApp().initUi(parent); + ui.setData(CmsApp.UI_NAME_PROPERTY, uiName); + ui.setLayoutData(CmsUiUtils.fillAll()); + } catch (Exception e) { + throw new IllegalStateException("Cannot create entrypoint contents", e); + } + return null; + } + }); + } + + protected Subject getSubject() { + return loginContext.getSubject(); + } + + @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) { + 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); + doRefresh(); + if (browserNavigation != null) + browserNavigation.pushState(state, title); + } + + @Override + 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)); + } + + /* + * EntryPoint IMPLEMENTATION + */ + + @Override + public int createUI() { + Display display = new Display(); + Shell shell = createShell(display); + shell.setLayout(CmsUiUtils.noSpaceGridLayout()); + CmsView.registerCmsView(shell, this); + createContents(shell); + shell.layout(); +// if (shell.getMaximized()) { +// shell.layout(); +// } else { +//// shell.pack(); +// } + shell.open(); + if (getApplicationContext().getLifeCycleFactory().getLifeCycle() instanceof RWTLifeCycle) { + while (!shell.isDisposed()) { + if (!display.readAndDispatch()) { + display.sleep(); + } + } + 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 new file mode 100644 index 000000000..188391c42 --- /dev/null +++ b/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.ui.util.BundleCmsTheme.CMS_THEME_BUNDLE_PROPERTY; + +import java.util.HashMap; +import java.util.Map; + +import org.argeo.cms.ui.util.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 new file mode 100644 index 000000000..2a651bd30 --- /dev/null +++ b/org.argeo.cms.ui.rap/src/org/argeo/cms/web/SimpleApp.java @@ -0,0 +1,414 @@ +package org.argeo.cms.web; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.security.Privilege; +import javax.jcr.version.VersionManager; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.api.NodeConstants; +import org.argeo.api.NodeUtils; +import org.argeo.cms.CmsException; +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}. */ +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 = NodeUtils.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 new file mode 100644 index 000000000..7e17770e6 --- /dev/null +++ b/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.cms.CmsException; +import org.argeo.cms.ui.CmsImageManager; +import org.argeo.cms.ui.CmsStyles; +import org.argeo.cms.ui.CmsUiProvider; +import org.argeo.cms.ui.UxContext; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.argeo.cms.ui.util.DefaultImageManager; +import org.argeo.cms.ui.util.SimpleUxContext; +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. */ +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(CmsUiUtils.noSpaceGridLayout(new GridLayout(3, false))); + + uxContext = new SimpleUxContext(); + 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(CmsUiUtils.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(CmsUiUtils.noSpaceGridLayout()); + + // TODO: bi-directional + rightArea = new Composite(parent, SWT.NONE); + rightArea.setLayoutData(new GridData(SWT.END, SWT.TOP, false, false)); + rightArea.setLayout(CmsUiUtils.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(); + CmsUiUtils.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(CmsUiUtils.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; + } + + @Override + 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 new file mode 100644 index 000000000..a71ff97d3 --- /dev/null +++ b/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.cms.ui.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/src/org/argeo/cms/ui/script/AppUi.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/script/AppUi.java deleted file mode 100644 index ad7cadc13..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/ui/script/AppUi.java +++ /dev/null @@ -1,266 +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.cms.ui.CmsUiProvider; -import org.argeo.cms.ui.util.CmsPane; -import org.argeo.cms.ui.util.CmsUiUtils; -import org.argeo.cms.web.SimpleErgonomics; -import org.argeo.eclipse.ui.Selected; -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, "main", "/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 - CmsUiUtils.style(cmsPane.getQaArea(), "qa"); - Button reload = new Button(cmsPane.getQaArea(), SWT.FLAT); - CmsUiUtils.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 - CmsUiUtils.style(cmsPane.getSupportArea(), "support"); - Label msg = new Label(cmsPane.getSupportArea(), SWT.NONE); - CmsUiUtils.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/src/org/argeo/cms/ui/script/Branding.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/script/Branding.java deleted file mode 100644 index f72338ef7..000000000 --- a/org.argeo.cms.ui/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/src/org/argeo/cms/ui/script/CmsScriptApp.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/script/CmsScriptApp.java deleted file mode 100644 index 3f0987199..000000000 --- a/org.argeo.cms.ui/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.cms.CmsException; -import org.argeo.cms.ui.CmsConstants; -import org.argeo.cms.ui.CmsTheme; -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/src/org/argeo/cms/ui/script/CmsScriptRwtApplication.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/script/CmsScriptRwtApplication.java deleted file mode 100644 index d7c1a63ce..000000000 --- a/org.argeo.cms.ui/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/src/org/argeo/cms/ui/script/ScriptAppActivator.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/script/ScriptAppActivator.java deleted file mode 100644 index 7813156ec..000000000 --- a/org.argeo.cms.ui/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/src/org/argeo/cms/ui/script/ScriptUi.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/script/ScriptUi.java deleted file mode 100644 index bf68fc299..000000000 --- a/org.argeo.cms.ui/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/src/org/argeo/cms/ui/script/cms.js b/org.argeo.cms.ui/src/org/argeo/cms/ui/script/cms.js deleted file mode 100644 index be9618dcb..000000000 --- a/org.argeo.cms.ui/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/src/org/argeo/cms/ui/script/package-info.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/script/package-info.java deleted file mode 100644 index 7440596ab..000000000 --- a/org.argeo.cms.ui/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/src/org/argeo/cms/ui/util/SimpleApp.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleApp.java deleted file mode 100644 index 149fff8ac..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleApp.java +++ /dev/null @@ -1,414 +0,0 @@ -package org.argeo.cms.ui.util; - -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.api.NodeUtils; -import org.argeo.cms.CmsException; -import org.argeo.cms.ui.CmsConstants; -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.cms.ui.LifeCycleUiProvider; -import org.argeo.cms.web.BundleResourceLoader; -import org.argeo.cms.web.SimpleErgonomics; -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}. */ -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 = NodeUtils.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/src/org/argeo/cms/web/AbstractCmsEntryPoint.java b/org.argeo.cms.ui/src/org/argeo/cms/web/AbstractCmsEntryPoint.java deleted file mode 100644 index bdc4f24bb..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/web/AbstractCmsEntryPoint.java +++ /dev/null @@ -1,392 +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.cms.CmsException; -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.auth.HttpRequestCallback; -import org.argeo.cms.auth.HttpRequestCallbackHandler; -import org.argeo.cms.ui.CmsStyles; -import org.argeo.cms.ui.CmsView; -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 */ -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(UiContext.getHttpRequest(), 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); - CmsView.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(UiContext.getHttpRequest()); - ((HttpRequestCallback) callback).setResponse(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/src/org/argeo/cms/web/BundleResourceLoader.java b/org.argeo.cms.ui/src/org/argeo/cms/web/BundleResourceLoader.java deleted file mode 100644 index e6ad61db6..000000000 --- a/org.argeo.cms.ui/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.argeo.cms.CmsException; -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 CmsException("Resource " + resourceName + " not found in bundle " + bundle.getSymbolicName()); - } - return res.openStream(); - } - - public Bundle getBundle() { - return bundle; - } - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/web/CmsThemeResourceLoader.java b/org.argeo.cms.ui/src/org/argeo/cms/web/CmsThemeResourceLoader.java deleted file mode 100644 index 0cf9a7269..000000000 --- a/org.argeo.cms.ui/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.cms.ui.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/src/org/argeo/cms/web/CmsWebApp.java b/org.argeo.cms.ui/src/org/argeo/cms/web/CmsWebApp.java deleted file mode 100644 index 03ac353df..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/web/CmsWebApp.java +++ /dev/null @@ -1,137 +0,0 @@ -package org.argeo.cms.web; - -import java.util.Dictionary; -import java.util.HashMap; -import java.util.Map; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.cms.ui.CmsApp; -import org.argeo.cms.ui.CmsAppListener; -import org.argeo.cms.ui.CmsTheme; -import org.argeo.util.LangUtils; -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; -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, CmsAppListener { - private final static Log log = LogFactory.getLog(CmsWebApp.class); - - private BundleContext bundleContext; - private CmsApp cmsApp; - private EventAdmin eventAdmin; - - private ServiceRegistration rwtAppReg; - - private final static String CONTEXT_NAME = "contextName"; - private String contextName; - - public void init(BundleContext bundleContext, Map properties) { - this.bundleContext = bundleContext; - contextName = properties.get(CONTEXT_NAME); - if (cmsApp != null) - themingUpdated(); -// registerIfAllThemesAvailable(); - } - - public void destroy(BundleContext bundleContext, Map properties) { - if (cmsApp != null) - cmsApp.removeCmsAppListener(this); - } - - @Override - public void configure(Application application) { - for (String uiName : cmsApp.getUiNames()) { - CmsTheme theme = cmsApp.getTheme(uiName); - if (theme != null) - WebThemeUtils.apply(application, theme); - } -// for (CmsTheme theme : themes.values()) -// WebThemeUtils.apply(application, theme); - - Map properties = new HashMap<>(); - addEntryPoints(application, properties); - - } - - 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()); - } else { - properties.put(WebClient.THEME_ID, RWT.DEFAULT_THEME_ID); -// if (themeId != null) -// log.warn("Theme id " + themeId + " was specified but it was not found, using default RWT theme."); - } - application.addEntryPoint("/" + uiName, () -> { - CmsWebEntryPoint entryPoint = new CmsWebEntryPoint(this, uiName); - entryPoint.setEventAdmin(eventAdmin); - return entryPoint; - }, properties); - if (log.isDebugEnabled()) - log.info("Added web entry point /" + (contextName != null ? contextName : "") + "/" + uiName); - } - log.debug("Published CMS web app /" + (contextName != null ? contextName : "")); - } - -// private void registerIfAllThemesAvailable() { -// boolean themeMissing = false; -// uiNames: for (String uiName : cmsApp.getUiNames()) { -// String themeId = cmsApp.getThemeId(uiName); -// if (RWT.DEFAULT_THEME_ID.equals(themeId)) -// continue uiNames; -// if (!themes.containsKey(themeId)) { -// themeMissing = true; -// break uiNames; -// } -// } -// if (!themeMissing) { -// Dictionary regProps = LangUtils.dict(CONTEXT_NAME, contextName); -// if (bundleContext != null) { -// rwtAppReg = bundleContext.registerService(ApplicationConfiguration.class, this, regProps); -// log.info("Published CMS web app /" + (contextName != null ? contextName : "")); -// } -// } -// } - - CmsApp getCmsApp() { - return cmsApp; - } - - public void setCmsApp(CmsApp cmsApp, Map properties) { - this.cmsApp = cmsApp; - this.cmsApp.addCmsAppListener(this); -// registerIfAllThemesAvailable(); - } - - public void unsetCmsApp(CmsApp cmsApp, Map properties) { - if (rwtAppReg != null) - rwtAppReg.unregister(); - this.cmsApp = null; - } - - @Override - public void themingUpdated() { - 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/src/org/argeo/cms/web/CmsWebEntryPoint.java b/org.argeo.cms.ui/src/org/argeo/cms/web/CmsWebEntryPoint.java deleted file mode 100644 index 2961eead9..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/web/CmsWebEntryPoint.java +++ /dev/null @@ -1,271 +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.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.cms.auth.CurrentUser; -import org.argeo.cms.auth.HttpRequestCallbackHandler; -import org.argeo.cms.ui.CmsApp; -import org.argeo.cms.ui.CmsImageManager; -import org.argeo.cms.ui.CmsView; -import org.argeo.cms.ui.UxContext; -import org.argeo.cms.ui.dialogs.CmsFeedback; -import org.argeo.cms.ui.util.CmsUiUtils; -import org.argeo.cms.ui.util.DefaultImageManager; -import org.argeo.cms.ui.util.SimpleUxContext; -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.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}. */ -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 Composite 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(UiContext.getHttpRequest(), 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 SimpleUxContext(); - imageManager = new DefaultImageManager(); - ui = cmsWebApp.getCmsApp().initUi(parent); - ui.setData(CmsApp.UI_NAME_PROPERTY, uiName); - ui.setLayoutData(CmsUiUtils.fillAll()); - } catch (Exception e) { - throw new IllegalStateException("Cannot create entrypoint contents", e); - } - return null; - } - }); - } - - protected Subject getSubject() { - return loginContext.getSubject(); - } - - @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) { - 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); - doRefresh(); - if (browserNavigation != null) - browserNavigation.pushState(state, title); - } - - @Override - 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)); - } - - /* - * EntryPoint IMPLEMENTATION - */ - - @Override - public int createUI() { - Display display = new Display(); - Shell shell = createShell(display); - shell.setLayout(CmsUiUtils.noSpaceGridLayout()); - CmsView.registerCmsView(shell, this); - createContents(shell); - shell.layout(); -// if (shell.getMaximized()) { -// shell.layout(); -// } else { -//// shell.pack(); -// } - shell.open(); - if (getApplicationContext().getLifeCycleFactory().getLifeCycle() instanceof RWTLifeCycle) { - while (!shell.isDisposed()) { - if (!display.readAndDispatch()) { - display.sleep(); - } - } - 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/src/org/argeo/cms/web/MinimalWebApp.java b/org.argeo.cms.ui/src/org/argeo/cms/web/MinimalWebApp.java deleted file mode 100644 index 188391c42..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/web/MinimalWebApp.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.argeo.cms.web; - -import static org.argeo.cms.ui.util.BundleCmsTheme.CMS_THEME_BUNDLE_PROPERTY; - -import java.util.HashMap; -import java.util.Map; - -import org.argeo.cms.ui.util.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/src/org/argeo/cms/web/SimpleErgonomics.java b/org.argeo.cms.ui/src/org/argeo/cms/web/SimpleErgonomics.java deleted file mode 100644 index 7e17770e6..000000000 --- a/org.argeo.cms.ui/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.cms.CmsException; -import org.argeo.cms.ui.CmsImageManager; -import org.argeo.cms.ui.CmsStyles; -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.cms.ui.UxContext; -import org.argeo.cms.ui.util.CmsUiUtils; -import org.argeo.cms.ui.util.DefaultImageManager; -import org.argeo.cms.ui.util.SimpleUxContext; -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. */ -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(CmsUiUtils.noSpaceGridLayout(new GridLayout(3, false))); - - uxContext = new SimpleUxContext(); - 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(CmsUiUtils.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(CmsUiUtils.noSpaceGridLayout()); - - // TODO: bi-directional - rightArea = new Composite(parent, SWT.NONE); - rightArea.setLayoutData(new GridData(SWT.END, SWT.TOP, false, false)); - rightArea.setLayout(CmsUiUtils.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(); - CmsUiUtils.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(CmsUiUtils.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; - } - - @Override - 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/src/org/argeo/cms/web/WebThemeUtils.java b/org.argeo.cms.ui/src/org/argeo/cms/web/WebThemeUtils.java deleted file mode 100644 index a71ff97d3..000000000 --- a/org.argeo.cms.ui/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.cms.ui.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/pom.xml b/pom.xml index 5c94eef04..04395f9fc 100644 --- a/pom.xml +++ b/pom.xml @@ -37,6 +37,7 @@ org.argeo.cms org.argeo.cms.ui.theme org.argeo.cms.ui + org.argeo.cms.ui.rap org.argeo.cms.e4 org.argeo.cms.e4.rap