--- /dev/null
+package org.argeo.cms.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.util.CmsPane;
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.cms.util.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();
+
+ // 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), scriptPath);
+ }
+
+ public AppUi(CmsScriptApp app, CmsUiProvider uiProvider) {
+ this.app = app;
+ this.ui = uiProvider;
+ }
+
+ public void apply(Repository repository, Application application, Branding appBranding, String path) {
+ Map<String, String> factoryProperties = new HashMap<>();
+ if (appBranding != null)
+ appBranding.applyBranding(factoryProperties);
+ applyBranding(factoryProperties);
+ EntryPointFactory entryPointFactory = new EntryPointFactory() {
+ @Override
+ public EntryPoint create() {
+ SimpleErgonomics ergonomics = new SimpleErgonomics(repository, "main", "/home/root/argeo:keyring",
+ AppUi.this, factoryProperties);
+ return ergonomics;
+ }
+ };
+ application.addEntryPoint("/" + path, entryPointFactory, factoryProperties);
+ }
+
+ public void setUi(CmsUiProvider uiProvider) {
+ this.ui = uiProvider;
+ }
+
+ public void applyBranding(Map<String, String> 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);
+ // QA
+ CmsUtils.style(cmsPane.getQaArea(), "qa");
+ Button reload = new Button(cmsPane.getQaArea(), SWT.FLAT);
+ CmsUtils.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
+ CmsUtils.style(cmsPane.getSupportArea(), "support");
+ Label msg = new Label(cmsPane.getSupportArea(), SWT.NONE);
+ CmsUtils.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;
+ }
+
+}
--- /dev/null
+package org.argeo.cms.script;
+
+import java.util.Map;
+
+import org.eclipse.rap.rwt.client.WebClient;
+
+public interface Branding {
+ public void applyBranding(Map<String, String> properties);
+
+ public String getThemeId();
+
+ public String getAdditionalHeaders();
+
+ public String getBodyHtml();
+
+ public String getPageTitle();
+
+ public String getPageOverflow();
+
+ public String getFavicon();
+
+}
--- /dev/null
+package org.argeo.cms.script;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+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 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.util.BundleResourceLoader;
+import org.argeo.cms.util.CmsUtils;
+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.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+public class CmsScriptApp implements Branding {
+ public final static String CONTEXT_NAME = "contextName";
+
+ ServiceRegistration<ApplicationConfiguration> 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 Theme theme;
+
+ private List<String> resources = new ArrayList<>();
+
+ private Map<String, AppUi> ui = new HashMap<>();
+
+ // 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) {
+ theme.apply(application);
+ String themeHeaders = theme.getAdditionalHeaders();
+ if (themeHeaders != null) {
+ if (additionalHeaders == null)
+ additionalHeaders = themeHeaders;
+ else
+ additionalHeaders = themeHeaders + "\n" + additionalHeaders;
+ }
+ themeId = theme.getThemeId();
+ }
+
+ for (String appUiName : ui.keySet()) {
+ AppUi appUi = ui.get(appUiName);
+ appUi.apply(repository, application, this, appUiName);
+ }
+
+ }
+
+ public void register(BundleContext bundleContext, ApplicationConfiguration appConfig) {
+ Hashtable<String, String> 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<String> 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<String, String> 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
+ CmsUtils.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 Theme getTheme() {
+ return theme;
+ }
+
+ public void setTheme(Theme 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<String, AppUi> getUi() {
+ return ui;
+ }
+
+ public void setUi(Map<String, AppUi> 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;
+ }
+
+}
--- /dev/null
+package org.argeo.cms.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;
+ // System.out.println("bundleContext=" + bundleContext);
+ // System.out.println("repository=" + repository);
+ ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
+ ClassLoader bundleCl = bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader();
+ ClassLoader currentCcl = Thread.currentThread().getContextClassLoader();
+ try {
+ Thread.currentThread().setContextClassLoader(bundleCl);
+ engine = scriptEngineManager.getEngineByName("JavaScript");
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ Thread.currentThread().setContextClassLoader(currentCcl);
+ }
+
+ // 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);
+
+ }
+
+ 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;
+ }
+
+}
--- /dev/null
+package org.argeo.cms.script;
+
+import javax.jcr.Repository;
+
+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 {
+// ServiceRegistration<ApplicationConfiguration> appConfigReg;
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ CmsScriptRwtApplication appConfig = new CmsScriptRwtApplication();
+ appConfig.init(context);
+ CmsScriptApp app = appConfig.getApp();
+ ServiceTracker<Repository, Repository> repoSt = new ServiceTracker<Repository, Repository>(context,
+ FrameworkUtil.createFilter("(&" + app.getRepo() + "(objectClass=javax.jcr.Repository))"), null) {
+
+ @Override
+ public Repository addingService(ServiceReference<Repository> reference) {
+ Repository repository = super.addingService(reference);
+ appConfig.setRepository(repository);
+ CmsScriptApp app = appConfig.getApp();
+ app.register(context, appConfig);
+// Hashtable<String, String> props = new Hashtable<>();
+// if (app.getWebPath() != null)
+// props.put("contextName", app.getWebPath());
+// appConfigReg = context.registerService(ApplicationConfiguration.class, appConfig, props);
+ return repository;
+ }
+
+ };
+ repoSt.open();
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+// if (appConfigReg != null)
+// appConfigReg.unregister();
+
+ }
+
+}
--- /dev/null
+package org.argeo.cms.script;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.URL;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.script.Invocable;
+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.ui.CmsUiProvider;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.wiring.BundleWiring;
+
+public 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, String path) {
+ ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
+ ClassLoader bundleCl = bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader();
+ ClassLoader currentCcl = Thread.currentThread().getContextClassLoader();
+ try {
+ Thread.currentThread().setContextClassLoader(bundleCl);
+ scriptEngine = scriptEngineManager.getEngineByName("JavaScript");
+ scriptEngine.put(CmsScriptRwtApplication.BC, bundleContext);
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ Thread.currentThread().setContextClassLoader(currentCcl);
+ }
+ 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);
+ }
+ }
+
+ // 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.isDebugEnabled())
+ log.debug(appUrl + " UI in " + duration + " ms");
+ return null;
+ }
+
+}
--- /dev/null
+package org.argeo.cms.script;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.util.BundleResourceLoader;
+import org.argeo.cms.util.ThemeUtils;
+import org.eclipse.rap.rwt.application.Application;
+import org.eclipse.rap.rwt.service.ResourceLoader;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+public class Theme {
+ private final static Log log = LogFactory.getLog(Theme.class);
+
+ private String themeId;
+ private Map<String, ResourceLoader> css = new HashMap<>();
+ private Map<String, ResourceLoader> resources = new HashMap<>();
+
+ private String headerCss;
+ private List<String> fonts = new ArrayList<>();
+
+ public Theme(BundleContext bundleContext, String symbolicName) {
+ this.themeId = symbolicName;
+ Bundle themeBundle = ThemeUtils.findThemeBundle(bundleContext, symbolicName);
+ addStyleSheets(themeBundle, new BundleResourceLoader(themeBundle));
+ BundleResourceLoader themeBRL = new BundleResourceLoader(themeBundle);
+ addResources(themeBRL, "*.png");
+ addResources(themeBRL, "*.gif");
+ addResources(themeBRL, "*.jpg");
+ addResources(themeBRL, "*.jpeg");
+ addResources(themeBRL, "*.svg");
+ addResources(themeBRL, "*.ico");
+
+ // fonts
+ URL fontsUrl = themeBundle.getEntry("fonts.txt");
+ if (fontsUrl != null) {
+ loadFontsUrl(fontsUrl);
+ }
+
+ // common CSS header (plain CSS)
+ URL headerCssUrl = themeBundle.getEntry("header.css");
+ if (headerCssUrl != null) {
+ try (BufferedReader buffer = new BufferedReader(new InputStreamReader(headerCssUrl.openStream(), UTF_8))) {
+ headerCss = buffer.lines().collect(Collectors.joining("\n"));
+ } catch (IOException e) {
+ throw new CmsException("Cannot read " + headerCssUrl, e);
+ }
+ }
+
+ }
+
+ public void apply(Application application) {
+ for (String name : resources.keySet()) {
+ application.addResource(name, resources.get(name));
+ }
+ for (String name : css.keySet()) {
+ application.addStyleSheet(themeId, name, css.get(name));
+ }
+ }
+
+ public String getAdditionalHeaders() {
+ StringBuilder sb = new StringBuilder();
+ if (headerCss != null) {
+ sb.append("<style type='text/css'>\n");
+ sb.append(headerCss);
+ sb.append("\n</style>\n");
+ }
+ for (String link : fonts) {
+ sb.append("<link rel='stylesheet' href='");
+ sb.append(link);
+ sb.append("'/>\n");
+ }
+ if (sb.length() == 0)
+ return null;
+ else
+ return sb.toString();
+ }
+
+ void addStyleSheets(Bundle themeBundle, ResourceLoader ssRL) {
+ Enumeration<URL> themeResources = themeBundle.findEntries("/rap", "*.css", 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("/")) {
+ if (css.containsKey(resource))
+ log.warn("Overriding " + resource + " from " + themeBundle.getSymbolicName());
+ css.put(resource, ssRL);
+ }
+
+ }
+
+ }
+
+ void loadFontsUrl(URL url) {
+ try (BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream(), UTF_8))) {
+ String line = null;
+ while ((line = in.readLine()) != null) {
+ line = line.trim();
+ if (!line.equals("") && !line.startsWith("#")) {
+ fonts.add(line);
+ }
+ }
+ } catch (IOException e) {
+ throw new CmsException("Cannot load URL " + url, e);
+ }
+ }
+
+ void addResources(BundleResourceLoader themeBRL, String pattern) {
+ Bundle themeBundle = themeBRL.getBundle();
+ Enumeration<URL> 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("/")) {
+ if (resources.containsKey(resource))
+ log.warn("Overriding " + resource + " from " + themeBundle.getSymbolicName());
+ resources.put(resource, themeBRL);
+ }
+
+ }
+
+ }
+
+ public String getThemeId() {
+ return themeId;
+ }
+
+}
--- /dev/null
+//var CmsUiProvider = Java.type('org.argeo.cms.ui.CmsUiProvider');
+var CmsScriptApp = Java.type('org.argeo.cms.script.CmsScriptApp');
+var AppUi = Java.type('org.argeo.cms.script.AppUi');
+var Theme = Java.type('org.argeo.cms.script.Theme');
+var ScriptUi = Java.type('org.argeo.cms.script.ScriptUi');
+var CmsUtils = Java.type('org.argeo.cms.util.CmsUtils');
+
+// 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 FillLayout = Java.type('org.eclipse.swt.layout.FillLayout');
+var GridLayout = Java.type('org.eclipse.swt.layout.GridLayout');
+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 newLabel(parent, style, text) {
+ var control = new Label(parent, SWT.NONE)
+ control.setText(text)
+ CmsUtils.style(control, style)
+ CmsUtils.markup(control)
+ return control
+}
+
+function newFormLabel(parent, style, text) {
+ return newLabel(parent, style, '<b>' + text + '</b>')
+}
+
+function newText(parent, style, msg) {
+ var control = new Text(parent, SWT.NONE)
+ control.setMessage(msg)
+ CmsUtils.style(control, style)
+ return control
+}
+
+// print(__FILE__, __LINE__, __DIR__)
import org.osgi.framework.Bundle;
/** {@link ResourceLoader} implementation wrapping an {@link Bundle}. */
-class BundleResourceLoader implements ResourceLoader {
+public class BundleResourceLoader implements ResourceLoader {
private final Bundle bundle;
public BundleResourceLoader(Bundle bundle) {
@Override
public InputStream getResourceAsStream(String resourceName) throws IOException {
- URL res = bundle.getResource(resourceName);
+ URL res = bundle.getEntry(resourceName);
if (res == null)
throw new CmsException("Resource " + resourceName + " not found in bundle " + bundle.getSymbolicName());
return res.openStream();
}
+ public Bundle getBundle() {
+ return bundle;
+ }
+
}
--- /dev/null
+package org.argeo.cms.util;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Composite;
+
+/** The main pane of a CMS display, with QA and support areas. */
+public class CmsPane {
+
+ private Composite mainArea;
+ private Composite qaArea;
+ private Composite supportArea;
+
+ public CmsPane(Composite parent, int style) {
+ parent.setLayout(CmsUtils.noSpaceGridLayout());
+
+ qaArea = new Composite(parent, SWT.NONE);
+ qaArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ RowLayout qaLayout = new RowLayout();
+ qaLayout.spacing = 0;
+ qaArea.setLayout(qaLayout);
+
+ mainArea = new Composite(parent, SWT.NONE);
+ mainArea.setLayout(new GridLayout());
+ mainArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ supportArea = new Composite(parent, SWT.NONE);
+ supportArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ RowLayout supportLayout = new RowLayout();
+ supportLayout.spacing = 0;
+ supportArea.setLayout(supportLayout);
+ }
+
+ public Composite getMainArea() {
+ return mainArea;
+ }
+
+ public Composite getQaArea() {
+ return qaArea;
+ }
+
+ public Composite getSupportArea() {
+ return supportArea;
+ }
+
+}
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;
// favicon
if (properties.containsKey(WebClient.FAVICON)) {
String themeId = defaultBranding.get(WebClient.THEME_ID);
- Bundle themeBundle = findThemeBundle(themeId);
+ Bundle themeBundle = ThemeUtils.findThemeBundle(bundleContext, themeId);
String faviconRelPath = properties.get(WebClient.FAVICON);
application.addResource(faviconRelPath,
new BundleResourceLoader(themeBundle != null ? themeBundle : bundleContext.getBundle()));
// stylesheets and themes
Set<Bundle> themeBundles = new HashSet<>();
for (String themeId : styleSheets.keySet()) {
- Bundle themeBundle = findThemeBundle(themeId);
+ Bundle themeBundle = ThemeUtils.findThemeBundle(bundleContext, themeId);
StyleSheetResourceLoader styleSheetRL = new StyleSheetResourceLoader(
themeBundle != null ? themeBundle : bundleContext.getBundle());
if (themeBundle != null)
}
for (Bundle themeBundle : themeBundles) {
BundleResourceLoader themeBRL = new BundleResourceLoader(themeBundle);
- addThemeResources(application, themeBundle, themeBRL, "*.png");
- addThemeResources(application, themeBundle, themeBRL, "*.gif");
- addThemeResources(application, themeBundle, themeBRL, "*.jpg");
+ ThemeUtils.addThemeResources(application, themeBundle, themeBRL, "*.png");
+ ThemeUtils.addThemeResources(application, themeBundle, themeBRL, "*.gif");
+ ThemeUtils.addThemeResources(application, themeBundle, themeBRL, "*.jpg");
}
} catch (RuntimeException e) {
// Easier access to initialisation errors
}
}
- private Bundle findThemeBundle(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;
- }
-
- private void addThemeResources(Application application, Bundle themeBundle, BundleResourceLoader themeBRL,
- String pattern) {
- Enumeration<URL> 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);
- }
-
- }
-
- }
-
public void init() throws RepositoryException {
Session session = null;
try {
private final CmsUiProvider uiProvider;
private CmsUiProvider header;
- private Integer headerHeight = 40;
+ private Integer headerHeight = 0;
private CmsImageManager imageManager = new ImageManagerImpl();
private UxContext uxContext = null;
}
protected void refreshHeader() {
+ if (header == null)
+ return;
+
for (Control child : headerArea.getChildren())
child.dispose();
try {
import org.osgi.framework.Bundle;
/** {@link ResourceLoader} caching stylesheets. */
-class StyleSheetResourceLoader implements ResourceLoader {
+public class StyleSheetResourceLoader implements ResourceLoader {
private Bundle themeBundle;
private Map<String, StyleSheet> stylesheets = new LinkedHashMap<String, StyleSheet>();
// }
// }
- URL res = themeBundle.getResource(resourceName);
+ URL res = themeBundle.getEntry(resourceName);
if (res == null)
throw new CmsException(
- "Resource " + resourceName + " not found in bundle " + themeBundle.getSymbolicName());
+ "Entry " + resourceName + " not found in bundle " + themeBundle.getSymbolicName());
ByteArrayOutputStream out = new ByteArrayOutputStream();
IOUtils.copy(res.openStream(), out);
stylesheets.put(resourceName, new StyleSheet(out.toByteArray()));
--- /dev/null
+package org.argeo.cms.util;
+
+import java.net.URL;
+import java.util.Enumeration;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.eclipse.rap.rwt.application.Application;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+public class ThemeUtils {
+ final static Log log = LogFactory.getLog(ThemeUtils.class);
+
+ public 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;
+ }
+
+ public static void addThemeResources(Application application, Bundle themeBundle, BundleResourceLoader themeBRL,
+ String pattern) {
+ Enumeration<URL> 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);
+ }
+
+ }
+
+ }
+
+}