Refactor suite UX
authorMathieu Baudier <mbaudier@argeo.org>
Tue, 13 Jun 2023 06:33:38 +0000 (08:33 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Tue, 13 Jun 2023 06:33:38 +0000 (08:33 +0200)
20 files changed:
org.argeo.app.core/bnd.bnd
swt/org.argeo.app.swt/src/org/argeo/app/swt/ux/DefaultFooter.java [new file with mode: 0644]
swt/org.argeo.app.swt/src/org/argeo/app/swt/ux/DefaultHeader.java [new file with mode: 0644]
swt/org.argeo.app.swt/src/org/argeo/app/swt/ux/DefaultLeadPane.java [new file with mode: 0644]
swt/org.argeo.app.swt/src/org/argeo/app/swt/ux/DefaultLoginScreen.java [new file with mode: 0644]
swt/org.argeo.app.swt/src/org/argeo/app/swt/ux/SwtArgeoApp.java [new file with mode: 0644]
swt/org.argeo.app.ui/OSGI-INF/adminLeadPane.xml
swt/org.argeo.app.ui/OSGI-INF/cmsApp.xml
swt/org.argeo.app.ui/OSGI-INF/footer.xml
swt/org.argeo.app.ui/OSGI-INF/header.xml
swt/org.argeo.app.ui/OSGI-INF/leadPane.xml
swt/org.argeo.app.ui/OSGI-INF/loginScreen.xml
swt/org.argeo.app.ui/bnd.bnd
swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultFooter.java [deleted file]
swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultHeader.java [deleted file]
swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultLeadPane.java [deleted file]
swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultLoginScreen.java [deleted file]
swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteApp.java [deleted file]
swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteUiUtils.java
swt/org.argeo.app.ui/src/org/argeo/app/ui/publish/PublishingApp.java

index e6be5731b07588f415912dcbc9273daba9a125de..dce57724e96ae28467d4c4f14b95b33f120fa0f8 100644 (file)
@@ -12,6 +12,7 @@ javax.jcr.nodetype,\
 javax.jcr.security,\
 com.fasterxml.jackson.core,\
 org.apache.jackrabbit.*;version="[1,4)",\
+org.argeo.cms.acr,\
 *
 
 Require-Capability:\
diff --git a/swt/org.argeo.app.swt/src/org/argeo/app/swt/ux/DefaultFooter.java b/swt/org.argeo.app.swt/src/org/argeo/app/swt/ux/DefaultFooter.java
new file mode 100644 (file)
index 0000000..303952d
--- /dev/null
@@ -0,0 +1,38 @@
+package org.argeo.app.swt.ux;
+
+import java.util.Map;
+
+import org.argeo.api.acr.Content;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.acr.SwtUiProvider;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.osgi.framework.BundleContext;
+
+/** Footer of a standard Argeo Suite application. */
+public class DefaultFooter implements SwtUiProvider {
+       @Override
+       public Control createUiPart(Composite parent, Content context) {
+               parent.setLayout(CmsSwtUtils.noSpaceGridLayout());
+               Composite content = new Composite(parent, SWT.NONE);
+               content.setLayoutData(new GridData(0, 0));
+               Control contentControl = createContent(content, context);
+
+               // TODO support and guarantee
+
+               return contentControl;
+       }
+
+       protected Control createContent(Composite parent, Content context) {
+               return parent;
+       }
+
+       public void init(BundleContext bundleContext, Map<String, String> properties) {
+       }
+
+       public void destroy(BundleContext bundleContext, Map<String, String> properties) {
+
+       }
+}
diff --git a/swt/org.argeo.app.swt/src/org/argeo/app/swt/ux/DefaultHeader.java b/swt/org.argeo.app.swt/src/org/argeo/app/swt/ux/DefaultHeader.java
new file mode 100644 (file)
index 0000000..36b37bf
--- /dev/null
@@ -0,0 +1,128 @@
+package org.argeo.app.swt.ux;
+
+import java.util.Map;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.app.ux.SuiteIcon;
+import org.argeo.app.ux.SuiteStyle;
+import org.argeo.cms.CurrentUser;
+import org.argeo.cms.Localized;
+import org.argeo.cms.swt.CmsSwtTheme;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.acr.SwtUiProvider;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.wiring.BundleWiring;
+
+/** Header of a standard Argeo Suite application. */
+public class DefaultHeader implements SwtUiProvider {
+       public final static String TITLE_PROPERTY = "argeo.suite.ui.header.title";
+       private Localized title = null;
+
+       @Override
+       public Control createUiPart(Composite parent, Content context) {
+               CmsView cmsView = CmsSwtUtils.getCmsView(parent);
+               CmsSwtTheme theme = CmsSwtUtils.getCmsTheme(parent);
+
+               parent.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(3, true)));
+
+               // TODO right to left
+               Composite lead = new Composite(parent, SWT.NONE);
+               CmsSwtUtils.style(lead, SuiteStyle.header);
+               lead.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, true, false));
+               lead.setLayout(new GridLayout());
+               Label lbl = new Label(lead, SWT.NONE);
+//             String title = properties.get(TITLE_PROPERTY);
+//             // TODO expose the localized
+//             lbl.setText(LocaleUtils.isLocaleKey(title) ? LocaleUtils.local(title, getClass().getClassLoader()).toString()
+//                             : title);
+               lbl.setText(title.lead());
+               CmsSwtUtils.style(lbl, SuiteStyle.headerTitle);
+               lbl.setLayoutData(CmsSwtUtils.fillWidth());
+
+               Composite middle = new Composite(parent, SWT.NONE);
+               CmsSwtUtils.style(middle, SuiteStyle.header);
+               middle.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false));
+               middle.setLayout(new GridLayout());
+
+               Composite end = new Composite(parent, SWT.NONE);
+               CmsSwtUtils.style(end, SuiteStyle.header);
+               end.setLayoutData(new GridData(SWT.END, SWT.CENTER, true, false));
+
+               if (!cmsView.isAnonymous()) {
+                       end.setLayout(new GridLayout(2, false));
+                       Label userL = new Label(end, SWT.NONE);
+                       CmsSwtUtils.style(userL, SuiteStyle.header);
+                       userL.setText(CurrentUser.getDisplayName());
+//                     Button logoutB = new Button(end, SWT.FLAT);
+//                     logoutB.setImage(theme.getSmallIcon(SuiteIcon.logout));
+//                     logoutB.addSelectionListener(new SelectionAdapter() {
+//                             private static final long serialVersionUID = 7116760083964201233L;
+//
+//                             @Override
+//                             public void widgetSelected(SelectionEvent e) {
+//                                     cmsView.logout();
+//                             }
+//
+//                     });
+                       Label logOutL = new Label(end, 0);
+                       logOutL.setImage(theme.getSmallIcon(SuiteIcon.openUserMenu));
+                       logOutL.addMouseListener(new MouseAdapter() {
+                               private static final long serialVersionUID = 6908266850511460799L;
+
+                               @Override
+                               public void mouseDown(MouseEvent e) {
+                                       cmsView.logout();
+                               }
+
+                       });
+               } else {
+                       end.setLayout(new GridLayout(1, false));
+                       // required in order to avoid wrong height after logout
+                       new Label(end, SWT.NONE).setText("");
+
+               }
+               return lbl;
+       }
+
+       public void init(BundleContext bundleContext, Map<String, String> properties) {
+               String titleStr = (String) properties.get(TITLE_PROPERTY);
+               if (titleStr != null) {
+                       if (titleStr.startsWith("%")) {
+                               title = new Localized() {
+
+                                       @Override
+                                       public String name() {
+                                               return titleStr;
+                                       }
+
+                                       @Override
+                                       public ClassLoader getL10nClassLoader() {
+                                               return bundleContext != null
+                                                               ? bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader()
+                                                               : getClass().getClassLoader();
+                                       }
+                               };
+                       } else {
+                               title = new Localized.Untranslated(titleStr);
+                       }
+               }
+       }
+
+       public void destroy(BundleContext bundleContext, Map<String, String> properties) {
+
+       }
+
+       public Localized getTitle() {
+               return title;
+       }
+
+}
diff --git a/swt/org.argeo.app.swt/src/org/argeo/app/swt/ux/DefaultLeadPane.java b/swt/org.argeo.app.swt/src/org/argeo/app/swt/ux/DefaultLeadPane.java
new file mode 100644 (file)
index 0000000..2b17dee
--- /dev/null
@@ -0,0 +1,223 @@
+package org.argeo.app.swt.ux;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.api.cms.ux.CmsIcon;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.app.api.RankedObject;
+import org.argeo.app.core.SuiteUtils;
+import org.argeo.app.ux.SuiteIcon;
+import org.argeo.app.ux.SuiteStyle;
+import org.argeo.app.ux.SuiteUxEvent;
+import org.argeo.cms.CurrentUser;
+import org.argeo.cms.LocaleUtils;
+import org.argeo.cms.Localized;
+import org.argeo.cms.swt.CmsSwtTheme;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.acr.SwtUiProvider;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.wiring.BundleWiring;
+
+/** Side pane listing various perspectives. */
+public class DefaultLeadPane implements SwtUiProvider {
+       private final static CmsLog log = CmsLog.getLog(DefaultLeadPane.class);
+
+       public static enum Property {
+               defaultLayers, adminLayers;
+       }
+
+       private Map<String, RankedObject<SwtAppLayer>> layers = Collections.synchronizedSortedMap(new TreeMap<>());
+       private List<String> defaultLayers;
+       private List<String> adminLayers = new ArrayList<>();
+
+       private ClassLoader l10nClassLoader;
+
+       @Override
+       public Control createUiPart(Composite parent, Content node) {
+               CmsView cmsView = CmsSwtUtils.getCmsView(parent);
+               parent.setLayout(CmsSwtUtils.noSpaceGridLayout());
+               Composite appLayersC = new Composite(parent, SWT.NONE);
+               CmsSwtUtils.style(appLayersC, SuiteStyle.leadPane);
+               GridLayout layout = new GridLayout();
+               layout.verticalSpacing = 10;
+               layout.marginTop = 10;
+               layout.marginLeft = 10;
+               layout.marginRight = 10;
+               appLayersC.setLayout(layout);
+               appLayersC.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false));
+
+               Composite adminLayersC;
+               if (!adminLayers.isEmpty()) {
+                       adminLayersC = new Composite(parent, SWT.NONE);
+                       CmsSwtUtils.style(adminLayersC, SuiteStyle.leadPane);
+                       GridLayout adminLayout = new GridLayout();
+                       adminLayout.verticalSpacing = 10;
+                       adminLayout.marginBottom = 10;
+                       adminLayout.marginLeft = 10;
+                       adminLayout.marginRight = 10;
+                       adminLayersC.setLayout(adminLayout);
+                       adminLayersC.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, false, true));
+               } else {
+                       adminLayersC = null;
+               }
+
+//             boolean isAdmin = cmsView.doAs(() -> CurrentUser.isInRole(NodeConstants.ROLE_USER_ADMIN));
+               // Set<String> userRoles = cmsView.doAs(() -> CurrentUser.roles());
+               Button first = null;
+               layers: for (String layerDef : defaultLayers) {
+                       layerDef = layerDef.trim();
+                       if ("".equals(layerDef))
+                               continue layers;// skip empty lines
+                       String[] semiColArr = layerDef.split(";");
+                       String layerId = semiColArr[0];
+                       Set<String> layerRoles = SuiteUtils.extractRoles(semiColArr);
+                       if (layers.containsKey(layerId)) {
+                               if (!layerRoles.isEmpty()) {
+                                       boolean authorized = false;
+                                       authorized = cmsView.doAs(() -> {
+                                               for (String layerRole : layerRoles) {
+                                                       if (CurrentUser.implies(layerRole, null)) {
+                                                               return true;
+                                                       }
+                                               }
+                                               return false;
+                                       });
+                                       if (!authorized)
+                                               continue layers;// skip unauthorized layer
+//                                     Set<String> intersection = new HashSet<String>(layerRoles);
+//                                     intersection.retainAll(userRoles);
+//                                     if (intersection.isEmpty())
+//                                             continue layers;// skip unauthorized layer
+                               }
+                               RankedObject<SwtAppLayer> layerObj = layers.get(layerId);
+
+                               Localized title = null;
+                               if (!adminLayers.contains(layerId)) {
+                                       String titleStr = (String) layerObj.getProperties().get(SwtAppLayer.Property.title.name());
+                                       if (titleStr != null) {
+                                               if (titleStr.startsWith("%")) {
+                                                       // LocaleUtils.local(titleStr, getClass().getClassLoader());
+                                                       title = () -> titleStr;
+                                               } else {
+                                                       title = new Localized.Untranslated(titleStr);
+                                               }
+                                       }
+                               }
+
+                               String iconName = (String) layerObj.getProperties().get(SwtAppLayer.Property.icon.name());
+                               SuiteIcon icon = null;
+                               if (iconName != null)
+                                       icon = SuiteIcon.valueOf(iconName);
+
+                               Composite buttonParent;
+                               if (adminLayers.contains(layerId))
+                                       buttonParent = adminLayersC;
+                               else
+                                       buttonParent = appLayersC;
+                               Button b = createLayerButton(buttonParent, layerId, title, icon, l10nClassLoader);
+                               if (first == null)
+                                       first = b;
+                       }
+               }
+               return first;
+       }
+
+       protected Button createLayerButton(Composite parent, String layer, Localized msg, CmsIcon icon,
+                       ClassLoader l10nClassLoader) {
+               CmsSwtTheme theme = CmsSwtUtils.getCmsTheme(parent);
+               Button button = new Button(parent, SWT.PUSH);
+               CmsSwtUtils.style(button, SuiteStyle.leadPane);
+               if (icon != null)
+                       button.setImage(theme.getBigIcon(icon));
+               button.setLayoutData(new GridData(SWT.CENTER, SWT.BOTTOM, true, false));
+               // button.setToolTipText(msg.lead());
+               if (msg != null) {
+                       Label lbl = new Label(parent, SWT.CENTER);
+                       CmsSwtUtils.style(lbl, SuiteStyle.leadPane);
+                       String txt = LocaleUtils.lead(msg, l10nClassLoader);
+//                     String txt = msg.lead();
+                       lbl.setText(txt);
+                       lbl.setLayoutData(new GridData(SWT.CENTER, SWT.TOP, true, false));
+               }
+               CmsSwtUtils.sendEventOnSelect(button, SuiteUxEvent.switchLayer.topic(), SuiteUxEvent.LAYER, layer);
+               return button;
+       }
+
+       public void init(BundleContext bundleContext, Map<String, Object> properties) {
+               l10nClassLoader = bundleContext != null ? bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader()
+                               : getClass().getClassLoader();
+
+               String[] defaultLayers = (String[]) properties.get(Property.defaultLayers.toString());
+               if (defaultLayers == null)
+                       throw new IllegalArgumentException("Default layers must be set.");
+               this.defaultLayers = Arrays.asList(defaultLayers);
+               if (log.isDebugEnabled())
+                       log.debug("Default layers: " + Arrays.asList(defaultLayers));
+               String[] adminLayers = (String[]) properties.get(Property.adminLayers.toString());
+               if (adminLayers != null) {
+                       this.adminLayers = Arrays.asList(adminLayers);
+                       if (log.isDebugEnabled())
+                               log.debug("Admin layers: " + Arrays.asList(adminLayers));
+               }
+       }
+
+       public void destroy(BundleContext bundleContext, Map<String, String> properties) {
+
+       }
+
+       public void addLayer(SwtAppLayer layer, Map<String, Object> properties) {
+               if (properties.containsKey(Constants.SERVICE_PID)) {
+                       String pid = (String) properties.get(Constants.SERVICE_PID);
+                       RankedObject.putIfHigherRank(layers, pid, layer, properties);
+               }
+       }
+
+       public void removeLayer(SwtAppLayer layer, Map<String, Object> properties) {
+               if (properties.containsKey(Constants.SERVICE_PID)) {
+                       String pid = (String) properties.get(Constants.SERVICE_PID);
+                       if (layers.containsKey(pid)) {
+                               if (layers.get(pid).equals(new RankedObject<SwtAppLayer>(layer, properties))) {
+                                       layers.remove(pid);
+                               }
+                       }
+               }
+       }
+
+//     protected Button createLayerButton(Composite parent, String layer, Localized msg, CmsIcon icon) {
+//             CmsTheme theme = CmsTheme.getCmsTheme(parent);
+//             Button button = new Button(parent, SWT.PUSH);
+//             CmsUiUtils.style(button, SuiteStyle.leadPane);
+//             if (icon != null)
+//                     button.setImage(icon.getBigIcon(theme));
+//             button.setLayoutData(new GridData(SWT.CENTER, SWT.BOTTOM, true, false));
+//             // button.setToolTipText(msg.lead());
+//             if (msg != null) {
+//                     Label lbl = new Label(parent, SWT.CENTER);
+//                     CmsUiUtils.style(lbl, SuiteStyle.leadPane);
+//                     // CmsUiUtils.markup(lbl);
+//                     ClassLoader l10nClassLoader = getClass().getClassLoader();
+//                     String txt = LocaleUtils.lead(msg, l10nClassLoader);
+////                   String txt = msg.lead();
+//                     lbl.setText(txt);
+//                     lbl.setLayoutData(new GridData(SWT.CENTER, SWT.TOP, true, false));
+//             }
+//             CmsUiUtils.sendEventOnSelect(button, SuiteEvent.switchLayer.topic(), SuiteEvent.LAYER, layer);
+//             return button;
+//     }
+}
diff --git a/swt/org.argeo.app.swt/src/org/argeo/app/swt/ux/DefaultLoginScreen.java b/swt/org.argeo.app.swt/src/org/argeo/app/swt/ux/DefaultLoginScreen.java
new file mode 100644 (file)
index 0000000..326ed4f
--- /dev/null
@@ -0,0 +1,39 @@
+package org.argeo.app.swt.ux;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.cms.CmsContext;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.cms.CurrentUser;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.acr.SwtUiProvider;
+import org.argeo.cms.swt.auth.CmsLogin;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/** Provides a login screen. */
+public class DefaultLoginScreen implements SwtUiProvider {
+       private CmsContext cmsContext;
+
+       @Override
+       public Control createUiPart(Composite parent, Content context) {
+               CmsView cmsView = CmsSwtUtils.getCmsView(parent);
+               if (!cmsView.isAnonymous())
+                       throw new IllegalStateException(CurrentUser.getUsername() + " is already logged in");
+
+               parent.setLayout(new GridLayout());
+               Composite loginArea = new Composite(parent, SWT.NONE);
+               loginArea.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true));
+
+               CmsLogin cmsLogin = new CmsLogin(cmsView, cmsContext);
+               cmsLogin.createUi(loginArea);
+               return cmsLogin.getCredentialsBlock();
+       }
+
+       public void setCmsContext(CmsContext cmsContext) {
+               this.cmsContext = cmsContext;
+       }
+
+}
diff --git a/swt/org.argeo.app.swt/src/org/argeo/app/swt/ux/SwtArgeoApp.java b/swt/org.argeo.app.swt/src/org/argeo/app/swt/ux/SwtArgeoApp.java
new file mode 100644 (file)
index 0000000..0b6c34d
--- /dev/null
@@ -0,0 +1,662 @@
+package org.argeo.app.swt.ux;
+
+import static org.argeo.api.cms.ux.CmsView.CMS_VIEW_UID_PROPERTY;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import javax.xml.namespace.QName;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.ContentRepository;
+import org.argeo.api.acr.spi.ProvidedSession;
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.api.cms.CmsEvent;
+import org.argeo.api.cms.CmsEventSubscriber;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.api.cms.CmsSession;
+import org.argeo.api.cms.ux.CmsTheme;
+import org.argeo.api.cms.ux.CmsUi;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.app.api.AppUserState;
+import org.argeo.app.api.EntityConstants;
+import org.argeo.app.api.EntityName;
+import org.argeo.app.api.EntityType;
+import org.argeo.app.api.RankedObject;
+import org.argeo.app.ux.AbstractArgeoApp;
+import org.argeo.app.ux.AppUi;
+import org.argeo.app.ux.SuiteUxEvent;
+import org.argeo.cms.LocaleUtils;
+import org.argeo.cms.Localized;
+import org.argeo.cms.acr.ContentUtils;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.acr.SwtUiProvider;
+import org.argeo.cms.swt.dialogs.CmsFeedback;
+import org.argeo.cms.util.LangUtils;
+import org.argeo.cms.ux.CmsUxUtils;
+import org.argeo.eclipse.ui.specific.UiContext;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.osgi.framework.Constants;
+
+/** The Argeo Suite App. */
+public class SwtArgeoApp extends AbstractArgeoApp implements CmsEventSubscriber {
+       private final static CmsLog log = CmsLog.getLog(SwtArgeoApp.class);
+
+       public final static String PUBLIC_BASE_PATH_PROPERTY = "publicBasePath";
+       public final static String DEFAULT_UI_NAME_PROPERTY = "defaultUiName";
+       public final static String DEFAULT_THEME_ID_PROPERTY = "defaultThemeId";
+       public final static String DEFAULT_LAYER_PROPERTY = "defaultLayer";
+       private final static String LOGIN = "login";
+       private final static String HOME_STATE = "~";
+
+       private String publicBasePath = null;
+
+       private String pidPrefix;
+       private String headerPid;
+       private String footerPid;
+       private String leadPanePid;
+       private String adminLeadPanePid;
+       private String loginScreenPid;
+
+       private String defaultUiName = "app";
+       private String adminUiName = "admin";
+
+       // FIXME such default names make refactoring more dangerous
+       @Deprecated
+       private String defaultLayerPid = "argeo.suite.ui.dashboardLayer";
+       @Deprecated
+       private String defaultThemeId = "org.argeo.app.theme.default";
+
+       // TODO use QName as key for byType
+       private Map<String, RankedObject<SwtUiProvider>> uiProvidersByPid = Collections.synchronizedMap(new HashMap<>());
+       private Map<String, RankedObject<SwtUiProvider>> uiProvidersByType = Collections.synchronizedMap(new HashMap<>());
+       private Map<String, RankedObject<SwtAppLayer>> layersByPid = Collections.synchronizedSortedMap(new TreeMap<>());
+       private Map<String, RankedObject<SwtAppLayer>> layersByType = Collections.synchronizedSortedMap(new TreeMap<>());
+
+//     private CmsUserManager cmsUserManager;
+
+       // TODO make more optimal or via CmsSession/CmsView
+       private Map<String, SwtAppUi> managedUis = new HashMap<>();
+
+       // ACR
+       private ContentRepository contentRepository;
+       private AppUserState appUserState;
+       // JCR
+//     private Repository repository;
+
+       public void init(Map<String, Object> properties) {
+               for (SuiteUxEvent event : SuiteUxEvent.values()) {
+                       getCmsContext().getCmsEventBus().addEventSubscriber(event.topic(), this);
+               }
+
+               if (log.isDebugEnabled())
+                       log.info("Argeo Suite App started");
+
+               if (properties.containsKey(DEFAULT_UI_NAME_PROPERTY))
+                       defaultUiName = LangUtils.get(properties, DEFAULT_UI_NAME_PROPERTY);
+               if (properties.containsKey(DEFAULT_THEME_ID_PROPERTY))
+                       defaultThemeId = LangUtils.get(properties, DEFAULT_THEME_ID_PROPERTY);
+               if (properties.containsKey(DEFAULT_LAYER_PROPERTY))
+                       defaultLayerPid = LangUtils.get(properties, DEFAULT_LAYER_PROPERTY);
+               publicBasePath = LangUtils.get(properties, PUBLIC_BASE_PATH_PROPERTY);
+
+               if (properties.containsKey(Constants.SERVICE_PID)) {
+                       String servicePid = properties.get(Constants.SERVICE_PID).toString();
+                       if (servicePid.endsWith(".app")) {
+                               pidPrefix = servicePid.substring(0, servicePid.length() - "app".length());
+                       }
+               }
+
+               if (pidPrefix == null)
+                       throw new IllegalArgumentException("PID prefix must be set.");
+
+               headerPid = pidPrefix + "header";
+               footerPid = pidPrefix + "footer";
+               leadPanePid = pidPrefix + "leadPane";
+               adminLeadPanePid = pidPrefix + "adminLeadPane";
+               loginScreenPid = pidPrefix + "loginScreen";
+       }
+
+       public void destroy(Map<String, Object> properties) {
+               for (SwtAppUi ui : managedUis.values())
+                       if (!ui.isDisposed()) {
+                               ui.getDisplay().syncExec(() -> ui.dispose());
+                       }
+               if (log.isDebugEnabled())
+                       log.info("Argeo Suite App stopped");
+
+       }
+
+       @Override
+       public Set<String> getUiNames() {
+               HashSet<String> uiNames = new HashSet<>();
+               uiNames.add(defaultUiName);
+               uiNames.add(adminUiName);
+               return uiNames;
+       }
+
+       @Override
+       public CmsUi initUi(Object parent) {
+               Composite uiParent = (Composite) parent;
+               String uiName = uiParent.getData(UI_NAME_PROPERTY) != null ? uiParent.getData(UI_NAME_PROPERTY).toString()
+                               : null;
+               CmsView cmsView = CmsSwtUtils.getCmsView(uiParent);
+               if (cmsView == null)
+                       throw new IllegalStateException("No CMS view is registered.");
+               CmsTheme theme = getTheme(uiName);
+               if (theme != null)
+                       CmsSwtUtils.registerCmsTheme(uiParent.getShell(), theme);
+               SwtAppUi argeoSuiteUi = new SwtAppUi(uiParent, SWT.INHERIT_DEFAULT);
+               String uid = cmsView.getUid();
+               managedUis.put(uid, argeoSuiteUi);
+               argeoSuiteUi.addDisposeListener((e) -> {
+                       managedUis.remove(uid);
+                       if (log.isDebugEnabled())
+                               log.debug("Suite UI " + uid + " has been disposed.");
+               });
+               return argeoSuiteUi;
+       }
+
+       @Override
+       public String getThemeId(String uiName) {
+               String themeId = System.getProperty("org.argeo.app.theme.default");
+               if (themeId != null)
+                       return themeId;
+               return defaultThemeId;
+       }
+
+       @Override
+       public void refreshUi(CmsUi cmsUi, String state) {
+               try {
+                       Content context = null;
+                       SwtAppUi ui = (SwtAppUi) cmsUi;
+
+                       String uiName = Objects.toString(ui.getParent().getData(UI_NAME_PROPERTY), null);
+                       if (uiName == null)
+                               throw new IllegalStateException("UI name should not be null");
+                       CmsView cmsView = CmsSwtUtils.getCmsView(ui);
+
+                       ProvidedSession contentSession = (ProvidedSession) CmsUxUtils.getContentSession(contentRepository, cmsView);
+
+                       SwtUiProvider headerUiProvider = findUiProvider(headerPid);
+                       SwtUiProvider footerUiProvider = findUiProvider(footerPid);
+                       SwtUiProvider leadPaneUiProvider;
+                       if (adminUiName.equals(uiName)) {
+                               leadPaneUiProvider = findUiProvider(adminLeadPanePid);
+                       } else {
+                               leadPaneUiProvider = findUiProvider(leadPanePid);
+                       }
+
+                       Localized appTitle = null;
+                       if (headerUiProvider instanceof DefaultHeader) {
+                               appTitle = ((DefaultHeader) headerUiProvider).getTitle();
+                       }
+                       ui.setTitle(appTitle);
+
+                       if (cmsView.isAnonymous() && publicBasePath == null) {// internal app, must login
+                               ui.logout();
+                               ui.setLoginScreen(true);
+                               if (headerUiProvider != null)
+                                       refreshPart(headerUiProvider, ui.getHeader(), context);
+                               ui.refreshBelowHeader(false);
+                               refreshPart(findUiProvider(loginScreenPid), ui.getBelowHeader(), context);
+                               if (footerUiProvider != null)
+                                       refreshPart(footerUiProvider, ui.getFooter(), context);
+                               ui.layout(true, true);
+                               setState(ui, LOGIN);
+                       } else {
+                               if (LOGIN.equals(state))
+                                       state = null;
+                               if (ui.isLoginScreen()) {
+                                       ui.setLoginScreen(false);
+                               }
+                               CmsSession cmsSession = cmsView.getCmsSession();
+                               if (ui.getUserDir() == null) {
+                                       // FIXME NPE on CMSSession when logging in from anonymous
+                                       if (cmsSession == null || cmsView.isAnonymous()) {
+                                               assert publicBasePath != null;
+                                               Content userDir = contentSession
+                                                               .get(ContentUtils.SLASH + CmsConstants.SYS_WORKSPACE + publicBasePath);
+                                               ui.setUserDir(userDir);
+                                       } else {
+                                               Content userDir = appUserState.getOrCreateSessionDir(contentSession, cmsSession);
+                                               ui.setUserDir(userDir);
+//                                             Node userDirNode = jcrContentProvider.doInAdminSession((adminSession) -> {
+//                                                     Node node = SuiteUtils.getOrCreateCmsSessionNode(adminSession, cmsSession);
+//                                                     return node;
+//                                             });
+//                                             Content userDir = contentSession
+//                                                             .get(ContentUtils.SLASH + CmsConstants.SYS_WORKSPACE + userDirNode.getPath());
+//                                             ui.setUserDir(userDir);
+                                       }
+                               }
+                               initLocale(cmsSession);
+                               context = stateToNode(ui, state);
+                               if (context == null)
+                                       context = ui.getUserDir();
+
+                               if (headerUiProvider != null)
+                                       refreshPart(headerUiProvider, ui.getHeader(), context);
+                               ui.refreshBelowHeader(true);
+                               for (String key : layersByPid.keySet()) {
+                                       SwtAppLayer layer = layersByPid.get(key).get();
+                                       ui.addLayer(key, layer);
+                               }
+
+                               if (leadPaneUiProvider != null)
+                                       refreshPart(leadPaneUiProvider, ui.getLeadPane(), context);
+                               if (footerUiProvider != null)
+                                       refreshPart(footerUiProvider, ui.getFooter(), context);
+                               ui.layout(true, true);
+                               setState(ui, state != null ? state : defaultLayerPid);
+                       }
+               } catch (Exception e) {
+                       CmsFeedback.error("Unexpected exception", e);
+               }
+       }
+
+       private void initLocale(CmsSession cmsSession) {
+               if (cmsSession == null)
+                       return;
+               Locale locale = cmsSession.getLocale();
+               UiContext.setLocale(locale);
+               LocaleUtils.setThreadLocale(locale);
+
+       }
+
+       private void refreshPart(SwtUiProvider uiProvider, Composite part, Content context) {
+               CmsSwtUtils.clear(part);
+               uiProvider.createUiPart(part, context);
+       }
+
+       private SwtUiProvider findUiProvider(String pid) {
+               if (!uiProvidersByPid.containsKey(pid))
+                       return null;
+               return uiProvidersByPid.get(pid).get();
+       }
+
+       private SwtAppLayer findLayer(String pid) {
+               if (!layersByPid.containsKey(pid))
+                       return null;
+               return layersByPid.get(pid).get();
+       }
+
+       private <T> T findByType(Map<String, RankedObject<T>> byType, Content content) {
+               if (content == null)
+                       throw new IllegalArgumentException("A node should be provided");
+
+//             boolean checkJcr = false;
+//             if (checkJcr && content instanceof JcrContent) {
+//                     Node context = ((JcrContent) content).getJcrNode();
+//                     try {
+//                             // mixins
+//                             Set<String> types = new TreeSet<>();
+//                             for (NodeType mixinType : context.getMixinNodeTypes()) {
+//                                     String mixinTypeName = mixinType.getName();
+//                                     if (byType.containsKey(mixinTypeName)) {
+//                                             types.add(mixinTypeName);
+//                                     }
+//                                     for (NodeType superType : mixinType.getDeclaredSupertypes()) {
+//                                             if (byType.containsKey(superType.getName())) {
+//                                                     types.add(superType.getName());
+//                                             }
+//                                     }
+//                             }
+//                             // primary node type
+//                             NodeType primaryType = context.getPrimaryNodeType();
+//                             String primaryTypeName = primaryType.getName();
+//                             if (byType.containsKey(primaryTypeName)) {
+//                                     types.add(primaryTypeName);
+//                             }
+//                             for (NodeType superType : primaryType.getDeclaredSupertypes()) {
+//                                     if (byType.containsKey(superType.getName())) {
+//                                             types.add(superType.getName());
+//                                     }
+//                             }
+//                             // entity type
+//                             if (context.isNodeType(EntityType.entity.get())) {
+//                                     if (context.hasProperty(EntityNames.ENTITY_TYPE)) {
+//                                             String entityTypeName = context.getProperty(EntityNames.ENTITY_TYPE).getString();
+//                                             if (byType.containsKey(entityTypeName)) {
+//                                                     types.add(entityTypeName);
+//                                             }
+//                                     }
+//                             }
+//
+//                             if (CmsJcrUtils.isUserHome(context) && byType.containsKey("nt:folder")) {// home node
+//                                     types.add("nt:folder");
+//                             }
+//
+//                             if (types.size() == 0)
+//                                     throw new IllegalArgumentException(
+//                                                     "No type found for " + context + " (" + listTypes(context) + ")");
+//                             String type = types.iterator().next();
+//                             if (!byType.containsKey(type))
+//                                     throw new IllegalArgumentException("No component found for " + context + " with type " + type);
+//                             return byType.get(type).get();
+//                     } catch (RepositoryException e) {
+//                             throw new IllegalStateException(e);
+//                     }
+//
+//             } else {
+               Set<String> types = new TreeSet<>();
+               if (content.hasContentClass(EntityType.entity.qName())) {
+                       String type = content.attr(EntityName.type.qName());
+                       if (type != null && byType.containsKey(type))
+                               types.add(type);
+               }
+
+               List<QName> objectClasses = content.getContentClasses();
+               for (QName cc : objectClasses) {
+                       String type = cc.getPrefix() + ":" + cc.getLocalPart();
+                       if (byType.containsKey(type))
+                               types.add(type);
+               }
+               if (types.size() == 0) {
+                       throw new IllegalArgumentException("No type found for " + content + " (" + objectClasses + ")");
+               }
+               String type = types.iterator().next();
+               if (!byType.containsKey(type))
+                       throw new IllegalArgumentException("No component found for " + content + " with type " + type);
+               return byType.get(type).get();
+//             }
+       }
+
+//     private static String listTypes(Node context) {
+//             try {
+//                     StringBuilder sb = new StringBuilder();
+//                     sb.append(context.getPrimaryNodeType().getName());
+//                     for (NodeType superType : context.getPrimaryNodeType().getDeclaredSupertypes()) {
+//                             sb.append(' ');
+//                             sb.append(superType.getName());
+//                     }
+//
+//                     for (NodeType nodeType : context.getMixinNodeTypes()) {
+//                             sb.append(' ');
+//                             sb.append(nodeType.getName());
+//                             if (nodeType.getName().equals(EntityType.local.get()))
+//                                     sb.append('/').append(context.getProperty(EntityNames.ENTITY_TYPE).getString());
+//                             for (NodeType superType : nodeType.getDeclaredSupertypes()) {
+//                                     sb.append(' ');
+//                                     sb.append(superType.getName());
+//                             }
+//                     }
+//                     return sb.toString();
+//             } catch (RepositoryException e) {
+//                     throw new JcrException(e);
+//             }
+//     }
+
+       @Override
+       public void setState(CmsUi cmsUi, String state) {
+               AppUi ui = (AppUi) cmsUi;
+               if (state == null)
+                       return;
+               if (!state.startsWith("/")) {
+//                     if (cmsUi instanceof SwtAppUi) {
+//                             SwtAppUi ui = (SwtAppUi) cmsUi;
+                       if (LOGIN.equals(state)) {
+                               String appTitle = "";
+                               if (ui.getTitle() != null)
+                                       appTitle = ui.getTitle().lead();
+                               ui.getCmsView().stateChanged(state, appTitle);
+                               return;
+                       }
+                       Map<String, Object> properties = new HashMap<>();
+                       String layerId = HOME_STATE.equals(state) ? defaultLayerPid : state;
+                       properties.put(SuiteUxEvent.LAYER, layerId);
+                       properties.put(SuiteUxEvent.CONTENT_PATH, HOME_STATE);
+                       ui.getCmsView().sendEvent(SuiteUxEvent.switchLayer.topic(), properties);
+//                     }
+                       return;
+               }
+//             SwtAppUi suiteUi = (SwtAppUi) cmsUi;
+               if (ui.isLoginScreen()) {
+                       return;
+               }
+
+               Content node = stateToNode(ui, state);
+               if (node == null) {
+                       ui.getCmsView().navigateTo(HOME_STATE);
+               } else {
+                       ui.getCmsView().sendEvent(SuiteUxEvent.switchLayer.topic(), SuiteUxEvent.eventProperties(node));
+                       ui.getCmsView().sendEvent(SuiteUxEvent.refreshPart.topic(), SuiteUxEvent.eventProperties(node));
+               }
+       }
+
+       // TODO move it to an internal package?
+       public static String nodeToState(Content node) {
+               return node.getPath();
+       }
+
+       private Content stateToNode(CmsUi suiteUi, String state) {
+               if (suiteUi == null)
+                       return null;
+               if (state == null || !state.startsWith("/"))
+                       return null;
+
+               String path = state;
+
+               ProvidedSession contentSession = (ProvidedSession) CmsUxUtils.getContentSession(contentRepository,
+                               suiteUi.getCmsView());
+               return contentSession.get(path);
+       }
+
+       /*
+        * Events management
+        */
+
+       @Override
+       public void onEvent(String topic, Map<String, Object> event) {
+
+               // Specific UI related events
+               SwtAppUi ui = getRelatedUi(event);
+               if (ui == null)
+                       return;
+               ui.getCmsView().runAs(() -> {
+                       try {
+                               String appTitle = "";
+                               if (ui.getTitle() != null)
+                                       appTitle = ui.getTitle().lead() + " - ";
+
+                               if (isTopic(topic, SuiteUxEvent.refreshPart)) {
+                                       Content node = getContentFromEvent(ui, event);
+                                       if (node == null)
+                                               return;
+                                       SwtUiProvider uiProvider = findByType(uiProvidersByType, node);
+                                       SwtAppLayer layer = findByType(layersByType, node);
+                                       ui.switchToLayer(layer, node);
+                                       layer.view(uiProvider, ui.getCurrentWorkArea(), node);
+                                       ui.getCmsView().stateChanged(nodeToState(node), appTitle + CmsUxUtils.getTitle(node));
+                               } else if (isTopic(topic, SuiteUxEvent.openNewPart)) {
+                                       Content node = getContentFromEvent(ui, event);
+                                       if (node == null)
+                                               return;
+                                       SwtUiProvider uiProvider = findByType(uiProvidersByType, node);
+                                       SwtAppLayer layer = findByType(layersByType, node);
+                                       ui.switchToLayer(layer, node);
+                                       layer.open(uiProvider, ui.getCurrentWorkArea(), node);
+                                       ui.getCmsView().stateChanged(nodeToState(node), appTitle + CmsUxUtils.getTitle(node));
+                               } else if (isTopic(topic, SuiteUxEvent.switchLayer)) {
+                                       String layerId = get(event, SuiteUxEvent.LAYER);
+                                       if (layerId != null) {
+                                               SwtAppLayer suiteLayer = findLayer(layerId);
+                                               if (suiteLayer == null)
+                                                       throw new IllegalArgumentException("No layer '" + layerId + "' available.");
+                                               Localized layerTitle = suiteLayer.getTitle();
+                                               // FIXME make sure we don't rebuild the work area twice
+                                               Composite workArea = ui.switchToLayer(layerId, ui.getUserDir());
+                                               String title = null;
+                                               if (layerTitle != null)
+                                                       title = layerTitle.lead();
+                                               Content nodeFromState = getContentFromEvent(ui, event);
+                                               if (nodeFromState != null && nodeFromState.getPath().equals(ui.getUserDir().getPath())) {
+                                                       // default layer view is forced
+                                                       String state = defaultLayerPid.equals(layerId) ? "~" : layerId;
+                                                       ui.getCmsView().stateChanged(state, appTitle + title);
+                                                       suiteLayer.view(null, workArea, nodeFromState);
+                                               } else {
+                                                       Content layerCurrentContext = suiteLayer.getCurrentContext(workArea);
+                                                       if (layerCurrentContext != null && !layerCurrentContext.equals(ui.getUserDir())) {
+                                                               // layer was already showing a context so we set the state to it
+                                                               ui.getCmsView().stateChanged(nodeToState(layerCurrentContext),
+                                                                               appTitle + CmsUxUtils.getTitle(layerCurrentContext));
+                                                       } else {
+                                                               // no context was shown
+                                                               ui.getCmsView().stateChanged(layerId, appTitle + title);
+                                                       }
+                                               }
+                                       } else {
+                                               Content node = getContentFromEvent(ui, event);
+                                               if (node != null) {
+                                                       SwtAppLayer layer = findByType(layersByType, node);
+                                                       ui.switchToLayer(layer, node);
+                                               }
+                                       }
+                               }
+                       } catch (Exception e) {
+                               CmsFeedback.error("Cannot handle event " + topic + " " + event, e);
+//                             log.error("Cannot handle event " + event, e);
+                       }
+               });
+       }
+
+       private boolean isTopic(String topic, CmsEvent cmsEvent) {
+               Objects.requireNonNull(topic);
+               return topic.equals(cmsEvent.topic());
+       }
+
+       protected Content getContentFromEvent(SwtAppUi ui, Map<String, Object> event) {
+               ProvidedSession contentSession = (ProvidedSession) CmsUxUtils.getContentSession(contentRepository,
+                               ui.getCmsView());
+
+               String path = get(event, SuiteUxEvent.CONTENT_PATH);
+
+               if (path != null && (path.equals(HOME_STATE) || path.equals("")))
+                       return ui.getUserDir();
+               Content node;
+               if (path == null) {
+                       return null;
+//                     // look for a user
+//                     String username = get(event, SuiteUxEvent.USERNAME);
+//                     if (username == null)
+//                             return null;
+//                     User user = cmsUserManager.getUser(username);
+//                     if (user == null)
+//                             return null;
+//                     node = ContentUtils.roleToContent(cmsUserManager, contentSession, user);
+               } else {
+                       node = contentSession.get(path);
+               }
+               return node;
+       }
+
+       private SwtAppUi getRelatedUi(Map<String, Object> eventProperties) {
+               return managedUis.get(get(eventProperties, CMS_VIEW_UID_PROPERTY));
+       }
+
+       public static String get(Map<String, Object> eventProperties, String key) {
+               Object value = eventProperties.get(key);
+               if (value == null)
+                       return null;
+               return value.toString();
+
+       }
+
+       /*
+        * Dependency injection.
+        */
+
+       public void addUiProvider(SwtUiProvider uiProvider, Map<String, Object> properties) {
+               if (properties.containsKey(Constants.SERVICE_PID)) {
+                       String pid = (String) properties.get(Constants.SERVICE_PID);
+                       RankedObject.putIfHigherRank(uiProvidersByPid, pid, uiProvider, properties);
+               }
+               if (properties.containsKey(EntityConstants.TYPE)) {
+                       List<String> types = LangUtils.toStringList(properties.get(EntityConstants.TYPE));
+                       for (String type : types) {
+                               RankedObject.putIfHigherRank(uiProvidersByType, type, uiProvider, properties);
+                       }
+               }
+       }
+
+       public void removeUiProvider(SwtUiProvider uiProvider, Map<String, Object> properties) {
+               if (properties.containsKey(Constants.SERVICE_PID)) {
+                       String pid = (String) properties.get(Constants.SERVICE_PID);
+                       if (uiProvidersByPid.containsKey(pid)) {
+                               if (uiProvidersByPid.get(pid).equals(new RankedObject<SwtUiProvider>(uiProvider, properties))) {
+                                       uiProvidersByPid.remove(pid);
+                               }
+                       }
+               }
+               if (properties.containsKey(EntityConstants.TYPE)) {
+                       List<String> types = LangUtils.toStringList(properties.get(EntityConstants.TYPE));
+                       for (String type : types) {
+                               if (uiProvidersByType.containsKey(type)) {
+                                       if (uiProvidersByType.get(type).equals(new RankedObject<SwtUiProvider>(uiProvider, properties))) {
+                                               uiProvidersByType.remove(type);
+                                       }
+                               }
+                       }
+               }
+       }
+
+       public void addLayer(SwtAppLayer layer, Map<String, Object> properties) {
+               if (properties.containsKey(Constants.SERVICE_PID)) {
+                       String pid = (String) properties.get(Constants.SERVICE_PID);
+                       RankedObject.putIfHigherRank(layersByPid, pid, layer, properties);
+               }
+               if (properties.containsKey(EntityConstants.TYPE)) {
+                       List<String> types = LangUtils.toStringList(properties.get(EntityConstants.TYPE));
+                       for (String type : types)
+                               RankedObject.putIfHigherRank(layersByType, type, layer, properties);
+               }
+       }
+
+       public void removeLayer(SwtAppLayer layer, Map<String, Object> properties) {
+               if (properties.containsKey(Constants.SERVICE_PID)) {
+                       String pid = (String) properties.get(Constants.SERVICE_PID);
+                       if (layersByPid.containsKey(pid)) {
+                               if (layersByPid.get(pid).equals(new RankedObject<SwtAppLayer>(layer, properties))) {
+                                       layersByPid.remove(pid);
+                               }
+                       }
+               }
+               if (properties.containsKey(EntityConstants.TYPE)) {
+                       List<String> types = LangUtils.toStringList(properties.get(EntityConstants.TYPE));
+                       for (String type : types) {
+                               if (layersByType.containsKey(type)) {
+                                       if (layersByType.get(type).equals(new RankedObject<SwtAppLayer>(layer, properties))) {
+                                               layersByType.remove(type);
+                                       }
+                               }
+                       }
+               }
+       }
+
+//     public void setCmsUserManager(CmsUserManager cmsUserManager) {
+//             this.cmsUserManager = cmsUserManager;
+//     }
+
+//     protected ContentRepository getContentRepository() {
+//             return contentRepository;
+//     }
+
+       public void setContentRepository(ContentRepository contentRepository) {
+               this.contentRepository = contentRepository;
+       }
+
+       public void setAppUserState(AppUserState appUserState) {
+               this.appUserState = appUserState;
+       }
+
+}
index 306ad942ae5f91c9789188acb55cc1f1d49a1878..db87157f406dd16447bd2c4336f0ba10e31d8ad7 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" immediate="false" name="Admin Lead Pane">
-   <implementation class="org.argeo.app.ui.DefaultLeadPane"/>
+   <implementation class="org.argeo.app.swt.ux.DefaultLeadPane"/>
    <service>
       <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
    </service>
