import org.argeo.cms.ui.util.SimpleImageManager;
import org.argeo.cms.ui.util.SimpleUxContext;
import org.argeo.cms.ui.widgets.auth.CmsLoginShell;
-import org.argeo.eclipse.ui.specific.UiContext;
import org.eclipse.e4.core.services.events.IEventBroker;
import org.eclipse.e4.ui.workbench.UIEvents;
import org.eclipse.e4.ui.workbench.lifecycle.PostContextCreate;
Subject subject = Subject.getSubject(AccessController.getContext());
Display display = Display.getCurrent();
- UiContext.setData(CmsView.KEY, this);
+// UiContext.setData(CmsView.KEY, this);
CmsLoginShell loginShell = new CmsLoginShell(this);
+ CmsView.registerCmsView(loginShell.getShell(), this);
loginShell.setSubject(subject);
try {
// try pre-auth
--- /dev/null
+package org.argeo.cms.ui;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.eclipse.rap.rwt.RWT;
+
+public abstract class AbstractCmsApp implements CmsApp {
+ private Map<String, CmsTheme> themes = Collections.synchronizedSortedMap(new TreeMap<>());
+
+ private List<CmsAppListener> cmsAppListeners = new ArrayList<>();
+
+ @Override
+ public Set<String> getUiNames() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ protected abstract String getThemeId(String uiName);
+
+ @Override
+ public CmsTheme getTheme(String uiName) {
+ String themeId = getThemeId(uiName);
+ if (themeId == null)
+ return null;
+ return themes.get(themeId);
+ }
+
+ protected boolean allThemesAvailable() {
+ boolean themeMissing = false;
+ uiNames: for (String uiName : getUiNames()) {
+ String themeId = getThemeId(uiName);
+ if (RWT.DEFAULT_THEME_ID.equals(themeId))
+ continue uiNames;
+ if (!themes.containsKey(themeId)) {
+ themeMissing = true;
+ break uiNames;
+ }
+ }
+ return !themeMissing;
+ }
+
+ public void addTheme(CmsTheme theme, Map<String, String> properties) {
+ themes.put(theme.getThemeId(), theme);
+ if (allThemesAvailable())
+ for (CmsAppListener listener : cmsAppListeners)
+ listener.themingUpdated();
+ }
+
+ public void removeTheme(CmsTheme theme, Map<String, String> properties) {
+ themes.remove(theme.getThemeId());
+ }
+
+ @Override
+ public void addCmsAppListener(CmsAppListener listener) {
+ cmsAppListeners.add(listener);
+ if (allThemesAvailable())
+ listener.themingUpdated();
+ }
+
+ @Override
+ public void removeCmsAppListener(CmsAppListener listener) {
+ cmsAppListeners.remove(listener);
+ }
+
+}
Set<String> getUiNames();
void initUi(String uiName, Composite parent);
+
+ CmsTheme getTheme(String uiName);
+
+ void addCmsAppListener(CmsAppListener listener);
+
+ void removeCmsAppListener(CmsAppListener listener);
}
--- /dev/null
+package org.argeo.cms.ui;
+
+/** Notifies important events in a {@link CmsApp} life cycle. */
+public interface CmsAppListener {
+ /** Theming has been updated and should be reloaded. */
+ void themingUpdated();
+}
--- /dev/null
+package org.argeo.cms.ui;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Set;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Shell;
+
+/** A CMS theme which can be applied to web apps as well as desktop apps. */
+public interface CmsTheme {
+ /** Unique ID of this theme. */
+ String getThemeId();
+
+ /**
+ * Load a resource as an input stream, base don its relative path, or
+ * <code>null</code> if not found
+ */
+ InputStream getResourceAsStream(String resourceName) throws IOException;
+
+ /** Relative paths to RAP specific CSS. */
+ Set<String> getRapCssPaths();
+
+ /** Relative paths to images such as icons. */
+ Set<String> getImagesPaths();
+
+ /** Tags to be added to the header section of the HTML page. */
+ String getHtmlHeaders();
+
+ /** The image registered at this path, or <code>null</code> if not found. */
+ Image getImage(String path);
+
+ /** The default icon size (typically the smallest). */
+ Integer getDefaultIconSize();
+
+ /**
+ * And icon with this file name (without the extension), with a best effort to
+ * find the appropriate size, or <code>null</code> if not found.
+ *
+ * @param name An icon file name without path and extension.
+ * @param preferredSize the preferred size, if <code>null</code>,
+ * {@link #getDefaultIconSize()} will be tried.
+ */
+ Image getIcon(String name, Integer preferredSize);
+
+ static CmsTheme getCmsTheme(Composite parent) {
+ // find parent shell
+ Shell topShell = parent.getShell();
+ while (topShell.getParent() != null)
+ topShell = (Shell) topShell.getParent();
+ return (CmsTheme) topShell.getData(CmsTheme.class.getName());
+ }
+
+ static void registerCmsTheme(Shell shell, CmsTheme theme) {
+ // find parent shell
+ Shell topShell = shell;
+ while (topShell.getParent() != null)
+ topShell = (Shell) topShell.getParent();
+ // check if already set
+ if (topShell.getData(CmsTheme.class.getName()) != null) {
+ CmsTheme registeredTheme = (CmsTheme) topShell.getData(CmsTheme.class.getName());
+ throw new IllegalArgumentException(
+ "Theme " + registeredTheme.getThemeId() + " already registered in this shell");
+ }
+ topShell.setData(CmsTheme.class.getName(), theme);
+ }
+
+}
import javax.security.auth.login.LoginContext;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Shell;
+
/** Provides interaction with the CMS system. */
public interface CmsView {
- String KEY = "org.argeo.cms.ui.view";
+ //String KEY = "org.argeo.cms.ui.view";
UxContext getUxContext();
CmsImageManager getImageManager();
boolean isAnonymous();
+
+ static CmsView getCmsView(Composite parent) {
+ // find parent shell
+ Shell topShell = parent.getShell();
+ while (topShell.getParent() != null)
+ topShell = (Shell) topShell.getParent();
+ return (CmsView) topShell.getData(CmsView.class.getName());
+ }
+
+ static void registerCmsView(Shell shell, CmsView view) {
+ // find parent shell
+ Shell topShell = shell;
+ while (topShell.getParent() != null)
+ topShell = (Shell) topShell.getParent();
+ // check if already set
+ if (topShell.getData(CmsView.class.getName()) != null) {
+ CmsView registeredView = (CmsView) topShell.getData(CmsView.class.getName());
+ throw new IllegalArgumentException(
+ "Cms view " + registeredView + " already registered in this shell");
+ }
+ shell.setData(CmsView.class.getName(), view);
+ }
+
}
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.BundleResourceLoader;
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;
private String repo = "(cn=node)";
// private Branding branding = new Branding();
- private Theme theme;
+ private CmsTheme theme;
private List<String> resources = new ArrayList<>();
}
if (theme != null) {
- theme.apply(application);
- String themeHeaders = theme.getAdditionalHeaders();
+ WebThemeUtils.apply(application, theme);
+ String themeHeaders = theme.getHtmlHeaders();
if (themeHeaders != null) {
if (additionalHeaders == null)
additionalHeaders = themeHeaders;
// TODO
}
- public Theme getTheme() {
+ public CmsTheme getTheme() {
return theme;
}
- public void setTheme(Theme theme) {
+ public void setTheme(CmsTheme theme) {
this.theme = theme;
}
+++ /dev/null
-package org.argeo.cms.ui.script;
-
-import org.argeo.cms.ui.util.CmsTheme;
-import org.osgi.framework.BundleContext;
-
-/** @deprecated Use <code>CmsTheme</code> instead. */
-@Deprecated
-public class Theme extends CmsTheme {
-
- public Theme(BundleContext bundleContext, String symbolicName) {
- super(bundleContext, symbolicName);
- }
-
- public Theme(BundleContext bundleContext) {
- super(bundleContext);
- }
-
-}
--- /dev/null
+package org.argeo.cms.ui.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.argeo.cms.ui.CmsTheme;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.widgets.Display;
+
+/** Centralises some generic {@link CmsTheme} patterns. */
+public abstract class AbstractCmsTheme implements CmsTheme {
+ private Map<String, Image> imageCache = new HashMap<>();
+
+ private Map<String, Map<Integer, String>> iconPaths = new HashMap<>();
+
+ private Integer defaultIconSize = 16;
+
+ public Image getImage(String path) {
+ if (!imageCache.containsKey(path)) {
+ try (InputStream in = getResourceAsStream(path)) {
+ if (in == null)
+ return null;
+ ImageData imageData = new ImageData(in);
+ Image image = new Image(Display.getDefault(), imageData);
+ imageCache.put(path, image);
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ return imageCache.get(path);
+ }
+
+ @Override
+ public Image getIcon(String name, Integer preferredSize) {
+ if (preferredSize == null)
+ preferredSize = defaultIconSize;
+ Map<Integer, String> subCache;
+ if (!iconPaths.containsKey(name))
+ subCache = new HashMap<>();
+ else
+ subCache = iconPaths.get(name);
+ Image image = null;
+ if (!subCache.containsKey(preferredSize)) {
+ Image bestMatchSoFar = null;
+ paths: for (String p : getImagesPaths()) {
+ int lastSlash = p.lastIndexOf('/');
+ String fileName = p;
+ if (lastSlash >= 0)
+ fileName = p.substring(lastSlash + 1);
+ int lastDot = fileName.lastIndexOf('.');
+ if (lastDot >= 0)
+ fileName = fileName.substring(0, lastDot);
+ if (fileName.equals(name)) {// matched
+ Image img = getImage(p);
+ int width = img.getBounds().width;
+ if (width == preferredSize) {// perfect match
+ subCache.put(preferredSize, p);
+ image = img;
+ break paths;
+ }
+ if (bestMatchSoFar == null) {
+ bestMatchSoFar = img;
+ } else {
+ if (Math.abs(width - preferredSize) < Math
+ .abs(bestMatchSoFar.getBounds().width - preferredSize))
+ bestMatchSoFar = img;
+ }
+ }
+ }
+
+ if (image == null)
+ image = bestMatchSoFar;
+ } else {
+ image = getImage(subCache.get(preferredSize));
+ }
+
+ if (image != null && !iconPaths.containsKey(name))
+ iconPaths.put(name, subCache);
+
+ return image;
+ }
+
+ @Override
+ public Integer getDefaultIconSize() {
+ return defaultIconSize;
+ }
+
+}
--- /dev/null
+package org.argeo.cms.ui.util;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.stream.Collectors;
+
+import org.argeo.cms.ui.CmsTheme;
+import org.eclipse.swt.graphics.Image;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Simplifies the theming of an app (only RAP is supported at this stage).<br>
+ *
+ * Additional fonts listed in <code>/fonts.txt</code>.<br>
+ * Additional (standard CSS) header in <code>/header.css</code>.<br>
+ * RAP specific CSS files in <code>/rap/*.css</code>.<br>
+ * All images added as additional resources based on extensions
+ * <code>/ ** /*.{png,gif,jpeg,...}</code>.<br>
+ */
+public class BundleCmsTheme extends AbstractCmsTheme {
+ public final static String DEFAULT_CMS_THEME_BUNDLE = "org.argeo.theme.argeo2";
+
+ public final static String CMS_THEME_PROPERTY = "argeo.cms.theme";
+ public final static String CMS_THEME_BUNDLE_PROPERTY = "argeo.cms.theme.bundle";
+
+// private final static Log log = LogFactory.getLog(BundleCmsTheme.class);
+
+ private String themeId;
+ private Set<String> rapCssPaths = new TreeSet<>();
+ private Set<String> imagesPaths = new TreeSet<>();
+
+ private String headerCss;
+ private List<String> fonts = new ArrayList<>();
+
+ private String basePath;
+ private String rapCssPath;
+ private Bundle themeBundle;
+
+ public BundleCmsTheme() {
+
+ }
+
+ public void init(BundleContext bundleContext, Map<String, String> properties) {
+ initResources(bundleContext, null);
+ }
+
+ public void destroy(BundleContext bundleContext, Map<String, String> properties) {
+
+ }
+
+ @Deprecated
+ public BundleCmsTheme(BundleContext bundleContext) {
+ this(bundleContext, null);
+ }
+
+ @Deprecated
+ public BundleCmsTheme(BundleContext bundleContext, String symbolicName) {
+ initResources(bundleContext, symbolicName);
+ }
+
+ private void initResources(BundleContext bundleContext, String symbolicName) {
+ if (symbolicName == null) {
+ themeBundle = bundleContext.getBundle();
+// basePath = "/theme/";
+// cssPath = basePath;
+ } else {
+ themeBundle = findThemeBundle(bundleContext, symbolicName);
+ }
+ basePath = "/";
+ rapCssPath = "/rap/";
+// this.themeId = RWT.DEFAULT_THEME_ID;
+ this.themeId = themeBundle.getSymbolicName();
+ addRapStyleSheets(themeBundle);
+ addResources("*.png");
+ addResources("*.gif");
+ addResources("*.jpg");
+ addResources("*.jpeg");
+ addResources("*.svg");
+ addResources("*.ico");
+
+ // fonts
+ URL fontsUrl = themeBundle.getEntry(basePath + "fonts.txt");
+ if (fontsUrl != null) {
+ loadFontsUrl(fontsUrl);
+ }
+
+ // common CSS header (plain CSS)
+ URL headerCssUrl = themeBundle.getEntry(basePath + "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 IllegalArgumentException("Cannot read " + headerCssUrl, e);
+ }
+ }
+ }
+
+ public String getHtmlHeaders() {
+ 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 addRapStyleSheets(Bundle themeBundle) {
+ Enumeration<URL> themeResources = themeBundle.findEntries(rapCssPath, "*.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 (rapCss.containsKey(resource))
+// log.warn("Overriding " + resource + " from " + themeBundle.getSymbolicName());
+// rapCss.put(resource, ssRL);
+ rapCssPaths.add(resource);
+ }
+
+ }
+
+ }
+
+ 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 IllegalArgumentException("Cannot load URL " + url, e);
+ }
+ }
+
+ void addResources(String pattern) {
+ Enumeration<URL> themeResources = themeBundle.findEntries(basePath, 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);
+ imagesPaths.add(resource);
+ }
+
+ }
+
+ }
+
+ @Override
+ public InputStream getResourceAsStream(String resourceName) throws IOException {
+ URL res = themeBundle.getEntry(resourceName);
+ if (res == null) {
+ res = themeBundle.getResource(resourceName);
+ if (res == null)
+ return null;
+// throw new IllegalArgumentException(
+// "Resource " + resourceName + " not found in bundle " + themeBundle.getSymbolicName());
+ }
+ return res.openStream();
+ }
+
+ public String getThemeId() {
+ return themeId;
+ }
+
+
+// public void setThemeId(String themeId) {
+// this.themeId = themeId;
+// }
+//
+// public String getBasePath() {
+// return basePath;
+// }
+//
+// public void setBasePath(String basePath) {
+// this.basePath = basePath;
+// }
+//
+// public String getRapCssPath() {
+// return rapCssPath;
+// }
+//
+// public void setRapCssPath(String cssPath) {
+// this.rapCssPath = cssPath;
+// }
+
+ @Override
+ public Set<String> getRapCssPaths() {
+ return rapCssPaths;
+ }
+
+ @Override
+ public Set<String> getImagesPaths() {
+ return imagesPaths;
+ }
+
+ 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;
+ }
+
+}
+++ /dev/null
-package org.argeo.cms.ui.util;
-
-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;
- }
-
-}
--- /dev/null
+package org.argeo.cms.ui.util;
+
+import org.argeo.cms.ui.CmsTheme;
+import org.eclipse.swt.graphics.Image;
+
+/** Can be applied to {@link Enum}s in order to generated {@link Image}s. */
+public interface CmsIcon {
+ String name();
+
+ default Image getSmallIcon(CmsTheme theme) {
+ return theme.getIcon(name(), getSmallIconSize());
+ }
+
+ default Image getBigIcon(CmsTheme theme) {
+ return theme.getIcon(name(), getBigIconSize());
+ }
+
+ default Integer getSmallIconSize() {
+ return 16;
+ }
+
+ default Integer getBigIconSize() {
+ return 32;
+ }
+}
/** Can be applied to {@link Enum}s in order to generated (CSS) class names. */
public interface CmsStyle {
+ String name();
+
default String toStyleClass() {
- return getClassPrefix() + "-" + ((Enum<?>) this).name();
+ return getClassPrefix() + "-" + name();
}
default String getClassPrefix() {
return "cms";
}
+
}
+++ /dev/null
-package org.argeo.cms.ui.util;
-
-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.eclipse.rap.rwt.application.Application;
-import org.eclipse.rap.rwt.service.ResourceLoader;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-
-/**
- * Simplifies the theming of an app (only RAP is supported at this stage).<br>
- *
- * Additional fonts listed in <code>/fonts.txt</code>.<br>
- * Additional (standard CSS) header in <code>/header.css</code>.<br>
- * RAP specific CSS files in <code>/rap/*.css</code>.<br>
- * All images added as additional resources based on extensions
- * <code>/ ** /*.{png,gif,jpeg,...}</code>.<br>
- */
-public class CmsTheme {
- public final static String DEFAULT_CMS_THEME_BUNDLE = "org.argeo.theme.argeo2";
-
- public final static String CMS_THEME_BUNDLE_PROPERTY = "argeo.cms.theme.bundle";
-
- private final static Log log = LogFactory.getLog(CmsTheme.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<>();
-
- private String basePath;
- private String cssPath;
- private final Bundle themeBundle;
-
- public CmsTheme(BundleContext bundleContext) {
- this(bundleContext, null);
- }
-
- public CmsTheme(BundleContext bundleContext, String symbolicName) {
- if (symbolicName == null) {
- themeBundle = bundleContext.getBundle();
-// basePath = "/theme/";
-// cssPath = basePath;
- } else {
- themeBundle = ThemeUtils.findThemeBundle(bundleContext, symbolicName);
- }
- basePath = "/";
- cssPath = "/rap/";
-// this.themeId = RWT.DEFAULT_THEME_ID;
- this.themeId = themeBundle.getSymbolicName();
- 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(basePath + "fonts.txt");
- if (fontsUrl != null) {
- loadFontsUrl(fontsUrl);
- }
-
- // common CSS header (plain CSS)
- URL headerCssUrl = themeBundle.getEntry(basePath + "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) {
- resources: for (String name : resources.keySet()) {
- if (name.startsWith("target/"))
- continue resources; // skip maven output
- application.addResource(name, resources.get(name));
- if (log.isTraceEnabled())
- log.trace("Theme " + themeBundle + ": added resource " + name);
- }
- for (String name : css.keySet()) {
- application.addStyleSheet(themeId, name, css.get(name));
- if (log.isDebugEnabled())
- log.debug("Theme " + themeBundle + ": added RAP CSS " + 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(cssPath, "*.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(basePath, 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;
- }
-
- public void setThemeId(String themeId) {
- this.themeId = themeId;
- }
-
- public String getBasePath() {
- return basePath;
- }
-
- public void setBasePath(String basePath) {
- this.basePath = basePath;
- }
-
- public String getCssPath() {
- return cssPath;
- }
-
- public void setCssPath(String cssPath) {
- this.cssPath = cssPath;
- }
-
-}
import org.argeo.cms.CmsException;
import org.argeo.cms.ui.CmsConstants;
import org.argeo.cms.ui.CmsView;
-import org.argeo.eclipse.ui.specific.UiContext;
import org.argeo.jcr.JcrUtils;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.rap.rwt.service.ResourceManager;
/**
* The CMS view related to this display, or null if none is available from this
* call.
+ *
+ * @deprecated Use {@link CmsView#getCmsView(Composite)} instead.
*/
+ @Deprecated
public static CmsView getCmsView() {
- return UiContext.getData(CmsView.KEY);
+// return UiContext.getData(CmsView.class.getName());
+ return CmsView.getCmsView(Display.getCurrent().getActiveShell());
}
public static StringBuilder getServerBaseUrl(HttpServletRequest request) {
@Override
public int createUI() {
final Display display = createDisplay();
- UiContext.setData(CmsView.KEY, this);
+// UiContext.setData(CmsView.KEY, this);
+
CmsLoginShell loginShell = createCmsLoginShell();
+ CmsView.registerCmsView(loginShell.getShell(), this);
try {
// try pre-auth
loginContext = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER, loginShell);
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 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;
Map<String, String> defaultBranding = null;
if (branding.containsKey("*"))
defaultBranding = branding.get("*");
- String defaultTheme = defaultBranding.get(WebClient.THEME_ID);
+ // String defaultTheme = defaultBranding.get(WebClient.THEME_ID);
// entry points
for (String page : pages.keySet()) {
// favicon
if (properties.containsKey(WebClient.FAVICON)) {
String themeId = defaultBranding.get(WebClient.THEME_ID);
- Bundle themeBundle = ThemeUtils.findThemeBundle(bundleContext, themeId);
+ Bundle themeBundle = 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 = ThemeUtils.findThemeBundle(bundleContext, themeId);
+ Bundle themeBundle = findThemeBundle(bundleContext, themeId);
StyleSheetResourceLoader styleSheetRL = new StyleSheetResourceLoader(
themeBundle != null ? themeBundle : bundleContext.getBundle());
if (themeBundle != null)
}
for (Bundle themeBundle : themeBundles) {
BundleResourceLoader themeBRL = new BundleResourceLoader(themeBundle);
- ThemeUtils.addThemeResources(application, themeBundle, themeBRL, "*.png");
- ThemeUtils.addThemeResources(application, themeBundle, themeBRL, "*.gif");
- ThemeUtils.addThemeResources(application, themeBundle, themeBRL, "*.jpg");
+ 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
this.contextName = contextName;
}
+ private 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);
+ }
+
+ }
+
+ }
+
+ 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
+++ /dev/null
-package org.argeo.cms.ui.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);
- }
-
- }
-
- }
-
-}
@Override
protected final void createContents(final Composite parent) {
- UiContext.setData(CmsView.KEY, this);
+ // UiContext.setData(CmsView.KEY, this);
+ CmsView.registerCmsView(parent.getShell(), this);
Subject.doAs(getSubject(), new PrivilegedAction<Void>() {
@Override
public Void run() {
--- /dev/null
+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;
+ }
+
+}
--- /dev/null
+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);
+ }
+
+}
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.AbstractEntryPoint;
import org.eclipse.rap.rwt.application.Application;
+import org.eclipse.rap.rwt.application.ApplicationConfiguration;
+import org.eclipse.rap.rwt.client.WebClient;
import org.eclipse.swt.widgets.Composite;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
-public class CmsWebApp extends MinimalWebApp {
+/** 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 ServiceRegistration<ApplicationConfiguration> rwtAppReg;
+
+ private final static String CONTEXT_NAME = "contextName";
+ private String contextName;
+
+ public void init(BundleContext bundleContext, Map<String, String> properties) {
+ this.bundleContext = bundleContext;
+ contextName = properties.get(CONTEXT_NAME);
+// registerIfAllThemesAvailable();
+ }
+
+ public void destroy(BundleContext bundleContext, Map<String, String> properties) {
+
+ }
+
@Override
- protected void addEntryPoints(Application application, Map<String, String> properties) {
+ 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<String, String> properties = new HashMap<>();
+ addEntryPoints(application, properties);
+
+ }
+
+ protected void addEntryPoints(Application application, Map<String, String> commonProperties) {
+ for (String uiName : cmsApp.getUiNames()) {
+ Map<String, String> 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, () -> {
return new AbstractEntryPoint() {
private static final long serialVersionUID = -9153259126766694485L;
}
}
+// 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<String, Object> 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 : ""));
+// }
+// }
+// }
+
public CmsApp getCmsApp() {
return cmsApp;
}
- public void setCmsApp(CmsApp cmsApp) {
+ public void setCmsApp(CmsApp cmsApp, Map<String, String> properties) {
this.cmsApp = cmsApp;
+ this.cmsApp.addCmsAppListener(this);
+// registerIfAllThemesAvailable();
+ }
+
+ public void unsetCmsApp(CmsApp cmsApp, Map<String, String> properties) {
+ if (rwtAppReg != null)
+ rwtAppReg.unregister();
+ this.cmsApp = null;
+ }
+
+ @Override
+ public void themingUpdated() {
+ Dictionary<String, Object> regProps = LangUtils.dict(CONTEXT_NAME, contextName);
+ if (rwtAppReg != null)
+ rwtAppReg.unregister();
+ if (bundleContext != null) {
+ rwtAppReg = bundleContext.registerService(ApplicationConfiguration.class, this, regProps);
+ log.info("Published CMS web app /" + (contextName != null ? contextName : ""));
+ }
}
}
package org.argeo.cms.web;
-import static org.argeo.cms.ui.util.CmsTheme.CMS_THEME_BUNDLE_PROPERTY;
+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.CmsTheme;
+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;
/** Lightweight web app using only RWT and not the whole Eclipse platform. */
public class MinimalWebApp implements ApplicationConfiguration {
- private CmsTheme theme;
+ private BundleCmsTheme theme;
public void init(BundleContext bundleContext, Map<String, Object> properties) {
if (properties.containsKey(CMS_THEME_BUNDLE_PROPERTY)) {
String cmsThemeBundle = properties.get(CMS_THEME_BUNDLE_PROPERTY).toString();
- theme = new CmsTheme(bundleContext, cmsThemeBundle);
+ theme = new BundleCmsTheme(bundleContext, cmsThemeBundle);
}
}
@Override
public void configure(Application application) {
if (theme != null)
- theme.apply(application);
+ WebThemeUtils.apply(application, theme);
Map<String, String> properties = new HashMap<>();
if (theme != null) {
properties.put(WebClient.THEME_ID, theme.getThemeId());
- properties.put(WebClient.HEAD_HTML, theme.getAdditionalHeaders());
+ 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;
+ }
+
}
--- /dev/null
+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);
+ }
+ }
+
+}
*/
/**
- * Creates a new {@link Dictionary} with one key-value pair (neither key not
- * value should be null)
+ * Creates a new {@link Dictionary} with one key-value pair. Key should not be
+ * null, but if the value is null, it returns an empty {@link Dictionary}.
*/
public static Dictionary<String, Object> dict(String key, Object value) {
assert key != null;
- assert value != null;
Hashtable<String, Object> props = new Hashtable<>();
- props.put(key, value);
+ if (value != null)
+ props.put(key, value);
return props;
}
- /**@deprecated Use {@link #dict(String, Object)} instead.*/
+ /** @deprecated Use {@link #dict(String, Object)} instead. */
@Deprecated
public static Dictionary<String, Object> dico(String key, Object value) {
return dict(key, value);
}
-
+
/** Converts a {@link Dictionary} to a {@link Map} of strings. */
public static Map<String, String> dictToStringMap(Dictionary<String, ?> properties) {
if (properties == null) {