X-Git-Url: https://git.argeo.org/?p=gpl%2Fargeo-suite.git;a=blobdiff_plain;f=org.argeo.suite.ui%2Fsrc%2Forg%2Fargeo%2Fsuite%2Fui%2FSuiteApp.java;h=8eb9ef4ebfa71aa4f80f5749b744afb408f6e508;hp=b4ba8d270f12e70c751a49db3230957454e0597d;hb=3cf66bc01bb8ad4c55139ae01be5a5bdb3759e2c;hpb=8e35075f4e0949d12c8232ba58e7efe18d4f7221 diff --git a/org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteApp.java b/org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteApp.java index b4ba8d2..8eb9ef4 100644 --- a/org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteApp.java +++ b/org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteApp.java @@ -1,6 +1,6 @@ package org.argeo.suite.ui; -import static org.argeo.cms.ui.CmsView.CMS_VIEW_UID_PROPERTY; +import static org.argeo.api.cms.CmsView.CMS_VIEW_UID_PROPERTY; import java.util.Collections; import java.util.HashMap; @@ -8,11 +8,13 @@ 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.jcr.Node; +import javax.jcr.Repository; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.nodetype.NodeType; @@ -21,22 +23,24 @@ import javax.naming.ldap.LdapName; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.argeo.api.NodeUtils; +import org.argeo.api.cms.CmsSession; +import org.argeo.api.cms.CmsTheme; +import org.argeo.api.cms.CmsUi; +import org.argeo.api.cms.CmsView; +import org.argeo.cms.AbstractCmsApp; import org.argeo.cms.CmsUserManager; import org.argeo.cms.LocaleUtils; -import org.argeo.cms.auth.CmsSession; -import org.argeo.cms.ui.AbstractCmsApp; -import org.argeo.cms.ui.CmsTheme; +import org.argeo.cms.Localized; +import org.argeo.cms.jcr.CmsJcrUtils; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.swt.dialogs.CmsFeedback; import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.cms.ui.CmsView; -import org.argeo.cms.ui.dialogs.CmsFeedback; -import org.argeo.cms.ui.util.CmsEvent; -import org.argeo.cms.ui.util.CmsUiUtils; import org.argeo.eclipse.ui.specific.UiContext; import org.argeo.entity.EntityConstants; import org.argeo.entity.EntityNames; import org.argeo.entity.EntityType; import org.argeo.jcr.Jcr; +import org.argeo.jcr.JcrException; import org.argeo.suite.RankedObject; import org.argeo.suite.SuiteUtils; import org.argeo.util.LangUtils; @@ -51,15 +55,27 @@ import org.osgi.service.useradmin.User; public class SuiteApp extends AbstractCmsApp implements EventHandler { private final static Log log = LogFactory.getLog(SuiteApp.class); - public final static String PID_PREFIX = "argeo.suite.ui."; - public final static String HEADER_PID = PID_PREFIX + "header"; - public final static String LEAD_PANE_PID = PID_PREFIX + "leadPane"; - public final static String LOGIN_SCREEN_PID = PID_PREFIX + "loginScreen"; - public final static String DASHBOARD_PID = PID_PREFIX + "dashboard"; - public final static String RECENT_ITEMS_PID = PID_PREFIX + "recentItems"; + 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 final static String DEFAULT_UI_NAME = "app"; - private final static String DEFAULT_THEME_ID = "org.argeo.suite.theme.default"; + private String publicBasePath = null; + + private String pidPrefix; + private String headerPid; + private String footerPid; + private String leadPanePid; + private String adminLeadPanePid; + private String loginScreenPid; + + private String defaultLayerPid = "argeo.suite.ui.dashboardLayer"; + + private String defaultUiName = "app"; + private String adminUiName = "admin"; + private String defaultThemeId = "org.argeo.suite.theme.default"; private Map> uiProvidersByPid = Collections.synchronizedMap(new HashMap<>()); private Map> uiProvidersByType = Collections.synchronizedMap(new HashMap<>()); @@ -71,14 +87,39 @@ public class SuiteApp extends AbstractCmsApp implements EventHandler { // TODO make more optimal or via CmsSession/CmsView private Map managedUis = new HashMap<>(); -// private CmsUiProvider headerPart = null; + private Repository repository; - public void init(Map properties) { + + public void init(Map properties) { 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) { + public void destroy(Map properties) { for (SuiteUi ui : managedUis.values()) if (!ui.isDisposed()) ui.dispose(); @@ -90,20 +131,22 @@ public class SuiteApp extends AbstractCmsApp implements EventHandler { @Override public Set getUiNames() { HashSet uiNames = new HashSet<>(); - uiNames.add(DEFAULT_UI_NAME); + uiNames.add(defaultUiName); + uiNames.add(adminUiName); return uiNames; } @Override - public Composite initUi(Composite parent) { - String uiName = parent.getData(UI_NAME_PROPERTY) != null ? parent.getData(UI_NAME_PROPERTY).toString() : null; - CmsView cmsView = CmsView.getCmsView(parent); + 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) - CmsTheme.registerCmsTheme(parent.getShell(), theme); - SuiteUi argeoSuiteUi = new SuiteUi(parent, SWT.NONE); + CmsSwtUtils.registerCmsTheme(uiParent.getShell(), theme); + SuiteUi argeoSuiteUi = new SuiteUi(uiParent, SWT.INHERIT_DEFAULT); String uid = cmsView.getUid(); managedUis.put(uid, argeoSuiteUi); argeoSuiteUi.addDisposeListener((e) -> { @@ -111,38 +154,67 @@ public class SuiteApp extends AbstractCmsApp implements EventHandler { if (log.isDebugEnabled()) log.debug("Suite UI " + uid + " has been disposed."); }); - refreshUi(argeoSuiteUi, null); return argeoSuiteUi; } @Override public String getThemeId(String uiName) { - // TODO make it configurable - return DEFAULT_THEME_ID; + return defaultThemeId; } @Override - public void refreshUi(Composite parent, String state) { + public void refreshUi(CmsUi cmsUi, String state) { try { Node context = null; - SuiteUi ui = (SuiteUi) parent; - refreshPart(findUiProvider(HEADER_PID), ui.getHeader(), context); - CmsView cmsView = CmsView.getCmsView(parent); - if (cmsView.isAnonymous()) { + SuiteUi ui = (SuiteUi) 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); + CmsUiProvider headerUiProvider = findUiProvider(headerPid); + CmsUiProvider footerUiProvider = findUiProvider(footerPid); + CmsUiProvider 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(); + if (headerUiProvider != null) + refreshPart(headerUiProvider, ui.getHeader(), context); ui.refreshBelowHeader(false); - refreshPart(findUiProvider(LOGIN_SCREEN_PID), ui.getBelowHeader(), context); + 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; CmsSession cmsSession = cmsView.getCmsSession(); if (ui.getUserDir() == null) { - Session adminSession = null; - try { - adminSession = NodeUtils.openDataAdminSession(getRepository(), null); - Node userDir = SuiteUtils.getOrCreateCmsSessionNode(adminSession, cmsSession); - ui.initSessions(getRepository(), userDir.getPath()); - } finally { - Jcr.logout(adminSession); + // FIXME NPE on CMSSession when logging in from anonymous + if (cmsSession == null || cmsView.isAnonymous()) { + assert publicBasePath != null; + ui.initSessions(getRepository(), publicBasePath); + } else { + Session adminSession = null; + try { + adminSession = CmsJcrUtils.openDataAdminSession(getRepository(), null); + Node userDir = SuiteUtils.getOrCreateCmsSessionNode(adminSession, cmsSession); + ui.initSessions(getRepository(), userDir.getPath()); + } finally { + Jcr.logout(adminSession); + } } } initLocale(cmsSession); @@ -150,14 +222,20 @@ public class SuiteApp extends AbstractCmsApp implements EventHandler { if (context == null) context = ui.getUserDir(); + if (headerUiProvider != null) + refreshPart(headerUiProvider, ui.getHeader(), context); ui.refreshBelowHeader(true); for (String key : layersByPid.keySet()) { SuiteLayer layer = layersByPid.get(key).get(); ui.addLayer(key, layer); } - refreshPart(findUiProvider(LEAD_PANE_PID), ui.getLeadPane(), context); + + if (leadPaneUiProvider != null) + refreshPart(leadPaneUiProvider, ui.getLeadPane(), context); + if (footerUiProvider != null) + refreshPart(footerUiProvider, ui.getFooter(), context); ui.layout(true, true); - setState(parent, state); + setState(ui, state != null ? state : defaultLayerPid); } } catch (Exception e) { CmsFeedback.show("Unexpected exception", e); @@ -165,6 +243,8 @@ public class SuiteApp extends AbstractCmsApp implements EventHandler { } private void initLocale(CmsSession cmsSession) { + if (cmsSession == null) + return; Locale locale = cmsSession.getLocale(); UiContext.setLocale(locale); LocaleUtils.setThreadLocale(locale); @@ -172,47 +252,56 @@ public class SuiteApp extends AbstractCmsApp implements EventHandler { } private void refreshPart(CmsUiProvider uiProvider, Composite part, Node context) { - CmsUiUtils.clear(part); + CmsSwtUtils.clear(part); uiProvider.createUiPart(part, context); } private CmsUiProvider findUiProvider(String pid) { if (!uiProvidersByPid.containsKey(pid)) - throw new IllegalArgumentException("No UI provider registered as " + pid); + return null; return uiProvidersByPid.get(pid).get(); } + private SuiteLayer findLayer(String pid) { + if (!layersByPid.containsKey(pid)) + return null; + return layersByPid.get(pid).get(); + } + private T findByType(Map> byType, Node context) { if (context == null) throw new IllegalArgumentException("A node should be provided"); try { // mixins Set types = new TreeSet<>(); - for (NodeType nodeType : context.getMixinNodeTypes()) { - String typeName = nodeType.getName(); - if (byType.containsKey(typeName)) { - types.add(typeName); + 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 nodeType = context.getPrimaryNodeType(); - String typeName = nodeType.getName(); - if (byType.containsKey(typeName)) { - types.add(typeName); - } - for (NodeType mixin : nodeType.getDeclaredSupertypes()) { - if (byType.containsKey(mixin.getName())) { - types.add(mixin.getName()); - } + 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 typeName = context.getProperty(EntityNames.ENTITY_TYPE).getString(); - if (byType.containsKey(typeName)) { - types.add(typeName); + String entityTypeName = context.getProperty(EntityNames.ENTITY_TYPE).getString(); + if (byType.containsKey(entityTypeName)) { + types.add(entityTypeName); } } } @@ -220,12 +309,12 @@ public class SuiteApp extends AbstractCmsApp implements EventHandler { // if (context.getPath().equals("/")) {// root node // types.add("nt:folder"); // } - if (NodeUtils.isUserHome(context) && byType.containsKey("nt:folder")) {// home node + 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); + 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); @@ -235,35 +324,65 @@ public class SuiteApp extends AbstractCmsApp implements EventHandler { } } + 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(Composite parent, String state) { - if (state == null || state.equals("~")) + public void setState(CmsUi cmsUi, String state) { + if (state == null) return; - if (!state.startsWith("/") && !state.equals("~")) { - if (parent instanceof SuiteUi) { - SuiteUi ui = (SuiteUi) parent; - String currentLayerId = ui.getCurrentLayerId(); - if (state.equals(currentLayerId)) - return; // does nothing - else { - Map properties = new HashMap<>(); - properties.put(SuiteEvent.LAYER, state); - ui.getCmsView().sendEvent(SuiteEvent.switchLayer.topic(), properties); + if (!state.startsWith("/")) { + if (cmsUi instanceof SuiteUi) { + SuiteUi ui = (SuiteUi) 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(SuiteEvent.LAYER, layerId); + properties.put(SuiteEvent.NODE_PATH, HOME_STATE); + ui.getCmsView().sendEvent(SuiteEvent.switchLayer.topic(), properties); } return; } - SuiteUi suiteUi = (SuiteUi) parent; + SuiteUi suiteUi = (SuiteUi) cmsUi; Node node = stateToNode(suiteUi, state); if (node == null) { - suiteUi.getCmsView().navigateTo("~"); + suiteUi.getCmsView().navigateTo(HOME_STATE); } else { suiteUi.getCmsView().sendEvent(SuiteEvent.switchLayer.topic(), SuiteEvent.eventProperties(node)); suiteUi.getCmsView().sendEvent(SuiteEvent.refreshPart.topic(), SuiteEvent.eventProperties(node)); } } - private String nodeToState(Node node) { + // TODO move it to an internal package? + static String nodeToState(Node node) { return '/' + Jcr.getWorkspaceName(node) + Jcr.getPath(node); } @@ -308,10 +427,16 @@ public class SuiteApp extends AbstractCmsApp implements EventHandler { // Specific UI related events SuiteUi ui = getRelatedUi(event); + if (ui == null) + return; try { + String appTitle = ""; + if (ui.getTitle() != null) + appTitle = ui.getTitle().lead() + " - "; + // String currentLayerId = ui.getCurrentLayerId(); // SuiteLayer currentLayer = currentLayerId != null ? layersByPid.get(currentLayerId).get() : null; - if (isTopic(event, SuiteEvent.refreshPart)) { + if (SuiteUiUtils.isTopic(event, SuiteEvent.refreshPart)) { Node node = getNode(ui, event); if (node == null) return; @@ -319,8 +444,8 @@ public class SuiteApp extends AbstractCmsApp implements EventHandler { SuiteLayer layer = findByType(layersByType, node); ui.switchToLayer(layer, node); ui.getCmsView().runAs(() -> layer.view(uiProvider, ui.getCurrentWorkArea(), node)); - ui.getCmsView().stateChanged(nodeToState(node), Jcr.getTitle(node)); - } else if (isTopic(event, SuiteEvent.openNewPart)) { + ui.getCmsView().stateChanged(nodeToState(node), appTitle + Jcr.getTitle(node)); + } else if (SuiteUiUtils.isTopic(event, SuiteEvent.openNewPart)) { Node node = getNode(ui, event); if (node == null) return; @@ -328,13 +453,37 @@ public class SuiteApp extends AbstractCmsApp implements EventHandler { SuiteLayer layer = findByType(layersByType, node); ui.switchToLayer(layer, node); ui.getCmsView().runAs(() -> layer.open(uiProvider, ui.getCurrentWorkArea(), node)); - ui.getCmsView().stateChanged(nodeToState(node), Jcr.getTitle(node)); - } else if (isTopic(event, SuiteEvent.switchLayer)) { + ui.getCmsView().stateChanged(nodeToState(node), appTitle + Jcr.getTitle(node)); + } else if (SuiteUiUtils.isTopic(event, SuiteEvent.switchLayer)) { String layerId = get(event, SuiteEvent.LAYER); if (layerId != null) { // ui.switchToLayer(layerId, ui.getUserDir()); - ui.getCmsView().runAs(() -> ui.switchToLayer(layerId, ui.getUserDir())); - ui.getCmsView().navigateTo(layerId); + SuiteLayer 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.getCmsView().doAs(() -> ui.switchToLayer(layerId, ui.getUserDir())); + String title = null; + if (layerTitle != null) + title = layerTitle.lead(); + Node nodeFromState = getNode(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 { + Node layerCurrentContext = suiteLayer.getCurrentContext(workArea); + if (layerCurrentContext != null) { + // layer was already showing a context so we set the state to it + ui.getCmsView().stateChanged(nodeToState(layerCurrentContext), + appTitle + Jcr.getTitle(layerCurrentContext)); + } else { + // no context was shown + ui.getCmsView().stateChanged(layerId, appTitle + title); + } + } } else { Node node = getNode(ui, event); if (node != null) { @@ -352,6 +501,8 @@ public class SuiteApp extends AbstractCmsApp implements EventHandler { private Node getNode(SuiteUi ui, Event event) { String nodePath = get(event, SuiteEvent.NODE_PATH); + if (nodePath != null && nodePath.equals(HOME_STATE)) + return ui.getUserDir(); String workspaceName = get(event, SuiteEvent.WORKSPACE); Session session = ui.getSession(workspaceName); Node node; @@ -375,7 +526,7 @@ public class SuiteApp extends AbstractCmsApp implements EventHandler { else { Session adminSession = null; try { - adminSession = NodeUtils.openDataAdminSession(getRepository(), workspaceName); + adminSession = CmsJcrUtils.openDataAdminSession(getRepository(), workspaceName); SuiteUtils.getOrCreateUserNode(adminSession, userDn); } finally { Jcr.logout(adminSession); @@ -392,11 +543,7 @@ public class SuiteApp extends AbstractCmsApp implements EventHandler { return managedUis.get(get(event, CMS_VIEW_UID_PROPERTY)); } - private static boolean isTopic(Event event, CmsEvent cmsEvent) { - return event.getTopic().equals(cmsEvent.topic()); - } - - private static String get(Event event, String key) { + public static String get(Event event, String key) { Object value = event.getProperty(key); if (value == null) return null; @@ -479,4 +626,13 @@ public class SuiteApp extends AbstractCmsApp implements EventHandler { this.cmsUserManager = cmsUserManager; } + public Repository getRepository() { + return repository; + } + + public void setRepository(Repository repository) { + this.repository = repository; + } + + }