index f170f9708e04b86fd1f69d0a5e95e66a37f954d6..7a516fcfd896de79bf061bc4ea611a052e46b26b 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="Argeo Suite App">
-   <implementation class="org.argeo.app.ui.SuiteApp"/>
+   <implementation class="org.argeo.app.swt.ux.SwtArgeoApp"/>
    <service>
       <provide interface="org.argeo.api.cms.CmsApp"/>
    </service>
index 8d202315b3f0201523f22cd5c4ecefa2d20330ec..ded75df8929523182049679b15bf8aae8a47c597 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" immediate="false" name="Default Suite Footer">
-   <implementation class="org.argeo.app.ui.DefaultFooter"/>
+   <implementation class="org.argeo.app.swt.ux.DefaultFooter"/>
    <service>
       <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
    </service>
index cb792e521813937541e053a8c9de452ed5910958..e6713ed32f18dad70e9c901a48ea6f7b69c94fcc 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" immediate="false" name="Default Suite Header">
-   <implementation class="org.argeo.app.ui.DefaultHeader"/>
+   <implementation class="org.argeo.app.swt.ux.DefaultHeader"/>
    <service>
       <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
    </service>
index a6204382e807e24b0510a3b74f6c0d10b68c1dfb..57a746863ba45222300aa9bea6a00b746db675f8 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" immediate="false" name="Default Lead Pane">
-   <implementation class="org.argeo.app.ui.DefaultLeadPane"/>
+   <implementation class="org.argeo.app.swt.ux.DefaultLeadPane"/>
    <service>
       <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
    </service>
