From 32315b6eea1e2284e4269536b5fb7fee8cc03b8d Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Fri, 9 Oct 2020 11:56:35 +0200 Subject: [PATCH] Introduce tabbed area. --- .../src/org/argeo/api/NodeConstants.java | 5 + .../src/org/argeo/api/RankingKey.java | 36 ++- .../src/org/argeo/cms/ui/CmsUiProvider.java | 1 - .../src/org/argeo/cms/ui/CmsView.java | 11 + .../src/org/argeo/cms/ui/MvcProvider.java | 2 +- .../src/org/argeo/cms/ui/util/CmsEvent.java | 21 ++ .../src/org/argeo/cms/ui/util/CmsUiUtils.java | 2 + .../src/org/argeo/cms/ui/viewers/Section.java | 34 +-- .../argeo/cms/ui/widgets/JcrComposite.java | 120 +++++----- .../argeo/cms/ui/widgets/StyledControl.java | 4 +- .../org/argeo/cms/ui/widgets/TabbedArea.java | 215 ++++++++++++++++++ .../org/argeo/cms/web/CmsWebEntryPoint.java | 3 + org.argeo.jcr/src/org/argeo/jcr/Jcr.java | 11 + 13 files changed, 388 insertions(+), 77 deletions(-) create mode 100644 org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsEvent.java create mode 100644 org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/TabbedArea.java diff --git a/org.argeo.api/src/org/argeo/api/NodeConstants.java b/org.argeo.api/src/org/argeo/api/NodeConstants.java index f56159168..c8fe96247 100644 --- a/org.argeo.api/src/org/argeo/api/NodeConstants.java +++ b/org.argeo.api/src/org/argeo/api/NodeConstants.java @@ -131,4 +131,9 @@ public interface NodeConstants { */ String NODE_REPOS_FACTORY_PID = "org.argeo.api.repos"; String NODE_USER_ADMIN_PID = "org.argeo.api.userAdmin"; + + /* + * ENTITIES + */ + String DATA_TYPE = "argeo.data.type"; } diff --git a/org.argeo.api/src/org/argeo/api/RankingKey.java b/org.argeo.api/src/org/argeo/api/RankingKey.java index 90ba691e1..c475c6733 100644 --- a/org.argeo.api/src/org/argeo/api/RankingKey.java +++ b/org.argeo.api/src/org/argeo/api/RankingKey.java @@ -13,7 +13,7 @@ public class RankingKey implements Comparable { private String pid; private Integer ranking = 0; - private Long id; + private Long id = 0l; private String dataType; private String dataPath; @@ -32,6 +32,11 @@ public class RankingKey implements Comparable { ? Integer.parseInt(properties.get(SERVICE_RANKING).toString()) : 0; this.id = properties.containsKey(SERVICE_ID) ? (Long) properties.get(SERVICE_ID) : null; + + // Argeo specific + this.dataType = properties.containsKey(NodeConstants.DATA_TYPE) + ? properties.get(NodeConstants.DATA_TYPE).toString() + : null; } @Override @@ -41,6 +46,8 @@ public class RankingKey implements Comparable { result = +pid.hashCode(); if (ranking != null) result = +ranking; + if (dataType != null) + result = +dataType.hashCode(); return result; } @@ -56,6 +63,8 @@ public class RankingKey implements Comparable { sb.append(pid); if (ranking != null && ranking != 0) sb.append(' ').append(ranking); + if (dataType != null) + sb.append(' ').append(dataType); return sb.toString(); } @@ -85,9 +94,22 @@ public class RankingKey implements Comparable { } } else { - + if (dataType != null && o.dataType != null) { + if (dataType.equals(o.dataType)) { + // TODO factorise + if (ranking.equals(o.ranking)) + if (id != null && o.id != null) + return id.compareTo(o.id); + else + return 0; + else + return ranking.compareTo(o.ranking); + } else { + return dataPath.compareTo(o.dataType); + } + } } - return 0; + return -1; } public String getPid() { @@ -118,6 +140,14 @@ public class RankingKey implements Comparable { return new RankingKey(pid, Integer.MAX_VALUE, null, null, null); } + public static RankingKey minDataType(String dataType) { + return new RankingKey(null, Integer.MIN_VALUE, null, dataType, null); + } + + public static RankingKey maxDataType(String dataType) { + return new RankingKey(null, Integer.MAX_VALUE, null, dataType, null); + } + private static boolean equalsOrBothNull(Object o1, Object o2) { if (o1 == null && o2 == null) return true; diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/CmsUiProvider.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/CmsUiProvider.java index 6b1dde38a..00939e154 100644 --- a/org.argeo.cms.ui/src/org/argeo/cms/ui/CmsUiProvider.java +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/CmsUiProvider.java @@ -3,7 +3,6 @@ package org.argeo.cms.ui; import javax.jcr.Node; import javax.jcr.RepositoryException; -import org.argeo.api.MvcProvider; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/CmsView.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/CmsView.java index 0e7e72680..9f5e4f797 100644 --- a/org.argeo.cms.ui/src/org/argeo/cms/ui/CmsView.java +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/CmsView.java @@ -1,5 +1,6 @@ package org.argeo.cms.ui; +import java.util.HashMap; import java.util.Map; import javax.security.auth.login.LoginContext; @@ -41,6 +42,16 @@ public interface CmsView { } + /** + * Convenience methods for when {@link #sendEvent(String, Map)} only requires + * one single parameter. + */ + default void sendEvent(String topic, String param, Object value) { + Map properties = new HashMap<>(); + properties.put(param, value); + sendEvent(topic, properties); + } + static CmsView getCmsView(Composite parent) { // find parent shell Shell topShell = parent.getShell(); diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/MvcProvider.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/MvcProvider.java index 5d48873ab..f29d6b76a 100644 --- a/org.argeo.cms.ui/src/org/argeo/cms/ui/MvcProvider.java +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/MvcProvider.java @@ -1,4 +1,4 @@ -package org.argeo.api; +package org.argeo.cms.ui; import java.util.function.BiFunction; diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsEvent.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsEvent.java new file mode 100644 index 000000000..ca0797ba1 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsEvent.java @@ -0,0 +1,21 @@ +package org.argeo.cms.ui.util; + +import org.argeo.cms.ui.CmsView; + +/** + * Can be applied to {@link Enum}s in order to define events used by + * {@link CmsView#sendEvent(String, java.util.Map)}. + */ +public interface CmsEvent { + String name(); + + default String topic() { + return getTopicBase() + "/" + name(); + } + + default String getTopicBase() { + return "argeo/cms"; + } + + +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsUiUtils.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsUiUtils.java index 65228aa5d..915b03336 100644 --- a/org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsUiUtils.java +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsUiUtils.java @@ -178,6 +178,8 @@ public class CmsUiUtils implements CmsConstants { /** Style widget */ public static T style(T widget, String style) { + if (style == null) + return widget;// does nothing widget.setData(CmsConstants.STYLE, style); return widget; } diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/Section.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/Section.java index 83ea56076..6f8705099 100644 --- a/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/Section.java +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/Section.java @@ -7,7 +7,6 @@ import java.util.Map; import javax.jcr.Node; import javax.jcr.RepositoryException; -import org.argeo.cms.CmsException; import org.argeo.cms.ui.util.CmsUiUtils; import org.argeo.cms.ui.widgets.JcrComposite; import org.eclipse.swt.SWT; @@ -21,23 +20,27 @@ public class Section extends JcrComposite { private Composite sectionHeader; private final Integer relativeDepth; - public Section(Composite parent, int style, Node node) throws RepositoryException { + public Section(Composite parent, int style, Node node) { this(parent, findSection(parent), style, node); } - public Section(Section section, int style, Node node) throws RepositoryException { + public Section(Section section, int style, Node node) { this(section, section, style, node); } - protected Section(Composite parent, Section parentSection, int style, Node node) throws RepositoryException { + protected Section(Composite parent, Section parentSection, int style, Node node) { super(parent, style, node); - this.parentSection = parentSection; - if (parentSection != null) { - relativeDepth = getNode().getDepth() - parentSection.getNode().getDepth(); - } else { - relativeDepth = 0; + try { + this.parentSection = parentSection; + if (parentSection != null) { + relativeDepth = getNode().getDepth() - parentSection.getNode().getDepth(); + } else { + relativeDepth = 0; + } + setLayout(CmsUiUtils.noSpaceGridLayout()); + } catch (RepositoryException e) { + throw new IllegalStateException("Cannot create section from " + node, e); } - setLayout(CmsUiUtils.noSpaceGridLayout()); } public Map getSubSections() throws RepositoryException { @@ -65,15 +68,20 @@ public class Section extends JcrComposite { collectDirectSubSections((Composite) child, subSections); } - public void createHeader() { + public Composite createHeader() { + return createHeader(this); + } + + public Composite createHeader(Composite parent) { if (sectionHeader != null) - throw new CmsException("Section header was already created"); + sectionHeader.dispose(); - sectionHeader = new Composite(this, SWT.NONE); + sectionHeader = new Composite(parent, SWT.NONE); sectionHeader.setLayoutData(CmsUiUtils.fillWidth()); sectionHeader.setLayout(CmsUiUtils.noSpaceGridLayout()); // sectionHeader.moveAbove(null); // layout(); + return sectionHeader; } public Composite getHeader() { diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/JcrComposite.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/JcrComposite.java index d47aba792..a3cdb9827 100644 --- a/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/JcrComposite.java +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/JcrComposite.java @@ -15,7 +15,7 @@ import org.eclipse.swt.widgets.Composite; public class JcrComposite extends Composite { private static final long serialVersionUID = -1447009015451153367L; - private final Session session; + private Session session; private String nodeId; private String property = null; @@ -28,37 +28,39 @@ public class JcrComposite extends Composite { nodeId = null; } - public JcrComposite(Composite parent, int style, Item item) - throws RepositoryException { + public JcrComposite(Composite parent, int style, Item item) { this(parent, style, item, false); } - public JcrComposite(Composite parent, int style, Item item, - boolean cacheImmediately) throws RepositoryException { + public JcrComposite(Composite parent, int style, Item item, boolean cacheImmediately) { super(parent, style); - this.session = item.getSession(); - if (!cacheImmediately && (SWT.READ_ONLY == (style & SWT.READ_ONLY))) { - // (useless?) optimization: we only save a pointer to the session, - // not even a reference to the item - this.nodeId = null; - } else { - Node node; - Property property = null; - if (item instanceof Node) { - node = (Node) item; - } else {// Property - property = (Property) item; - if (property.isMultiple())// TODO manage property index - throw new CmsException( - "Multiple properties not supported yet."); - this.property = property.getName(); - node = property.getParent(); + if (item != null) + try { + this.session = item.getSession(); + if (!cacheImmediately && (SWT.READ_ONLY == (style & SWT.READ_ONLY))) { + // (useless?) optimization: we only save a pointer to the session, + // not even a reference to the item + this.nodeId = null; + } else { + Node node; + Property property = null; + if (item instanceof Node) { + node = (Node) item; + } else {// Property + property = (Property) item; + if (property.isMultiple())// TODO manage property index + throw new CmsException("Multiple properties not supported yet."); + this.property = property.getName(); + node = property.getParent(); + } + this.nodeId = node.getIdentifier(); + if (cacheImmediately) + this.cache = node; + } + setLayout(CmsUiUtils.noSpaceGridLayout()); + } catch (RepositoryException e) { + throw new IllegalStateException("Cannot create composite from " + item, e); } - this.nodeId = node.getIdentifier(); - if (cacheImmediately) - this.cache = node; - } - setLayout(CmsUiUtils.noSpaceGridLayout()); } public synchronized Node getNode() { @@ -89,12 +91,10 @@ public class JcrComposite extends Composite { throw new CmsException("Item is not a Property"); Node node = getNodeInternal(); if (!node.hasProperty(property)) - throw new CmsException("Property " + property - + " is not set on " + node); + throw new CmsException("Property " + property + " is not set on " + node); return node.getProperty(property); } catch (RepositoryException e) { - throw new CmsException("Cannot get property " + property - + " from node " + nodeId, e); + throw new CmsException("Cannot get property " + property + " from node " + nodeId, e); } } @@ -103,7 +103,7 @@ public class JcrComposite extends Composite { } /** Set/update the cache or change the node */ - public synchronized void setNode(Node node) throws RepositoryException { + public synchronized void setNode(Node node) { if (!itemIsNode()) throw new CmsException("Cannot set a Node on a Property"); @@ -112,21 +112,25 @@ public class JcrComposite extends Composite { return; } - if (session == null || session != node.getSession())// check session - throw new CmsException("Uncompatible session"); - - if (nodeId == null || !nodeId.equals(node.getIdentifier())) { - nodeId = node.getIdentifier(); - cache = node; - itemUpdated(); - } else { - cache = node;// set/update cache + try { +// if (session != null || session != node.getSession())// check session +// throw new IllegalArgumentException("Uncompatible session"); +// if (session == null) + session = node.getSession(); + if (nodeId == null || !nodeId.equals(node.getIdentifier())) { + nodeId = node.getIdentifier(); + cache = node; + itemUpdated(); + } else { + cache = node;// set/update cache + } + } catch (RepositoryException e) { + throw new IllegalStateException(e); } } /** Set/update the cache or change the property */ - public synchronized void setProperty(Property prop) - throws RepositoryException { + public synchronized void setProperty(Property prop) { if (itemIsNode()) throw new CmsException("Cannot set a Property on a Node"); @@ -135,18 +139,21 @@ public class JcrComposite extends Composite { return; } - if (session == null || session != prop.getSession())// check session - throw new CmsException("Uncompatible session"); - - Node node = prop.getNode(); - if (nodeId == null || !nodeId.equals(node.getIdentifier()) - || !property.equals(prop.getName())) { - nodeId = node.getIdentifier(); - property = prop.getName(); - cache = node; - itemUpdated(); - } else { - cache = node;// set/update cache + try { + if (session == null || session != prop.getSession())// check session + throw new IllegalArgumentException("Uncompatible session"); + + Node node = prop.getNode(); + if (nodeId == null || !nodeId.equals(node.getIdentifier()) || !property.equals(prop.getName())) { + nodeId = node.getIdentifier(); + property = prop.getName(); + cache = node; + itemUpdated(); + } else { + cache = node;// set/update cache + } + } catch (RepositoryException e) { + throw new IllegalStateException(e); } } @@ -155,8 +162,7 @@ public class JcrComposite extends Composite { } /** Change the node, does nothing if same. */ - public synchronized void setNodeId(String nodeId) - throws RepositoryException { + public synchronized void setNodeId(String nodeId) throws RepositoryException { if (this.nodeId != null && this.nodeId.equals(nodeId)) return; this.nodeId = nodeId; diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/StyledControl.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/StyledControl.java index b085fdf9c..9d7037c25 100644 --- a/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/StyledControl.java +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/StyledControl.java @@ -29,11 +29,11 @@ public abstract class StyledControl extends JcrComposite implements CmsConstants setLayout(CmsUiUtils.noSpaceGridLayout()); } - public StyledControl(Composite parent, int style, Item item) throws RepositoryException { + public StyledControl(Composite parent, int style, Item item) { super(parent, style, item); } - public StyledControl(Composite parent, int style, Item item, boolean cacheImmediately) throws RepositoryException { + public StyledControl(Composite parent, int style, Item item, boolean cacheImmediately) { super(parent, style, item, cacheImmediately); } diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/TabbedArea.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/TabbedArea.java new file mode 100644 index 000000000..ba1e2f6f4 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/TabbedArea.java @@ -0,0 +1,215 @@ +package org.argeo.cms.ui.widgets; + +import java.util.ArrayList; +import java.util.List; + +import javax.jcr.Node; + +import org.argeo.cms.ui.CmsUiProvider; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.argeo.cms.ui.viewers.Section; +import org.argeo.eclipse.ui.Selected; +import org.argeo.jcr.Jcr; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.FormLayout; +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.eclipse.swt.widgets.ToolBar; +import org.eclipse.swt.widgets.ToolItem; + +public class TabbedArea extends Composite { + private static final long serialVersionUID = 8659669229482033444L; + + private Composite headers; + private Composite body; + + private List
sections = new ArrayList<>(); + + private Node previousNode; + private CmsUiProvider previousUiProvider; + private CmsUiProvider currentUiProvider; + + private String tabStyle; + private String tabSelectedStyle; + private Image closeIcon; + + private long openingTimer = 500; + + public TabbedArea(Composite parent, int style) { + super(parent, style); + + setLayout(CmsUiUtils.noSpaceGridLayout()); + + // TODO manage tabs at bottom or sides + headers = new Composite(this, SWT.NONE); + headers.setLayoutData(CmsUiUtils.fillWidth()); + body = new Composite(this, SWT.NONE); + body.setLayoutData(CmsUiUtils.fillAll()); + + body.setLayout(new FormLayout()); + emptyState(); + } + + protected void refreshTabHeaders() { + int tabCount = sections.size() > 0 ? sections.size() : 1; + for (Control tab : headers.getChildren()) + tab.dispose(); + headers.setLayout(CmsUiUtils.noSpaceGridLayout(new GridLayout(tabCount, true))); + + if (sections.size() == 0) { + Composite emptyHeader = new Composite(headers, SWT.NONE); + emptyHeader.setLayoutData(CmsUiUtils.fillAll()); + emptyHeader.setLayout(new GridLayout()); + Label lbl = new Label(emptyHeader, SWT.NONE); + lbl.setText("-"); + lbl.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false)); + + } + + Section currentSection = getCurrentSection(); + for (Section section : sections) { + boolean selected = section == currentSection; + Composite sectionHeader = section.createHeader(headers); + CmsUiUtils.style(sectionHeader, selected ? tabSelectedStyle : tabStyle); + int headerColumns = 2; + sectionHeader.setLayout(new GridLayout(headerColumns, false)); + Button title = new Button(sectionHeader, SWT.FLAT); + CmsUiUtils.style(title, selected ? tabSelectedStyle : tabStyle); + title.setLayoutData(CmsUiUtils.fillWidth()); + title.addSelectionListener((Selected) (e) -> section.moveAbove(null)); + Node node = section.getNode(); + title.setText(Jcr.getTitle(node)); + ToolBar toolBar = new ToolBar(sectionHeader, SWT.NONE); + CmsUiUtils.style(toolBar, selected ? tabSelectedStyle : tabStyle); + ToolItem closeItem = new ToolItem(toolBar, SWT.FLAT); + if (closeIcon != null) + closeItem.setImage(closeIcon); + else + closeItem.setText("X"); + closeItem.addSelectionListener((Selected) (e) -> closeTab(section)); + } + } + + public void view(CmsUiProvider uiProvider, Node context) { + int index = tabIndex(context); + if (index >= 0) { + showTab(index); + previousNode = context; + previousUiProvider = uiProvider; + return; + } + Section section = (Section) body.getChildren()[0]; + previousNode = section.getNode(); + if (previousNode == null) {// empty state + previousNode = context; + previousUiProvider = uiProvider; + } else { + previousUiProvider = currentUiProvider; + } + currentUiProvider = uiProvider; + section.setNode(context); + section.setLayoutData(CmsUiUtils.coversAll()); + for (Control child : section.getChildren()) + child.dispose(); + uiProvider.createUiPart(section, context); + if (sections.size() == 0) + sections.add(section); + refreshTabHeaders(); + layout(true, true); + } + + public void open(CmsUiProvider uiProvider, Node context) { + try { + if (openingTimer > 0) + Thread.sleep(openingTimer); + } catch (InterruptedException e) { + // silent + } + + // int index = tabIndex(context); + if (previousNode != null && Jcr.getIdentifier(previousNode).equals(Jcr.getIdentifier(context))) { + // does nothing + return; + } + if (sections.size() == 0) + CmsUiUtils.clear(body); + Section currentSection = getCurrentSection(); + int currentIndex = sections.indexOf(currentSection); + Section nextCurrentSection = new Section(body, SWT.NONE, context); + nextCurrentSection.setLayoutData(CmsUiUtils.coversAll()); + sections.remove(currentSection); + sections.add(currentIndex, nextCurrentSection); + sections.add(currentSection); + nextCurrentSection.moveAbove(null); + if (previousNode != null) { + view(previousUiProvider, previousNode); + } + refreshTabHeaders(); + layout(true, true); + } + + public void showTab(int index) { + Section sectionToShow = sections.get(index); + sectionToShow.moveAbove(null); + layout(true, true); + } + + private int tabIndex(Node node) { + for (int i = 0; i < sections.size(); i++) { + Section section = sections.get(i); + if (Jcr.getIdentifier(section.getNode()).equals(Jcr.getIdentifier(node))) + return i; + } + return -1; + } + + public void closeTab(Section section) { + int currentIndex = sections.indexOf(section); + int nextIndex = currentIndex == 0 ? 0 : currentIndex - 1; + sections.remove(section); + section.dispose(); + if (sections.size() == 0) { + emptyState(); + refreshTabHeaders(); + layout(true, true); + return; + } + refreshTabHeaders(); + showTab(nextIndex); + } + + protected void emptyState() { + new Section(body, SWT.NONE, null); + refreshTabHeaders(); + } + + public Composite getCurrent() { + return getCurrentSection(); + } + + protected Section getCurrentSection() { + return (Section) body.getChildren()[0]; + } + + public void setTabStyle(String tabStyle) { + this.tabStyle = tabStyle; + } + + public void setTabSelectedStyle(String tabSelectedStyle) { + this.tabSelectedStyle = tabSelectedStyle; + } + + public void setCloseIcon(Image closeIcon) { + this.closeIcon = closeIcon; + } + + public void setOpeningTimer(long openingTimer) { + this.openingTimer = openingTimer; + } + +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/web/CmsWebEntryPoint.java b/org.argeo.cms.ui/src/org/argeo/cms/web/CmsWebEntryPoint.java index 186c988b1..2961eead9 100644 --- a/org.argeo.cms.ui/src/org/argeo/cms/web/CmsWebEntryPoint.java +++ b/org.argeo.cms.ui/src/org/argeo/cms/web/CmsWebEntryPoint.java @@ -3,6 +3,7 @@ package org.argeo.cms.web; import static org.eclipse.rap.rwt.internal.service.ContextProvider.getApplicationContext; import java.security.PrivilegedAction; +import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -213,6 +214,8 @@ public class CmsWebEntryPoint implements EntryPoint, CmsView, BrowserNavigationL @Override public void sendEvent(String topic, Map properties) { + if (properties == null) + properties = new HashMap<>(); if (properties.containsKey(CMS_VIEW_UID_PROPERTY) && !properties.get(CMS_VIEW_UID_PROPERTY).equals(uid)) throw new IllegalArgumentException("Property " + CMS_VIEW_UID_PROPERTY + " is set to another CMS view uid (" + properties.get(CMS_VIEW_UID_PROPERTY) + ") then " + uid); diff --git a/org.argeo.jcr/src/org/argeo/jcr/Jcr.java b/org.argeo.jcr/src/org/argeo/jcr/Jcr.java index 624b92b5b..c3db4b604 100644 --- a/org.argeo.jcr/src/org/argeo/jcr/Jcr.java +++ b/org.argeo.jcr/src/org/argeo/jcr/Jcr.java @@ -131,6 +131,17 @@ public class Jcr { } } + /** + * If node has mixin {@link NodeType#MIX_TITLE}, return + * {@link Property#JCR_TITLE}, otherwise return {@link #getName(Node)}. + */ + public static String getTitle(Node node) { + if (Jcr.isNodeType(node, NodeType.MIX_TITLE)) + return get(node, Property.JCR_TITLE); + else + return Jcr.getName(node); + } + /** Accesses a {@link NodeIterator} as an {@link Iterable}. */ @SuppressWarnings("unchecked") public static Iterable iterate(NodeIterator nodeIterator) { -- 2.30.2