From: Mathieu Baudier Date: Tue, 13 Jun 2023 06:33:38 +0000 (+0200) Subject: Refactor suite UX X-Git-Tag: v2.3.15~22 X-Git-Url: https://git.argeo.org/?p=gpl%2Fargeo-suite.git;a=commitdiff_plain;h=618968cf9d259ccded45a9455a26c516dbfe828f Refactor suite UX --- diff --git a/org.argeo.app.core/bnd.bnd b/org.argeo.app.core/bnd.bnd index e6be573..dce5772 100644 --- a/org.argeo.app.core/bnd.bnd +++ b/org.argeo.app.core/bnd.bnd @@ -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 index 0000000..303952d --- /dev/null +++ b/swt/org.argeo.app.swt/src/org/argeo/app/swt/ux/DefaultFooter.java @@ -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 properties) { + } + + public void destroy(BundleContext bundleContext, Map 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 index 0000000..36b37bf --- /dev/null +++ b/swt/org.argeo.app.swt/src/org/argeo/app/swt/ux/DefaultHeader.java @@ -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 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 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 index 0000000..2b17dee --- /dev/null +++ b/swt/org.argeo.app.swt/src/org/argeo/app/swt/ux/DefaultLeadPane.java @@ -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> layers = Collections.synchronizedSortedMap(new TreeMap<>()); + private List defaultLayers; + private List 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 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 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 intersection = new HashSet(layerRoles); +// intersection.retainAll(userRoles); +// if (intersection.isEmpty()) +// continue layers;// skip unauthorized layer + } + RankedObject 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 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 properties) { + + } + + public void addLayer(SwtAppLayer layer, Map 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 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(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 index 0000000..326ed4f --- /dev/null +++ b/swt/org.argeo.app.swt/src/org/argeo/app/swt/ux/DefaultLoginScreen.java @@ -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 index 0000000..0b6c34d --- /dev/null +++ b/swt/org.argeo.app.swt/src/org/argeo/app/swt/ux/SwtArgeoApp.java @@ -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> uiProvidersByPid = Collections.synchronizedMap(new HashMap<>()); + private Map> uiProvidersByType = Collections.synchronizedMap(new HashMap<>()); + private Map> layersByPid = Collections.synchronizedSortedMap(new TreeMap<>()); + private Map> layersByType = Collections.synchronizedSortedMap(new TreeMap<>()); + +// private CmsUserManager cmsUserManager; + + // TODO make more optimal or via CmsSession/CmsView + private Map managedUis = new HashMap<>(); + + // ACR + private ContentRepository contentRepository; + private AppUserState appUserState; + // JCR +// private Repository repository; + + public void init(Map 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 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 getUiNames() { + HashSet 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 findByType(Map> 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 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 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 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 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 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 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 eventProperties) { + return managedUis.get(get(eventProperties, CMS_VIEW_UID_PROPERTY)); + } + + public static String get(Map eventProperties, String key) { + Object value = eventProperties.get(key); + if (value == null) + return null; + return value.toString(); + + } + + /* + * Dependency injection. + */ + + public void addUiProvider(SwtUiProvider uiProvider, Map 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 types = LangUtils.toStringList(properties.get(EntityConstants.TYPE)); + for (String type : types) { + RankedObject.putIfHigherRank(uiProvidersByType, type, uiProvider, properties); + } + } + } + + public void removeUiProvider(SwtUiProvider uiProvider, Map 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(uiProvider, properties))) { + uiProvidersByPid.remove(pid); + } + } + } + if (properties.containsKey(EntityConstants.TYPE)) { + List types = LangUtils.toStringList(properties.get(EntityConstants.TYPE)); + for (String type : types) { + if (uiProvidersByType.containsKey(type)) { + if (uiProvidersByType.get(type).equals(new RankedObject(uiProvider, properties))) { + uiProvidersByType.remove(type); + } + } + } + } + } + + public void addLayer(SwtAppLayer layer, Map 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 types = LangUtils.toStringList(properties.get(EntityConstants.TYPE)); + for (String type : types) + RankedObject.putIfHigherRank(layersByType, type, layer, properties); + } + } + + public void removeLayer(SwtAppLayer layer, Map 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(layer, properties))) { + layersByPid.remove(pid); + } + } + } + if (properties.containsKey(EntityConstants.TYPE)) { + List types = LangUtils.toStringList(properties.get(EntityConstants.TYPE)); + for (String type : types) { + if (layersByType.containsKey(type)) { + if (layersByType.get(type).equals(new RankedObject(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; + } + +} diff --git a/swt/org.argeo.app.ui/OSGI-INF/adminLeadPane.xml b/swt/org.argeo.app.ui/OSGI-INF/adminLeadPane.xml index 306ad94..db87157 100644 --- a/swt/org.argeo.app.ui/OSGI-INF/adminLeadPane.xml +++ b/swt/org.argeo.app.ui/OSGI-INF/adminLeadPane.xml @@ -1,6 +1,6 @@ - + diff --git a/swt/org.argeo.app.ui/OSGI-INF/cmsApp.xml b/swt/org.argeo.app.ui/OSGI-INF/cmsApp.xml index f170f97..7a516fc 100644 --- a/swt/org.argeo.app.ui/OSGI-INF/cmsApp.xml +++ b/swt/org.argeo.app.ui/OSGI-INF/cmsApp.xml @@ -1,6 +1,6 @@ - + diff --git a/swt/org.argeo.app.ui/OSGI-INF/footer.xml b/swt/org.argeo.app.ui/OSGI-INF/footer.xml index 8d20231..ded75df 100644 --- a/swt/org.argeo.app.ui/OSGI-INF/footer.xml +++ b/swt/org.argeo.app.ui/OSGI-INF/footer.xml @@ -1,6 +1,6 @@ - + diff --git a/swt/org.argeo.app.ui/OSGI-INF/header.xml b/swt/org.argeo.app.ui/OSGI-INF/header.xml index cb792e5..e6713ed 100644 --- a/swt/org.argeo.app.ui/OSGI-INF/header.xml +++ b/swt/org.argeo.app.ui/OSGI-INF/header.xml @@ -1,6 +1,6 @@ - + diff --git a/swt/org.argeo.app.ui/OSGI-INF/leadPane.xml b/swt/org.argeo.app.ui/OSGI-INF/leadPane.xml index a620438..57a7468 100644 --- a/swt/org.argeo.app.ui/OSGI-INF/leadPane.xml +++ b/swt/org.argeo.app.ui/OSGI-INF/leadPane.xml @@ -1,6 +1,6 @@ - + diff --git a/swt/org.argeo.app.ui/OSGI-INF/loginScreen.xml b/swt/org.argeo.app.ui/OSGI-INF/loginScreen.xml index eab7592..6b3d49d 100644 --- a/swt/org.argeo.app.ui/OSGI-INF/loginScreen.xml +++ b/swt/org.argeo.app.ui/OSGI-INF/loginScreen.xml @@ -1,6 +1,6 @@ - + diff --git a/swt/org.argeo.app.ui/bnd.bnd b/swt/org.argeo.app.ui/bnd.bnd index 4a74f2d..fb2e3f7 100644 --- a/swt/org.argeo.app.ui/bnd.bnd +++ b/swt/org.argeo.app.ui/bnd.bnd @@ -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 index 5e54368..0000000 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultFooter.java +++ /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 properties) { - } - - public void destroy(BundleContext bundleContext, Map 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 index ede28d1..0000000 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultHeader.java +++ /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 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 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 index 9a2faf5..0000000 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultLeadPane.java +++ /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> layers = Collections.synchronizedSortedMap(new TreeMap<>()); - private List defaultLayers; - private List 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 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 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 intersection = new HashSet(layerRoles); -// intersection.retainAll(userRoles); -// if (intersection.isEmpty()) -// continue layers;// skip unauthorized layer - } - RankedObject 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 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 properties) { - - } - - public void addLayer(SwtAppLayer layer, Map 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 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(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 index 1cb1f95..0000000 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultLoginScreen.java +++ /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 index da32c1a..0000000 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteApp.java +++ /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> uiProvidersByPid = Collections.synchronizedMap(new HashMap<>()); - private Map> uiProvidersByType = Collections.synchronizedMap(new HashMap<>()); - private Map> layersByPid = Collections.synchronizedSortedMap(new TreeMap<>()); - private Map> layersByType = Collections.synchronizedSortedMap(new TreeMap<>()); - -// private CmsUserManager cmsUserManager; - - // TODO make more optimal or via CmsSession/CmsView - private Map managedUis = new HashMap<>(); - - // ACR - private ContentRepository contentRepository; - private AppUserState appUserState; - // JCR -// private Repository repository; - - public void init(Map 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 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 getUiNames() { - HashSet 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 findByType(Map> 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 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 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 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 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 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 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 eventProperties) { - return managedUis.get(get(eventProperties, CMS_VIEW_UID_PROPERTY)); - } - - public static String get(Map eventProperties, String key) { - Object value = eventProperties.get(key); - if (value == null) - return null; - return value.toString(); - - } - - /* - * Dependency injection. - */ - - public void addUiProvider(SwtUiProvider uiProvider, Map 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 types = LangUtils.toStringList(properties.get(EntityConstants.TYPE)); - for (String type : types) { - RankedObject.putIfHigherRank(uiProvidersByType, type, uiProvider, properties); - } - } - } - - public void removeUiProvider(SwtUiProvider uiProvider, Map 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(uiProvider, properties))) { - uiProvidersByPid.remove(pid); - } - } - } - if (properties.containsKey(EntityConstants.TYPE)) { - List types = LangUtils.toStringList(properties.get(EntityConstants.TYPE)); - for (String type : types) { - if (uiProvidersByType.containsKey(type)) { - if (uiProvidersByType.get(type).equals(new RankedObject(uiProvider, properties))) { - uiProvidersByType.remove(type); - } - } - } - } - } - - public void addLayer(SwtAppLayer layer, Map 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 types = LangUtils.toStringList(properties.get(EntityConstants.TYPE)); - for (String type : types) - RankedObject.putIfHigherRank(layersByType, type, layer, properties); - } - } - - public void removeLayer(SwtAppLayer layer, Map 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(layer, properties))) { - layersByPid.remove(pid); - } - } - } - if (properties.containsKey(EntityConstants.TYPE)) { - List types = LangUtils.toStringList(properties.get(EntityConstants.TYPE)); - for (String type : types) { - if (layersByType.containsKey(type)) { - if (layersByType.get(type).equals(new RankedObject(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; - } - -} diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteUiUtils.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteUiUtils.java index c98847a..5568201 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteUiUtils.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteUiUtils.java @@ -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 eventProperties(Node node) { Map properties = new HashMap<>(); diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/publish/PublishingApp.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/publish/PublishingApp.java index ad8a913..9057a6b 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/publish/PublishingApp.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/publish/PublishingApp.java @@ -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())