index eab7592c9dcc61190c09d113aa956c514c358b48..6b3d49d65b33f17dc1b7e21dbf69708ab44fce90 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Default Login Screen">
-   <implementation class="org.argeo.app.ui.DefaultLoginScreen"/>
+   <implementation class="org.argeo.app.swt.ux.DefaultLoginScreen"/>
    <properties entry="config/loginScreen.properties"/>
    <service>
       <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
index 4a74f2d830a6bd076dd24bd5829354b56fdc75f3..fb2e3f72382239853355c5d22fc7b61fc25d9999 100644 (file)
@@ -39,4 +39,5 @@ org.eclipse.jface.window,\
 org.eclipse.jface.dialogs,\
 org.eclipse.rap.rwt,\
 javax.servlet.*;version="[3,5)",\
+javax.jcr.nodetype,\
 *
diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultFooter.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultFooter.java
deleted file mode 100644 (file)
index 5e54368..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-package org.argeo.app.ui;
-
-import java.util.Map;
-
-import org.argeo.api.acr.Content;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.ui.CmsUiProvider;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.osgi.framework.BundleContext;
-
-/** Footer of a standard Argeo Suite application. */
-public class DefaultFooter implements CmsUiProvider {
-       @Override
-       public Control createUiPart(Composite parent, Content context) {
-               parent.setLayout(CmsSwtUtils.noSpaceGridLayout());
-               Composite content = new Composite(parent, SWT.NONE);
-               content.setLayoutData(new GridData(0, 0));
-               Control contentControl = createContent(content, context);
-
-               // TODO support and guarantee
-
-               return contentControl;
-       }
-
-       protected Control createContent(Composite parent, Content context) {
-               return parent;
-       }
-
-       public void init(BundleContext bundleContext, Map<String, String> properties) {
-       }
-
-       public void destroy(BundleContext bundleContext, Map<String, String> properties) {
-
-       }
-}
diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultHeader.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultHeader.java
deleted file mode 100644 (file)
index ede28d1..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-package org.argeo.app.ui;
-
-import java.util.Map;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.app.ux.SuiteIcon;
-import org.argeo.app.ux.SuiteStyle;
-import org.argeo.cms.CurrentUser;
-import org.argeo.cms.Localized;
-import org.argeo.cms.swt.CmsSwtTheme;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.ui.CmsUiProvider;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.MouseAdapter;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.wiring.BundleWiring;
-
-/** Header of a standard Argeo Suite application. */
-public class DefaultHeader implements CmsUiProvider {
-       public final static String TITLE_PROPERTY = "argeo.suite.ui.header.title";
-       private Localized title = null;
-
-       @Override
-       public Control createUiPart(Composite parent, Content context) {
-               CmsView cmsView = CmsSwtUtils.getCmsView(parent);
-               CmsSwtTheme theme = CmsSwtUtils.getCmsTheme(parent);
-
-               parent.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(3, true)));
-
-               // TODO right to left
-               Composite lead = new Composite(parent, SWT.NONE);
-               CmsSwtUtils.style(lead, SuiteStyle.header);
-               lead.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, true, false));
-               lead.setLayout(new GridLayout());
-               Label lbl = new Label(lead, SWT.NONE);
-//             String title = properties.get(TITLE_PROPERTY);
-//             // TODO expose the localized
-//             lbl.setText(LocaleUtils.isLocaleKey(title) ? LocaleUtils.local(title, getClass().getClassLoader()).toString()
-//                             : title);
-               lbl.setText(title.lead());
-               CmsSwtUtils.style(lbl, SuiteStyle.headerTitle);
-               lbl.setLayoutData(CmsSwtUtils.fillWidth());
-
-               Composite middle = new Composite(parent, SWT.NONE);
-               CmsSwtUtils.style(middle, SuiteStyle.header);
-               middle.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false));
-               middle.setLayout(new GridLayout());
-
-               Composite end = new Composite(parent, SWT.NONE);
-               CmsSwtUtils.style(end, SuiteStyle.header);
-               end.setLayoutData(new GridData(SWT.END, SWT.CENTER, true, false));
-
-               if (!cmsView.isAnonymous()) {
-                       end.setLayout(new GridLayout(2, false));
-                       Label userL = new Label(end, SWT.NONE);
-                       CmsSwtUtils.style(userL, SuiteStyle.header);
-                       userL.setText(CurrentUser.getDisplayName());
-//                     Button logoutB = new Button(end, SWT.FLAT);
-//                     logoutB.setImage(theme.getSmallIcon(SuiteIcon.logout));
-//                     logoutB.addSelectionListener(new SelectionAdapter() {
-//                             private static final long serialVersionUID = 7116760083964201233L;
-//
-//                             @Override
-//                             public void widgetSelected(SelectionEvent e) {
-//                                     cmsView.logout();
-//                             }
-//
-//                     });
-                       Label logOutL = new Label(end, 0);
-                       logOutL.setImage(theme.getSmallIcon(SuiteIcon.openUserMenu));
-                       logOutL.addMouseListener(new MouseAdapter() {
-                               private static final long serialVersionUID = 6908266850511460799L;
-
-                               @Override
-                               public void mouseDown(MouseEvent e) {
-                                       cmsView.logout();
-                               }
-
-                       });
-               } else {
-                       end.setLayout(new GridLayout(1, false));
-                       // required in order to avoid wrong height after logout
-                       new Label(end, SWT.NONE).setText("");
-
-               }
-               return lbl;
-       }
-
-       public void init(BundleContext bundleContext, Map<String, String> properties) {
-               String titleStr = (String) properties.get(TITLE_PROPERTY);
-               if (titleStr != null) {
-                       if (titleStr.startsWith("%")) {
-                               title = new Localized() {
-
-                                       @Override
-                                       public String name() {
-                                               return titleStr;
-                                       }
-
-                                       @Override
-                                       public ClassLoader getL10nClassLoader() {
-                                               return bundleContext != null
-                                                               ? bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader()
-                                                               : getClass().getClassLoader();
-                                       }
-                               };
-                       } else {
-                               title = new Localized.Untranslated(titleStr);
-                       }
-               }
-       }
-
-       public void destroy(BundleContext bundleContext, Map<String, String> properties) {
-
-       }
-
-       public Localized getTitle() {
-               return title;
-       }
-
-}
diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultLeadPane.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultLeadPane.java
deleted file mode 100644 (file)
index 9a2faf5..0000000
+++ /dev/null
@@ -1,198 +0,0 @@
-package org.argeo.app.ui;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.app.api.RankedObject;
-import org.argeo.app.core.SuiteUtils;
-import org.argeo.app.swt.ux.SwtAppLayer;
-import org.argeo.app.ux.SuiteIcon;
-import org.argeo.app.ux.SuiteStyle;
-import org.argeo.cms.CurrentUser;
-import org.argeo.cms.Localized;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.ui.CmsUiProvider;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
-import org.osgi.framework.wiring.BundleWiring;
-
-/** Side pane listing various perspectives. */
-public class DefaultLeadPane implements CmsUiProvider {
-       private final static CmsLog log = CmsLog.getLog(DefaultLeadPane.class);
-
-       public static enum Property {
-               defaultLayers, adminLayers;
-       }
-
-       private Map<String, RankedObject<SwtAppLayer>> layers = Collections.synchronizedSortedMap(new TreeMap<>());
-       private List<String> defaultLayers;
-       private List<String> adminLayers = new ArrayList<>();
-
-       private ClassLoader l10nClassLoader;
-
-       @Override
-       public Control createUiPart(Composite parent, Content node) {
-               CmsView cmsView = CmsSwtUtils.getCmsView(parent);
-               parent.setLayout(CmsSwtUtils.noSpaceGridLayout());
-               Composite appLayersC = new Composite(parent, SWT.NONE);
-               CmsSwtUtils.style(appLayersC, SuiteStyle.leadPane);
-               GridLayout layout = new GridLayout();
-               layout.verticalSpacing = 10;
-               layout.marginTop = 10;
-               layout.marginLeft = 10;
-               layout.marginRight = 10;
-               appLayersC.setLayout(layout);
-               appLayersC.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false));
-
-               Composite adminLayersC;
-               if (!adminLayers.isEmpty()) {
-                       adminLayersC = new Composite(parent, SWT.NONE);
-                       CmsSwtUtils.style(adminLayersC, SuiteStyle.leadPane);
-                       GridLayout adminLayout = new GridLayout();
-                       adminLayout.verticalSpacing = 10;
-                       adminLayout.marginBottom = 10;
-                       adminLayout.marginLeft = 10;
-                       adminLayout.marginRight = 10;
-                       adminLayersC.setLayout(adminLayout);
-                       adminLayersC.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, false, true));
-               } else {
-                       adminLayersC = null;
-               }
-
-//             boolean isAdmin = cmsView.doAs(() -> CurrentUser.isInRole(NodeConstants.ROLE_USER_ADMIN));
-               // Set<String> userRoles = cmsView.doAs(() -> CurrentUser.roles());
-               Button first = null;
-               layers: for (String layerDef : defaultLayers) {
-                       layerDef = layerDef.trim();
-                       if ("".equals(layerDef))
-                               continue layers;// skip empty lines
-                       String[] semiColArr = layerDef.split(";");
-                       String layerId = semiColArr[0];
-                       Set<String> layerRoles = SuiteUtils.extractRoles(semiColArr);
-                       if (layers.containsKey(layerId)) {
-                               if (!layerRoles.isEmpty()) {
-                                       boolean authorized = false;
-                                       authorized = cmsView.doAs(() -> {
-                                               for (String layerRole : layerRoles) {
-                                                       if (CurrentUser.implies(layerRole, null)) {
-                                                               return true;
-                                                       }
-                                               }
-                                               return false;
-                                       });
-                                       if (!authorized)
-                                               continue layers;// skip unauthorized layer
-//                                     Set<String> intersection = new HashSet<String>(layerRoles);
-//                                     intersection.retainAll(userRoles);
-//                                     if (intersection.isEmpty())
-//                                             continue layers;// skip unauthorized layer
-                               }
-                               RankedObject<SwtAppLayer> layerObj = layers.get(layerId);
-
-                               Localized title = null;
-                               if (!adminLayers.contains(layerId)) {
-                                       String titleStr = (String) layerObj.getProperties().get(SwtAppLayer.Property.title.name());
-                                       if (titleStr != null) {
-                                               if (titleStr.startsWith("%")) {
-                                                       // LocaleUtils.local(titleStr, getClass().getClassLoader());
-                                                       title = () -> titleStr;
-                                               } else {
-                                                       title = new Localized.Untranslated(titleStr);
-                                               }
-                                       }
-                               }
-
-                               String iconName = (String) layerObj.getProperties().get(SwtAppLayer.Property.icon.name());
-                               SuiteIcon icon = null;
-                               if (iconName != null)
-                                       icon = SuiteIcon.valueOf(iconName);
-
-                               Composite buttonParent;
-                               if (adminLayers.contains(layerId))
-                                       buttonParent = adminLayersC;
-                               else
-                                       buttonParent = appLayersC;
-                               Button b = SuiteUiUtils.createLayerButton(buttonParent, layerId, title, icon, l10nClassLoader);
-                               if (first == null)
-                                       first = b;
-                       }
-               }
-               return first;
-       }
-
-       public void init(BundleContext bundleContext, Map<String, Object> properties) {
-               l10nClassLoader = bundleContext != null ? bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader()
-                               : getClass().getClassLoader();
-
-               String[] defaultLayers = (String[]) properties.get(Property.defaultLayers.toString());
-               if (defaultLayers == null)
-                       throw new IllegalArgumentException("Default layers must be set.");
-               this.defaultLayers = Arrays.asList(defaultLayers);
-               if (log.isDebugEnabled())
-                       log.debug("Default layers: " + Arrays.asList(defaultLayers));
-               String[] adminLayers = (String[]) properties.get(Property.adminLayers.toString());
-               if (adminLayers != null) {
-                       this.adminLayers = Arrays.asList(adminLayers);
-                       if (log.isDebugEnabled())
-                               log.debug("Admin layers: " + Arrays.asList(adminLayers));
-               }
-       }
-
-       public void destroy(BundleContext bundleContext, Map<String, String> properties) {
-
-       }
-
-       public void addLayer(SwtAppLayer layer, Map<String, Object> properties) {
-               if (properties.containsKey(Constants.SERVICE_PID)) {
-                       String pid = (String) properties.get(Constants.SERVICE_PID);
-                       RankedObject.putIfHigherRank(layers, pid, layer, properties);
-               }
-       }
-
-       public void removeLayer(SwtAppLayer layer, Map<String, Object> properties) {
-               if (properties.containsKey(Constants.SERVICE_PID)) {
-                       String pid = (String) properties.get(Constants.SERVICE_PID);
-                       if (layers.containsKey(pid)) {
-                               if (layers.get(pid).equals(new RankedObject<SwtAppLayer>(layer, properties))) {
-                                       layers.remove(pid);
-                               }
-                       }
-               }
-       }
-
-//     protected Button createLayerButton(Composite parent, String layer, Localized msg, CmsIcon icon) {
-//             CmsTheme theme = CmsTheme.getCmsTheme(parent);
-//             Button button = new Button(parent, SWT.PUSH);
-//             CmsUiUtils.style(button, SuiteStyle.leadPane);
-//             if (icon != null)
-//                     button.setImage(icon.getBigIcon(theme));
-//             button.setLayoutData(new GridData(SWT.CENTER, SWT.BOTTOM, true, false));
-//             // button.setToolTipText(msg.lead());
-//             if (msg != null) {
-//                     Label lbl = new Label(parent, SWT.CENTER);
-//                     CmsUiUtils.style(lbl, SuiteStyle.leadPane);
-//                     // CmsUiUtils.markup(lbl);
-//                     ClassLoader l10nClassLoader = getClass().getClassLoader();
-//                     String txt = LocaleUtils.lead(msg, l10nClassLoader);
-////                   String txt = msg.lead();
-//                     lbl.setText(txt);
-//                     lbl.setLayoutData(new GridData(SWT.CENTER, SWT.TOP, true, false));
-//             }
-//             CmsUiUtils.sendEventOnSelect(button, SuiteEvent.switchLayer.topic(), SuiteEvent.LAYER, layer);
-//             return button;
-//     }
-}
diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultLoginScreen.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultLoginScreen.java
deleted file mode 100644 (file)
index 1cb1f95..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-package org.argeo.app.ui;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.cms.CmsContext;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.cms.CurrentUser;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.auth.CmsLogin;
-import org.argeo.cms.ui.CmsUiProvider;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-
-/** Provides a login screen. */
-public class DefaultLoginScreen implements CmsUiProvider {
-       private CmsContext cmsContext;
-
-       @Override
-       public Control createUiPart(Composite parent, Content context) {
-               CmsView cmsView = CmsSwtUtils.getCmsView(parent);
-               if (!cmsView.isAnonymous())
-                       throw new IllegalStateException(CurrentUser.getUsername() + " is already logged in");
-
-               parent.setLayout(new GridLayout());
-               Composite loginArea = new Composite(parent, SWT.NONE);
-               loginArea.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true));
-
-               CmsLogin cmsLogin = new CmsLogin(cmsView, cmsContext);
-               cmsLogin.createUi(loginArea);
-               return cmsLogin.getCredentialsBlock();
-       }
-
-       public void setCmsContext(CmsContext cmsContext) {
-               this.cmsContext = cmsContext;
-       }
-
-}
diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteApp.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteApp.java
deleted file mode 100644 (file)
index da32c1a..0000000
+++ /dev/null
@@ -1,658 +0,0 @@
-package org.argeo.app.ui;
-
-import static org.argeo.api.cms.ux.CmsView.CMS_VIEW_UID_PROPERTY;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.TreeSet;
-
-import javax.xml.namespace.QName;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.acr.ContentRepository;
-import org.argeo.api.acr.spi.ProvidedSession;
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.api.cms.CmsEventSubscriber;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.api.cms.CmsSession;
-import org.argeo.api.cms.ux.CmsTheme;
-import org.argeo.api.cms.ux.CmsUi;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.app.api.AppUserState;
-import org.argeo.app.api.EntityConstants;
-import org.argeo.app.api.EntityName;
-import org.argeo.app.api.EntityType;
-import org.argeo.app.api.RankedObject;
-import org.argeo.app.swt.ux.SwtAppLayer;
-import org.argeo.app.swt.ux.SwtAppUi;
-import org.argeo.app.ux.AbstractArgeoApp;
-import org.argeo.app.ux.AppUi;
-import org.argeo.app.ux.SuiteUxEvent;
-import org.argeo.cms.LocaleUtils;
-import org.argeo.cms.Localized;
-import org.argeo.cms.acr.ContentUtils;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.acr.SwtUiProvider;
-import org.argeo.cms.swt.dialogs.CmsFeedback;
-import org.argeo.cms.util.LangUtils;
-import org.argeo.cms.ux.CmsUxUtils;
-import org.argeo.eclipse.ui.specific.UiContext;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
-import org.osgi.framework.Constants;
-
-/** The Argeo Suite App. */
-public class SuiteApp extends AbstractArgeoApp implements CmsEventSubscriber {
-       private final static CmsLog log = CmsLog.getLog(SuiteApp.class);
-
-       public final static String PUBLIC_BASE_PATH_PROPERTY = "publicBasePath";
-       public final static String DEFAULT_UI_NAME_PROPERTY = "defaultUiName";
-       public final static String DEFAULT_THEME_ID_PROPERTY = "defaultThemeId";
-       public final static String DEFAULT_LAYER_PROPERTY = "defaultLayer";
-       private final static String LOGIN = "login";
-       private final static String HOME_STATE = "~";
-
-       private String publicBasePath = null;
-
-       private String pidPrefix;
-       private String headerPid;
-       private String footerPid;
-       private String leadPanePid;
-       private String adminLeadPanePid;
-       private String loginScreenPid;
-
-       private String defaultUiName = "app";
-       private String adminUiName = "admin";
-
-       // FIXME such default names make refactoring more dangerous
-       @Deprecated
-       private String defaultLayerPid = "argeo.suite.ui.dashboardLayer";
-       @Deprecated
-       private String defaultThemeId = "org.argeo.app.theme.default";
-
-       // TODO use QName as key for byType
-       private Map<String, RankedObject<SwtUiProvider>> uiProvidersByPid = Collections.synchronizedMap(new HashMap<>());
-       private Map<String, RankedObject<SwtUiProvider>> uiProvidersByType = Collections.synchronizedMap(new HashMap<>());
-       private Map<String, RankedObject<SwtAppLayer>> layersByPid = Collections.synchronizedSortedMap(new TreeMap<>());
-       private Map<String, RankedObject<SwtAppLayer>> layersByType = Collections.synchronizedSortedMap(new TreeMap<>());
-
-//     private CmsUserManager cmsUserManager;
-
-       // TODO make more optimal or via CmsSession/CmsView
-       private Map<String, SwtAppUi> managedUis = new HashMap<>();
-
-       // ACR
-       private ContentRepository contentRepository;
-       private AppUserState appUserState;
-       // JCR
-//     private Repository repository;
-
-       public void init(Map<String, Object> properties) {
-               for (SuiteUxEvent event : SuiteUxEvent.values()) {
-                       getCmsContext().getCmsEventBus().addEventSubscriber(event.topic(), this);
-               }
-
-               if (log.isDebugEnabled())
-                       log.info("Argeo Suite App started");
-
-               if (properties.containsKey(DEFAULT_UI_NAME_PROPERTY))
-                       defaultUiName = LangUtils.get(properties, DEFAULT_UI_NAME_PROPERTY);
-               if (properties.containsKey(DEFAULT_THEME_ID_PROPERTY))
-                       defaultThemeId = LangUtils.get(properties, DEFAULT_THEME_ID_PROPERTY);
-               if (properties.containsKey(DEFAULT_LAYER_PROPERTY))
-                       defaultLayerPid = LangUtils.get(properties, DEFAULT_LAYER_PROPERTY);
-               publicBasePath = LangUtils.get(properties, PUBLIC_BASE_PATH_PROPERTY);
-
-               if (properties.containsKey(Constants.SERVICE_PID)) {
-                       String servicePid = properties.get(Constants.SERVICE_PID).toString();
-                       if (servicePid.endsWith(".app")) {
-                               pidPrefix = servicePid.substring(0, servicePid.length() - "app".length());
-                       }
-               }
-
-               if (pidPrefix == null)
-                       throw new IllegalArgumentException("PID prefix must be set.");
-
-               headerPid = pidPrefix + "header";
-               footerPid = pidPrefix + "footer";
-               leadPanePid = pidPrefix + "leadPane";
-               adminLeadPanePid = pidPrefix + "adminLeadPane";
-               loginScreenPid = pidPrefix + "loginScreen";
-       }
-
-       public void destroy(Map<String, Object> properties) {
-               for (SwtAppUi ui : managedUis.values())
-                       if (!ui.isDisposed()) {
-                               ui.getDisplay().syncExec(() -> ui.dispose());
-                       }
-               if (log.isDebugEnabled())
-                       log.info("Argeo Suite App stopped");
-
-       }
-
-       @Override
-       public Set<String> getUiNames() {
-               HashSet<String> uiNames = new HashSet<>();
-               uiNames.add(defaultUiName);
-               uiNames.add(adminUiName);
-               return uiNames;
-       }
-
-       @Override
-       public CmsUi initUi(Object parent) {
-               Composite uiParent = (Composite) parent;
-               String uiName = uiParent.getData(UI_NAME_PROPERTY) != null ? uiParent.getData(UI_NAME_PROPERTY).toString()
-                               : null;
-               CmsView cmsView = CmsSwtUtils.getCmsView(uiParent);
-               if (cmsView == null)
-                       throw new IllegalStateException("No CMS view is registered.");
-               CmsTheme theme = getTheme(uiName);
-               if (theme != null)
-                       CmsSwtUtils.registerCmsTheme(uiParent.getShell(), theme);
-               SwtAppUi argeoSuiteUi = new SwtAppUi(uiParent, SWT.INHERIT_DEFAULT);
-               String uid = cmsView.getUid();
-               managedUis.put(uid, argeoSuiteUi);
-               argeoSuiteUi.addDisposeListener((e) -> {
-                       managedUis.remove(uid);
-                       if (log.isDebugEnabled())
-                               log.debug("Suite UI " + uid + " has been disposed.");
-               });
-               return argeoSuiteUi;
-       }
-
-       @Override
-       public String getThemeId(String uiName) {
-               String themeId = System.getProperty("org.argeo.app.theme.default");
-               if (themeId != null)
-                       return themeId;
-               return defaultThemeId;
-       }
-
-       @Override
-       public void refreshUi(CmsUi cmsUi, String state) {
-               try {
-                       Content context = null;
-                       SwtAppUi ui = (SwtAppUi) cmsUi;
-
-                       String uiName = Objects.toString(ui.getParent().getData(UI_NAME_PROPERTY), null);
-                       if (uiName == null)
-                               throw new IllegalStateException("UI name should not be null");
-                       CmsView cmsView = CmsSwtUtils.getCmsView(ui);
-
-                       ProvidedSession contentSession = (ProvidedSession) CmsUxUtils.getContentSession(contentRepository, cmsView);
-
-                       SwtUiProvider headerUiProvider = findUiProvider(headerPid);
-                       SwtUiProvider footerUiProvider = findUiProvider(footerPid);
-                       SwtUiProvider leadPaneUiProvider;
-                       if (adminUiName.equals(uiName)) {
-                               leadPaneUiProvider = findUiProvider(adminLeadPanePid);
-                       } else {
-                               leadPaneUiProvider = findUiProvider(leadPanePid);
-                       }
-
-                       Localized appTitle = null;
-                       if (headerUiProvider instanceof DefaultHeader) {
-                               appTitle = ((DefaultHeader) headerUiProvider).getTitle();
-                       }
-                       ui.setTitle(appTitle);
-
-                       if (cmsView.isAnonymous() && publicBasePath == null) {// internal app, must login
-                               ui.logout();
-                               ui.setLoginScreen(true);
-                               if (headerUiProvider != null)
-                                       refreshPart(headerUiProvider, ui.getHeader(), context);
-                               ui.refreshBelowHeader(false);
-                               refreshPart(findUiProvider(loginScreenPid), ui.getBelowHeader(), context);
-                               if (footerUiProvider != null)
-                                       refreshPart(footerUiProvider, ui.getFooter(), context);
-                               ui.layout(true, true);
-                               setState(ui, LOGIN);
-                       } else {
-                               if (LOGIN.equals(state))
-                                       state = null;
-                               if (ui.isLoginScreen()) {
-                                       ui.setLoginScreen(false);
-                               }
-                               CmsSession cmsSession = cmsView.getCmsSession();
-                               if (ui.getUserDir() == null) {
-                                       // FIXME NPE on CMSSession when logging in from anonymous
-                                       if (cmsSession == null || cmsView.isAnonymous()) {
-                                               assert publicBasePath != null;
-                                               Content userDir = contentSession
-                                                               .get(ContentUtils.SLASH + CmsConstants.SYS_WORKSPACE + publicBasePath);
-                                               ui.setUserDir(userDir);
-                                       } else {
-                                               Content userDir = appUserState.getOrCreateSessionDir(contentSession, cmsSession);
-                                               ui.setUserDir(userDir);
-//                                             Node userDirNode = jcrContentProvider.doInAdminSession((adminSession) -> {
-//                                                     Node node = SuiteUtils.getOrCreateCmsSessionNode(adminSession, cmsSession);
-//                                                     return node;
-//                                             });
-//                                             Content userDir = contentSession
-//                                                             .get(ContentUtils.SLASH + CmsConstants.SYS_WORKSPACE + userDirNode.getPath());
-//                                             ui.setUserDir(userDir);
-                                       }
-                               }
-                               initLocale(cmsSession);
-                               context = stateToNode(ui, state);
-                               if (context == null)
-                                       context = ui.getUserDir();
-
-                               if (headerUiProvider != null)
-                                       refreshPart(headerUiProvider, ui.getHeader(), context);
-                               ui.refreshBelowHeader(true);
-                               for (String key : layersByPid.keySet()) {
-                                       SwtAppLayer layer = layersByPid.get(key).get();
-                                       ui.addLayer(key, layer);
-                               }
-
-                               if (leadPaneUiProvider != null)
-                                       refreshPart(leadPaneUiProvider, ui.getLeadPane(), context);
-                               if (footerUiProvider != null)
-                                       refreshPart(footerUiProvider, ui.getFooter(), context);
-                               ui.layout(true, true);
-                               setState(ui, state != null ? state : defaultLayerPid);
-                       }
-               } catch (Exception e) {
-                       CmsFeedback.error("Unexpected exception", e);
-               }
-       }
-
-       private void initLocale(CmsSession cmsSession) {
-               if (cmsSession == null)
-                       return;
-               Locale locale = cmsSession.getLocale();
-               UiContext.setLocale(locale);
-               LocaleUtils.setThreadLocale(locale);
-
-       }
-
-       private void refreshPart(SwtUiProvider uiProvider, Composite part, Content context) {
-               CmsSwtUtils.clear(part);
-               uiProvider.createUiPart(part, context);
-       }
-
-       private SwtUiProvider findUiProvider(String pid) {
-               if (!uiProvidersByPid.containsKey(pid))
-                       return null;
-               return uiProvidersByPid.get(pid).get();
-       }
-
-       private SwtAppLayer findLayer(String pid) {
-               if (!layersByPid.containsKey(pid))
-                       return null;
-               return layersByPid.get(pid).get();
-       }
-
-       private <T> T findByType(Map<String, RankedObject<T>> byType, Content content) {
-               if (content == null)
-                       throw new IllegalArgumentException("A node should be provided");
-
-//             boolean checkJcr = false;
-//             if (checkJcr && content instanceof JcrContent) {
-//                     Node context = ((JcrContent) content).getJcrNode();
-//                     try {
-//                             // mixins
-//                             Set<String> types = new TreeSet<>();
-//                             for (NodeType mixinType : context.getMixinNodeTypes()) {
-//                                     String mixinTypeName = mixinType.getName();
-//                                     if (byType.containsKey(mixinTypeName)) {
-//                                             types.add(mixinTypeName);
-//                                     }
-//                                     for (NodeType superType : mixinType.getDeclaredSupertypes()) {
-//                                             if (byType.containsKey(superType.getName())) {
-//                                                     types.add(superType.getName());
-//                                             }
-//                                     }
-//                             }
-//                             // primary node type
-//                             NodeType primaryType = context.getPrimaryNodeType();
-//                             String primaryTypeName = primaryType.getName();
-//                             if (byType.containsKey(primaryTypeName)) {
-//                                     types.add(primaryTypeName);
-//                             }
-//                             for (NodeType superType : primaryType.getDeclaredSupertypes()) {
-//                                     if (byType.containsKey(superType.getName())) {
-//                                             types.add(superType.getName());
-//                                     }
-//                             }
-//                             // entity type
-//                             if (context.isNodeType(EntityType.entity.get())) {
-//                                     if (context.hasProperty(EntityNames.ENTITY_TYPE)) {
-//                                             String entityTypeName = context.getProperty(EntityNames.ENTITY_TYPE).getString();
-//                                             if (byType.containsKey(entityTypeName)) {
-//                                                     types.add(entityTypeName);
-//                                             }
-//                                     }
-//                             }
-//
-//                             if (CmsJcrUtils.isUserHome(context) && byType.containsKey("nt:folder")) {// home node
-//                                     types.add("nt:folder");
-//                             }
-//
-//                             if (types.size() == 0)
-//                                     throw new IllegalArgumentException(
-//                                                     "No type found for " + context + " (" + listTypes(context) + ")");
-//                             String type = types.iterator().next();
-//                             if (!byType.containsKey(type))
-//                                     throw new IllegalArgumentException("No component found for " + context + " with type " + type);
-//                             return byType.get(type).get();
-//                     } catch (RepositoryException e) {
-//                             throw new IllegalStateException(e);
-//                     }
-//
-//             } else {
-               Set<String> types = new TreeSet<>();
-               if (content.hasContentClass(EntityType.entity.qName())) {
-                       String type = content.attr(EntityName.type.qName());
-                       if (type != null && byType.containsKey(type))
-                               types.add(type);
-               }
-
-               List<QName> objectClasses = content.getContentClasses();
-               for (QName cc : objectClasses) {
-                       String type = cc.getPrefix() + ":" + cc.getLocalPart();
-                       if (byType.containsKey(type))
-                               types.add(type);
-               }
-               if (types.size() == 0) {
-                       throw new IllegalArgumentException("No type found for " + content + " (" + objectClasses + ")");
-               }
-               String type = types.iterator().next();
-               if (!byType.containsKey(type))
-                       throw new IllegalArgumentException("No component found for " + content + " with type " + type);
-               return byType.get(type).get();
-//             }
-       }
-
-//     private static String listTypes(Node context) {
-//             try {
-//                     StringBuilder sb = new StringBuilder();
-//                     sb.append(context.getPrimaryNodeType().getName());
-//                     for (NodeType superType : context.getPrimaryNodeType().getDeclaredSupertypes()) {
-//                             sb.append(' ');
-//                             sb.append(superType.getName());
-//                     }
-//
-//                     for (NodeType nodeType : context.getMixinNodeTypes()) {
-//                             sb.append(' ');
-//                             sb.append(nodeType.getName());
-//                             if (nodeType.getName().equals(EntityType.local.get()))
-//                                     sb.append('/').append(context.getProperty(EntityNames.ENTITY_TYPE).getString());
-//                             for (NodeType superType : nodeType.getDeclaredSupertypes()) {
-//                                     sb.append(' ');
-//                                     sb.append(superType.getName());
-//                             }
-//                     }
-//                     return sb.toString();
-//             } catch (RepositoryException e) {
-//                     throw new JcrException(e);
-//             }
-//     }
-
-       @Override
-       public void setState(CmsUi cmsUi, String state) {
-               AppUi ui = (AppUi) cmsUi;
-               if (state == null)
-                       return;
-               if (!state.startsWith("/")) {
-//                     if (cmsUi instanceof SwtAppUi) {
-//                             SwtAppUi ui = (SwtAppUi) cmsUi;
-                       if (LOGIN.equals(state)) {
-                               String appTitle = "";
-                               if (ui.getTitle() != null)
-                                       appTitle = ui.getTitle().lead();
-                               ui.getCmsView().stateChanged(state, appTitle);
-                               return;
-                       }
-                       Map<String, Object> properties = new HashMap<>();
-                       String layerId = HOME_STATE.equals(state) ? defaultLayerPid : state;
-                       properties.put(SuiteUxEvent.LAYER, layerId);
-                       properties.put(SuiteUxEvent.CONTENT_PATH, HOME_STATE);
-                       ui.getCmsView().sendEvent(SuiteUxEvent.switchLayer.topic(), properties);
-//                     }
-                       return;
-               }
-//             SwtAppUi suiteUi = (SwtAppUi) cmsUi;
-               if (ui.isLoginScreen()) {
-                       return;
-               }
-
-               Content node = stateToNode(ui, state);
-               if (node == null) {
-                       ui.getCmsView().navigateTo(HOME_STATE);
-               } else {
-                       ui.getCmsView().sendEvent(SuiteUxEvent.switchLayer.topic(), SuiteUxEvent.eventProperties(node));
-                       ui.getCmsView().sendEvent(SuiteUxEvent.refreshPart.topic(), SuiteUxEvent.eventProperties(node));
-               }
-       }
-
-       // TODO move it to an internal package?
-       public static String nodeToState(Content node) {
-               return node.getPath();
-       }
-
-       private Content stateToNode(CmsUi suiteUi, String state) {
-               if (suiteUi == null)
-                       return null;
-               if (state == null || !state.startsWith("/"))
-                       return null;
-
-               String path = state;
-
-               ProvidedSession contentSession = (ProvidedSession) CmsUxUtils.getContentSession(contentRepository,
-                               suiteUi.getCmsView());
-               return contentSession.get(path);
-       }
-
-       /*
-        * Events management
-        */
-
-       @Override
-       public void onEvent(String topic, Map<String, Object> event) {
-
-               // Specific UI related events
-               SwtAppUi ui = getRelatedUi(event);
-               if (ui == null)
-                       return;
-               ui.getCmsView().runAs(() -> {
-                       try {
-                               String appTitle = "";
-                               if (ui.getTitle() != null)
-                                       appTitle = ui.getTitle().lead() + " - ";
-
-                               if (SuiteUiUtils.isTopic(topic, SuiteUxEvent.refreshPart)) {
-                                       Content node = getContentFromEvent(ui, event);
-                                       if (node == null)
-                                               return;
-                                       SwtUiProvider uiProvider = findByType(uiProvidersByType, node);
-                                       SwtAppLayer layer = findByType(layersByType, node);
-                                       ui.switchToLayer(layer, node);
-                                       layer.view(uiProvider, ui.getCurrentWorkArea(), node);
-                                       ui.getCmsView().stateChanged(nodeToState(node), appTitle + CmsUxUtils.getTitle(node));
-                               } else if (SuiteUiUtils.isTopic(topic, SuiteUxEvent.openNewPart)) {
-                                       Content node = getContentFromEvent(ui, event);
-                                       if (node == null)
-                                               return;
-                                       SwtUiProvider uiProvider = findByType(uiProvidersByType, node);
-                                       SwtAppLayer layer = findByType(layersByType, node);
-                                       ui.switchToLayer(layer, node);
-                                       layer.open(uiProvider, ui.getCurrentWorkArea(), node);
-                                       ui.getCmsView().stateChanged(nodeToState(node), appTitle + CmsUxUtils.getTitle(node));
-                               } else if (SuiteUiUtils.isTopic(topic, SuiteUxEvent.switchLayer)) {
-                                       String layerId = get(event, SuiteUxEvent.LAYER);
-                                       if (layerId != null) {
-                                               SwtAppLayer suiteLayer = findLayer(layerId);
-                                               if (suiteLayer == null)
-                                                       throw new IllegalArgumentException("No layer '" + layerId + "' available.");
-                                               Localized layerTitle = suiteLayer.getTitle();
-                                               // FIXME make sure we don't rebuild the work area twice
-                                               Composite workArea = ui.switchToLayer(layerId, ui.getUserDir());
-                                               String title = null;
-                                               if (layerTitle != null)
-                                                       title = layerTitle.lead();
-                                               Content nodeFromState = getContentFromEvent(ui, event);
-                                               if (nodeFromState != null && nodeFromState.getPath().equals(ui.getUserDir().getPath())) {
-                                                       // default layer view is forced
-                                                       String state = defaultLayerPid.equals(layerId) ? "~" : layerId;
-                                                       ui.getCmsView().stateChanged(state, appTitle + title);
-                                                       suiteLayer.view(null, workArea, nodeFromState);
-                                               } else {
-                                                       Content layerCurrentContext = suiteLayer.getCurrentContext(workArea);
-                                                       if (layerCurrentContext != null && !layerCurrentContext.equals(ui.getUserDir())) {
-                                                               // layer was already showing a context so we set the state to it
-                                                               ui.getCmsView().stateChanged(nodeToState(layerCurrentContext),
-                                                                               appTitle + CmsUxUtils.getTitle(layerCurrentContext));
-                                                       } else {
-                                                               // no context was shown
-                                                               ui.getCmsView().stateChanged(layerId, appTitle + title);
-                                                       }
-                                               }
-                                       } else {
-                                               Content node = getContentFromEvent(ui, event);
-                                               if (node != null) {
-                                                       SwtAppLayer layer = findByType(layersByType, node);
-                                                       ui.switchToLayer(layer, node);
-                                               }
-                                       }
-                               }
-                       } catch (Exception e) {
-                               CmsFeedback.error("Cannot handle event " + topic + " " + event, e);
-//                             log.error("Cannot handle event " + event, e);
-                       }
-               });
-       }
-
-       protected Content getContentFromEvent(SwtAppUi ui, Map<String, Object> event) {
-               ProvidedSession contentSession = (ProvidedSession) CmsUxUtils.getContentSession(contentRepository,
-                               ui.getCmsView());
-
-               String path = get(event, SuiteUxEvent.CONTENT_PATH);
-
-               if (path != null && (path.equals(HOME_STATE) || path.equals("")))
-                       return ui.getUserDir();
-               Content node;
-               if (path == null) {
-                       return null;
-//                     // look for a user
-//                     String username = get(event, SuiteUxEvent.USERNAME);
-//                     if (username == null)
-//                             return null;
-//                     User user = cmsUserManager.getUser(username);
-//                     if (user == null)
-//                             return null;
-//                     node = ContentUtils.roleToContent(cmsUserManager, contentSession, user);
-               } else {
-                       node = contentSession.get(path);
-               }
-               return node;
-       }
-
-       private SwtAppUi getRelatedUi(Map<String, Object> eventProperties) {
-               return managedUis.get(get(eventProperties, CMS_VIEW_UID_PROPERTY));
-       }
-
-       public static String get(Map<String, Object> eventProperties, String key) {
-               Object value = eventProperties.get(key);
-               if (value == null)
-                       return null;
-               return value.toString();
-
-       }
-
-       /*
-        * Dependency injection.
-        */
-
-       public void addUiProvider(SwtUiProvider uiProvider, Map<String, Object> properties) {
-               if (properties.containsKey(Constants.SERVICE_PID)) {
-                       String pid = (String) properties.get(Constants.SERVICE_PID);
-                       RankedObject.putIfHigherRank(uiProvidersByPid, pid, uiProvider, properties);
-               }
-               if (properties.containsKey(EntityConstants.TYPE)) {
-                       List<String> types = LangUtils.toStringList(properties.get(EntityConstants.TYPE));
-                       for (String type : types) {
-                               RankedObject.putIfHigherRank(uiProvidersByType, type, uiProvider, properties);
-                       }
-               }
-       }
-
-       public void removeUiProvider(SwtUiProvider uiProvider, Map<String, Object> properties) {
-               if (properties.containsKey(Constants.SERVICE_PID)) {
-                       String pid = (String) properties.get(Constants.SERVICE_PID);
-                       if (uiProvidersByPid.containsKey(pid)) {
-                               if (uiProvidersByPid.get(pid).equals(new RankedObject<SwtUiProvider>(uiProvider, properties))) {
-                                       uiProvidersByPid.remove(pid);
-                               }
-                       }
-               }
-               if (properties.containsKey(EntityConstants.TYPE)) {
-                       List<String> types = LangUtils.toStringList(properties.get(EntityConstants.TYPE));
-                       for (String type : types) {
-                               if (uiProvidersByType.containsKey(type)) {
-                                       if (uiProvidersByType.get(type).equals(new RankedObject<SwtUiProvider>(uiProvider, properties))) {
-                                               uiProvidersByType.remove(type);
-                                       }
-                               }
-                       }
-               }
-       }
-
-       public void addLayer(SwtAppLayer layer, Map<String, Object> properties) {
-               if (properties.containsKey(Constants.SERVICE_PID)) {
-                       String pid = (String) properties.get(Constants.SERVICE_PID);
-                       RankedObject.putIfHigherRank(layersByPid, pid, layer, properties);
-               }
-               if (properties.containsKey(EntityConstants.TYPE)) {
-                       List<String> types = LangUtils.toStringList(properties.get(EntityConstants.TYPE));
-                       for (String type : types)
-                               RankedObject.putIfHigherRank(layersByType, type, layer, properties);
-               }
-       }
-
-       public void removeLayer(SwtAppLayer layer, Map<String, Object> properties) {
-               if (properties.containsKey(Constants.SERVICE_PID)) {
-                       String pid = (String) properties.get(Constants.SERVICE_PID);
-                       if (layersByPid.containsKey(pid)) {
-                               if (layersByPid.get(pid).equals(new RankedObject<SwtAppLayer>(layer, properties))) {
-                                       layersByPid.remove(pid);
-                               }
-                       }
-               }
-               if (properties.containsKey(EntityConstants.TYPE)) {
-                       List<String> types = LangUtils.toStringList(properties.get(EntityConstants.TYPE));
-                       for (String type : types) {
-                               if (layersByType.containsKey(type)) {
-                                       if (layersByType.get(type).equals(new RankedObject<SwtAppLayer>(layer, properties))) {
-                                               layersByType.remove(type);
-                                       }
-                               }
-                       }
-               }
-       }
-
-//     public void setCmsUserManager(CmsUserManager cmsUserManager) {
-//             this.cmsUserManager = cmsUserManager;
-//     }
-
-//     protected ContentRepository getContentRepository() {
-//             return contentRepository;
-//     }
-
-       public void setContentRepository(ContentRepository contentRepository) {
-               this.contentRepository = contentRepository;
-       }
-
-       public void setAppUserState(AppUserState appUserState) {
-               this.appUserState = appUserState;
-       }
-
-}
index c98847a0dd8b143f93c89fa63778d1c715fb6fd5..5568201651cef091af430c1ed66be5559c574302 100644 (file)
@@ -7,7 +7,6 @@ import java.nio.file.Files;
 import java.nio.file.Paths;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Objects;
 
 import javax.jcr.Node;
 import javax.jcr.RepositoryException;
@@ -15,18 +14,15 @@ import javax.jcr.Session;
 
 import org.apache.commons.io.IOUtils;
 import org.argeo.api.acr.Content;
-import org.argeo.api.cms.CmsEvent;
 import org.argeo.api.cms.ux.CmsEditable;
-import org.argeo.api.cms.ux.CmsIcon;
 import org.argeo.api.cms.ux.CmsStyle;
 import org.argeo.app.api.EntityNames;
 import org.argeo.app.api.EntityType;
+import org.argeo.app.swt.ux.SwtArgeoApp;
 import org.argeo.app.ux.SuiteStyle;
 import org.argeo.app.ux.SuiteUxEvent;
-import org.argeo.cms.LocaleUtils;
 import org.argeo.cms.Localized;
 import org.argeo.cms.jcr.acr.JcrContent;
-import org.argeo.cms.swt.CmsSwtTheme;
 import org.argeo.cms.swt.CmsSwtUtils;
 import org.argeo.cms.swt.dialogs.LightweightDialog;
 import org.argeo.cms.ui.util.CmsLink;
@@ -367,11 +363,11 @@ public class SuiteUiUtils {
        }
 
        public static String toLink(Content node) {
-               return node != null ? "#" + CmsSwtUtils.cleanPathForUrl(SuiteApp.nodeToState(node)) : null;
+               return node != null ? "#" + CmsSwtUtils.cleanPathForUrl(SwtArgeoApp.nodeToState(node)) : null;
        }
 
        public static String toLink(Node node) {
-               return node != null ? "#" + CmsSwtUtils.cleanPathForUrl(SuiteApp.nodeToState(JcrContent.nodeToContent(node)))
+               return node != null ? "#" + CmsSwtUtils.cleanPathForUrl(SwtArgeoApp.nodeToState(JcrContent.nodeToContent(node)))
                                : null;
        }
 
@@ -401,37 +397,6 @@ public class SuiteUiUtils {
                return lbl;
        }
 
-//     public static boolean isCoworker(CmsView cmsView) {
-//             boolean coworker = cmsView.doAs(() -> CurrentUser.isInRole(SuiteRole.coworker.dn()));
-//             return coworker;
-//     }
-
-       public static boolean isTopic(String topic, CmsEvent cmsEvent) {
-               Objects.requireNonNull(topic);
-               return topic.equals(cmsEvent.topic());
-       }
-
-       public static Button createLayerButton(Composite parent, String layer, Localized msg, CmsIcon icon,
-                       ClassLoader l10nClassLoader) {
-               CmsSwtTheme theme = CmsSwtUtils.getCmsTheme(parent);
-               Button button = new Button(parent, SWT.PUSH);
-               CmsSwtUtils.style(button, SuiteStyle.leadPane);
-               if (icon != null)
-                       button.setImage(theme.getBigIcon(icon));
-               button.setLayoutData(new GridData(SWT.CENTER, SWT.BOTTOM, true, false));
-               // button.setToolTipText(msg.lead());
-               if (msg != null) {
-                       Label lbl = new Label(parent, SWT.CENTER);
-                       CmsSwtUtils.style(lbl, SuiteStyle.leadPane);
-                       String txt = LocaleUtils.lead(msg, l10nClassLoader);
-//                     String txt = msg.lead();
-                       lbl.setText(txt);
-                       lbl.setLayoutData(new GridData(SWT.CENTER, SWT.TOP, true, false));
-               }
-               CmsSwtUtils.sendEventOnSelect(button, SuiteUxEvent.switchLayer.topic(), SuiteUxEvent.LAYER, layer);
-               return button;
-       }
-
        @Deprecated
        public static Map<String, Object> eventProperties(Node node) {
                Map<String, Object> properties = new HashMap<>();
index ad8a9139ef6852250daa28d93bf0a446828c03d8..9057a6bee83e7ddb70eb00f0da29c1546741ed00 100644 (file)
@@ -1,7 +1,7 @@
 package org.argeo.app.ui.publish;
 
-import static org.argeo.app.ui.SuiteApp.DEFAULT_THEME_ID_PROPERTY;
-import static org.argeo.app.ui.SuiteApp.DEFAULT_UI_NAME_PROPERTY;
+import static org.argeo.app.swt.ux.SwtArgeoApp.DEFAULT_THEME_ID_PROPERTY;
+import static org.argeo.app.swt.ux.SwtArgeoApp.DEFAULT_UI_NAME_PROPERTY;
 
 import java.util.HashSet;
 import java.util.Map;
@@ -14,7 +14,7 @@ import javax.jcr.Session;
 import org.argeo.api.cms.CmsApp;
 import org.argeo.api.cms.CmsLog;
 import org.argeo.api.cms.ux.CmsUi;
-import org.argeo.app.ui.SuiteApp;
+import org.argeo.app.swt.ux.SwtArgeoApp;
 import org.argeo.cms.AbstractCmsApp;
 import org.argeo.cms.ui.CmsUiProvider;
 import org.argeo.cms.util.LangUtils;
@@ -47,7 +47,7 @@ public class PublishingApp extends AbstractCmsApp {
                        defaultUiName = LangUtils.get(properties, DEFAULT_UI_NAME_PROPERTY);
                if (properties.containsKey(DEFAULT_THEME_ID_PROPERTY))
                        defaultThemeId = LangUtils.get(properties, DEFAULT_THEME_ID_PROPERTY);
-               publicBasePath = LangUtils.get(properties, SuiteApp.PUBLIC_BASE_PATH_PROPERTY);
+               publicBasePath = LangUtils.get(properties, SwtArgeoApp.PUBLIC_BASE_PATH_PROPERTY);
                pid = properties.get(Constants.SERVICE_PID);
 
                if (log.isDebugEnabled())