Refactor to use ACR instead of JCR.
authorMathieu Baudier <mbaudier@argeo.org>
Thu, 16 Jun 2022 07:29:03 +0000 (09:29 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Thu, 16 Jun 2022 07:29:03 +0000 (09:29 +0200)
14 files changed:
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtUtils.java
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/ContentComposite.java [new file with mode: 0644]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtSection.java [new file with mode: 0644]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtSectionPart.java [new file with mode: 0644]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtTabbedArea.java [new file with mode: 0644]
jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContent.java
jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/CmsUiProvider.java
jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsUiUtils.java
jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/JcrComposite.java
org.argeo.api.acr/src/org/argeo/api/acr/spi/ProvidedContent.java
org.argeo.cms.ux/src/org/argeo/cms/ux/CmsUxUtils.java
org.argeo.cms.ux/src/org/argeo/cms/ux/widgets/ContentPart.java [new file with mode: 0644]
org.argeo.cms.ux/src/org/argeo/cms/ux/widgets/EditablePart.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/acr/AbstractContent.java

index 3ff747949c5553d0fa0694e13f6eb323ad1e772c..784d03173bce51ec53285c39f1a1c87defc9490f 100644 (file)
@@ -1,7 +1,10 @@
 package org.argeo.cms.swt;
 
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.StringTokenizer;
 
 import org.argeo.api.cms.CmsStyle;
 import org.argeo.api.cms.CmsTheme;
@@ -272,4 +275,18 @@ public class CmsSwtUtils {
                for (Control child : composite.getChildren())
                        child.dispose();
        }
+
+       /** Clean reserved URL characters for use in HTTP links. */
+       public static String cleanPathForUrl(String path) {
+               StringTokenizer st = new StringTokenizer(path, "/");
+               StringBuilder sb = new StringBuilder();
+               while (st.hasMoreElements()) {
+                       sb.append('/');
+                       String encoded = URLEncoder.encode(st.nextToken(), StandardCharsets.UTF_8);
+                       encoded = encoded.replace("+", "%20");
+                       sb.append(encoded);
+       
+               }
+               return sb.toString();
+       }
 }
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/ContentComposite.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/ContentComposite.java
new file mode 100644 (file)
index 0000000..951889e
--- /dev/null
@@ -0,0 +1,41 @@
+package org.argeo.cms.swt.acr;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.spi.ProvidedContent;
+import org.eclipse.swt.widgets.Composite;
+
+/** A composite which can (optionally) manage a content. */
+public class ContentComposite extends Composite {
+       private static final long serialVersionUID = -1447009015451153367L;
+
+       public ContentComposite(Composite parent, int style, Content item) {
+               super(parent, style);
+               setData(item);
+       }
+
+       public Content getContent() {
+               return (Content) getData();
+       }
+
+       @Deprecated
+       public Content getNode() {
+               return getContent();
+       }
+
+       protected ProvidedContent getProvidedContent() {
+               return (ProvidedContent) getContent();
+       }
+
+       public String getSessionLocalId() {
+               return getProvidedContent().getSessionLocalId();
+       }
+
+       protected void itemUpdated() {
+               layout();
+       }
+
+       public void setContent(Content content) {
+               setData(content);
+               itemUpdated();
+       }
+}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtSection.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtSection.java
new file mode 100644 (file)
index 0000000..5562bcf
--- /dev/null
@@ -0,0 +1,159 @@
+package org.argeo.cms.swt.widgets;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.argeo.api.acr.Content;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.acr.ContentComposite;
+import org.argeo.cms.ux.widgets.EditablePart;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/** A structured UI related to a JCR context. */
+public class SwtSection extends ContentComposite {
+       private static final long serialVersionUID = -5933796173755739207L;
+
+       private final SwtSection parentSection;
+       private Composite sectionHeader;
+       private final Integer relativeDepth;
+
+       public SwtSection(Composite parent, int style, Content node) {
+               this(parent, findSection(parent), style, node);
+       }
+
+       public SwtSection(SwtSection section, int style, Content node) {
+               this(section, section, style, node);
+       }
+
+       protected SwtSection(Composite parent, SwtSection parentSection, int style, Content node) {
+               super(parent, style, node);
+               this.parentSection = parentSection;
+               if (parentSection != null) {
+                       relativeDepth = getProvidedContent().getDepth() - parentSection.getProvidedContent().getDepth();
+               } else {
+                       relativeDepth = 0;
+               }
+               setLayout(CmsSwtUtils.noSpaceGridLayout());
+       }
+
+       public Map<String, SwtSection> getSubSections() {
+               LinkedHashMap<String, SwtSection> result = new LinkedHashMap<String, SwtSection>();
+               for (Control child : getChildren()) {
+                       if (child instanceof Composite) {
+                               collectDirectSubSections((Composite) child, result);
+                       }
+               }
+               return Collections.unmodifiableMap(result);
+       }
+
+       private void collectDirectSubSections(Composite composite, LinkedHashMap<String, SwtSection> subSections) {
+               if (composite == sectionHeader || composite instanceof EditablePart)
+                       return;
+               if (composite instanceof SwtSection) {
+                       SwtSection section = (SwtSection) composite;
+                       subSections.put(section.getProvidedContent().getSessionLocalId(), section);
+                       return;
+               }
+
+               for (Control child : composite.getChildren())
+                       if (child instanceof Composite)
+                               collectDirectSubSections((Composite) child, subSections);
+       }
+
+       public Composite createHeader() {
+               return createHeader(this);
+       }
+
+       public Composite createHeader(Composite parent) {
+               if (sectionHeader != null)
+                       sectionHeader.dispose();
+
+               sectionHeader = new Composite(parent, SWT.NONE);
+               sectionHeader.setLayoutData(CmsSwtUtils.fillWidth());
+               sectionHeader.setLayout(CmsSwtUtils.noSpaceGridLayout());
+               // sectionHeader.moveAbove(null);
+               // layout();
+               return sectionHeader;
+       }
+
+       public Composite getHeader() {
+               if (sectionHeader != null && sectionHeader.isDisposed())
+                       sectionHeader = null;
+               return sectionHeader;
+       }
+
+       // SECTION PARTS
+       public SwtSectionPart getSectionPart(String partId) {
+               for (Control child : getChildren()) {
+                       if (child instanceof SwtSectionPart) {
+                               SwtSectionPart sectionPart = (SwtSectionPart) child;
+                               if (sectionPart.getPartId().equals(partId))
+                                       return sectionPart;
+                       }
+               }
+               return null;
+       }
+
+       public SwtSectionPart nextSectionPart(SwtSectionPart sectionPart) {
+               Control[] children = getChildren();
+               for (int i = 0; i < children.length; i++) {
+                       if (sectionPart == children[i]) {
+                               for (int j = i + 1; j < children.length; j++) {
+                                       if (children[i + 1] instanceof SwtSectionPart) {
+                                               return (SwtSectionPart) children[i + 1];
+                                       }
+                               }
+
+//                             if (i + 1 < children.length) {
+//                                     Composite next = (Composite) children[i + 1];
+//                                     return (SectionPart) next;
+//                             } else {
+//                                     // next section
+//                             }
+                       }
+               }
+               return null;
+       }
+
+       public SwtSectionPart previousSectionPart(SwtSectionPart sectionPart) {
+               Control[] children = getChildren();
+               for (int i = 0; i < children.length; i++) {
+                       if (sectionPart == children[i])
+                               if (i != 0) {
+                                       Composite previous = (Composite) children[i - 1];
+                                       return (SwtSectionPart) previous;
+                               } else {
+                                       // previous section
+                               }
+               }
+               return null;
+       }
+
+       @Override
+       public String toString() {
+               if (parentSection == null)
+                       return "Main section " + getContent();
+               return "Section " + getContent();
+       }
+
+       public SwtSection getParentSection() {
+               return parentSection;
+       }
+
+       public Integer getRelativeDepth() {
+               return relativeDepth;
+       }
+
+       /** Recursively finds the related section in the parents (can be itself) */
+       public static SwtSection findSection(Control control) {
+               if (control == null)
+                       return null;
+               if (control instanceof SwtSection)
+                       return (SwtSection) control;
+               else
+                       return findSection(control.getParent());
+       }
+}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtSectionPart.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtSectionPart.java
new file mode 100644 (file)
index 0000000..e057860
--- /dev/null
@@ -0,0 +1,11 @@
+package org.argeo.cms.swt.widgets;
+
+import org.argeo.cms.ux.widgets.ContentPart;
+import org.argeo.cms.ux.widgets.EditablePart;
+
+/** An editable part dynamically related to a Section */
+public interface SwtSectionPart extends EditablePart, ContentPart {
+       public String getPartId();
+
+       public SwtSection getSection();
+}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtTabbedArea.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtTabbedArea.java
new file mode 100644 (file)
index 0000000..a2f7671
--- /dev/null
@@ -0,0 +1,259 @@
+package org.argeo.cms.swt.widgets;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.spi.ProvidedContent;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.Selected;
+import org.argeo.cms.swt.acr.SwtUiProvider;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StackLayout;
+import org.eclipse.swt.graphics.Image;
+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;
+
+/** Manages {@link SwtSection} in a tab-like structure. */
+public class SwtTabbedArea extends Composite {
+       private static final long serialVersionUID = 8659669229482033444L;
+
+       private Composite headers;
+       private Composite body;
+
+       private List<SwtSection> sections = new ArrayList<>();
+
+       private ProvidedContent previousNode;
+       private SwtUiProvider previousUiProvider;
+       private SwtUiProvider currentUiProvider;
+
+       private String tabStyle;
+       private String tabSelectedStyle;
+       private String bodyStyle;
+       private Image closeIcon;
+
+       private StackLayout stackLayout;
+
+       private boolean singleTab = false;
+
+       public SwtTabbedArea(Composite parent, int style) {
+               super(parent, SWT.NONE);
+               CmsSwtUtils.style(parent, bodyStyle);
+
+               setLayout(CmsSwtUtils.noSpaceGridLayout());
+
+               // TODO manage tabs at bottom or sides
+               headers = new Composite(this, SWT.NONE);
+               headers.setLayoutData(CmsSwtUtils.fillWidth());
+               body = new Composite(this, SWT.NONE);
+               body.setLayoutData(CmsSwtUtils.fillAll());
+               // body.setLayout(new FormLayout());
+               stackLayout = new StackLayout();
+               body.setLayout(stackLayout);
+               emptyState();
+       }
+
+       protected void refreshTabHeaders() {
+               int tabCount = sections.size() > 0 ? sections.size() : 1;
+               for (Control tab : headers.getChildren())
+                       tab.dispose();
+
+               headers.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(tabCount, true)));
+
+               if (sections.size() == 0) {
+                       Composite emptyHeader = new Composite(headers, SWT.NONE);
+                       emptyHeader.setLayoutData(CmsSwtUtils.fillAll());
+                       emptyHeader.setLayout(new GridLayout());
+                       Label lbl = new Label(emptyHeader, SWT.NONE);
+                       lbl.setText("");
+                       lbl.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false));
+
+               }
+
+               SwtSection currentSection = getCurrentSection();
+               for (SwtSection section : sections) {
+                       boolean selected = section == currentSection;
+                       Composite sectionHeader = section.createHeader(headers);
+                       CmsSwtUtils.style(sectionHeader, selected ? tabSelectedStyle : tabStyle);
+                       int headerColumns = singleTab ? 1 : 2;
+                       sectionHeader.setLayout(new GridLayout(headerColumns, false));
+                       sectionHeader.setLayout(CmsSwtUtils.noSpaceGridLayout(headerColumns));
+                       Button title = new Button(sectionHeader, SWT.FLAT);
+                       CmsSwtUtils.style(title, selected ? tabSelectedStyle : tabStyle);
+                       title.setLayoutData(CmsSwtUtils.fillWidth());
+                       title.addSelectionListener((Selected) (e) -> showTab(tabIndex(section.getNode())));
+                       Content node = section.getContent();
+
+                       // FIXME find a standard way to display titles
+                       String titleStr = node.getName().getLocalPart();
+                       
+                       // TODO internationalize
+                       title.setText(titleStr);
+                       if (!singleTab) {
+                               ToolBar toolBar = new ToolBar(sectionHeader, SWT.NONE);
+                               ToolItem closeItem = new ToolItem(toolBar, SWT.FLAT);
+                               if (closeIcon != null)
+                                       closeItem.setImage(closeIcon);
+                               else
+                                       closeItem.setText("X");
+                               CmsSwtUtils.style(closeItem, selected ? tabSelectedStyle : tabStyle);
+                               closeItem.addSelectionListener((Selected) (e) -> closeTab(section));
+                       }
+               }
+
+       }
+
+       public void view(SwtUiProvider uiProvider, Content context) {
+               if (body.isDisposed())
+                       return;
+               int index = tabIndex(context);
+               if (index >= 0) {
+                       showTab(index);
+                       previousNode = (ProvidedContent) context;
+                       previousUiProvider = uiProvider;
+                       return;
+               }
+               SwtSection section = (SwtSection) body.getChildren()[0];
+               previousNode = (ProvidedContent) section.getNode();
+               if (previousNode == null) {// empty state
+                       previousNode = (ProvidedContent) context;
+                       previousUiProvider = uiProvider;
+               } else {
+                       previousUiProvider = currentUiProvider;
+               }
+               currentUiProvider = uiProvider;
+               section.setContent(context);
+               // section.setLayoutData(CmsUiUtils.coverAll());
+               build(section, uiProvider, context);
+               if (sections.size() == 0)
+                       sections.add(section);
+               refreshTabHeaders();
+               index = tabIndex(context);
+               showTab(index);
+               layout(true, true);
+       }
+
+       public void open(SwtUiProvider uiProvider, Content context) {
+               if (singleTab)
+                       throw new UnsupportedOperationException("Open is not supported in single tab mode.");
+
+               if (previousNode != null
+                               && previousNode.getSessionLocalId().equals(((ProvidedContent) context).getSessionLocalId())) {
+                       // does nothing
+                       return;
+               }
+               if (sections.size() == 0)
+                       CmsSwtUtils.clear(body);
+               SwtSection currentSection = getCurrentSection();
+               int currentIndex = sections.indexOf(currentSection);
+               SwtSection previousSection = new SwtSection(body, SWT.NONE, context);
+               build(previousSection, previousUiProvider, previousNode);
+               // previousSection.setLayoutData(CmsUiUtils.coverAll());
+               int newIndex = currentIndex + 1;
+               sections.add(currentIndex, previousSection);
+//             sections.add(newIndex, previousSection);
+               showTab(newIndex);
+               refreshTabHeaders();
+               layout(true, true);
+       }
+
+       public void showTab(int index) {
+               SwtSection sectionToShow = sections.get(index);
+               // sectionToShow.moveAbove(null);
+               stackLayout.topControl = sectionToShow;
+               refreshTabHeaders();
+               layout(true, true);
+       }
+
+       protected void build(SwtSection section, SwtUiProvider uiProvider, Content context) {
+               for (Control child : section.getChildren())
+                       child.dispose();
+               CmsSwtUtils.style(section, bodyStyle);
+               section.setContent(context);
+               uiProvider.createUiPart(section, context);
+
+       }
+
+       private int tabIndex(Content context) {
+               for (int i = 0; i < sections.size(); i++) {
+                       SwtSection section = sections.get(i);
+                       if (section.getSessionLocalId().equals(((ProvidedContent) context).getSessionLocalId()))
+                               return i;
+               }
+               return -1;
+       }
+
+       public void closeTab(SwtSection 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);
+       }
+
+       public void closeAllTabs() {
+               for (SwtSection section : sections) {
+                       section.dispose();
+               }
+               sections.clear();
+               emptyState();
+               refreshTabHeaders();
+               layout(true, true);
+       }
+
+       protected void emptyState() {
+               new SwtSection(body, SWT.NONE, null);
+               refreshTabHeaders();
+       }
+
+       public Composite getCurrent() {
+               return getCurrentSection();
+       }
+
+       protected SwtSection getCurrentSection() {
+               return (SwtSection) stackLayout.topControl;
+       }
+
+       public Content getCurrentContext() {
+               SwtSection section = getCurrentSection();
+               if (section != null) {
+                       return section.getNode();
+               } else {
+                       return null;
+               }
+       }
+
+       public void setTabStyle(String tabStyle) {
+               this.tabStyle = tabStyle;
+       }
+
+       public void setTabSelectedStyle(String tabSelectedStyle) {
+               this.tabSelectedStyle = tabSelectedStyle;
+       }
+
+       public void setBodyStyle(String bodyStyle) {
+               this.bodyStyle = bodyStyle;
+       }
+
+       public void setCloseIcon(Image closeIcon) {
+               this.closeIcon = closeIcon;
+       }
+
+       public void setSingleTab(boolean singleTab) {
+               this.singleTab = singleTab;
+       }
+
+}
index b32ae302085a059bf67c67ddfcca25c173bc9f4a..63bf8dfffae04110938d08fc56ef418cbc6382ca 100644 (file)
@@ -180,6 +180,8 @@ public class JcrContent extends AbstractContent {
 
        @Override
        public Content getParent() {
+               if (Jcr.isRoot(getJcrNode())) // root
+                       return null;
                return new JcrContent(session, provider, jcrWorkspace, Jcr.getParentPath(getJcrNode()));
        }
 
@@ -223,6 +225,7 @@ public class JcrContent extends AbstractContent {
        /*
         * ADAPTERS
         */
+       @SuppressWarnings("unchecked")
        public <A> A adapt(Class<A> clss) {
                if (Source.class.isAssignableFrom(clss)) {
 //                     try {
@@ -246,6 +249,7 @@ public class JcrContent extends AbstractContent {
                        return super.adapt(clss);
        }
 
+       @SuppressWarnings("unchecked")
        @Override
        public <C extends Closeable> C open(Class<C> clss) throws IOException, IllegalArgumentException {
                if (InputStream.class.isAssignableFrom(clss)) {
@@ -271,6 +275,15 @@ public class JcrContent extends AbstractContent {
                return provider;
        }
 
+       @Override
+       public String getSessionLocalId() {
+               try {
+                       return getJcrNode().getIdentifier();
+               } catch (RepositoryException e) {
+                       throw new JcrException("Cannot get identifier for " + getJcrNode(), e);
+               }
+       }
+
        /*
         * STATIC UTLITIES
         */
index 08b100efa00f0ccd9d211f1d36fa953f88edb7ad..4473498c19459b5dedc8642f7a54c00050b30d52 100644 (file)
@@ -31,6 +31,8 @@ public interface CmsUiProvider extends SwtUiProvider {
 
        @Override
        default Control createUiPart(Composite parent, Content context) {
+               if (context == null)
+                       return createUiPart(parent, (Node) null);
                if (context instanceof JcrContent) {
                        Node node = ((JcrContent) context).getJcrNode();
                        return createUiPart(parent, node);
index 8b384799fb9e2410aacb7f42a88f1c63fb89af92..73c545d0da10492f9a190a2ff86fe5a31570b265 100644 (file)
@@ -4,9 +4,6 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.net.MalformedURLException;
 import java.net.URL;
-import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
-import java.util.StringTokenizer;
 
 import javax.jcr.Node;
 import javax.jcr.RepositoryException;
@@ -83,21 +80,7 @@ public class CmsUiUtils {
 
        /** Clean reserved URL characters for use in HTTP links. */
        public static String getDataPathForUrl(Node node) {
-               return cleanPathForUrl(getDataPath(node));
-       }
-
-       /** Clean reserved URL characters for use in HTTP links. */
-       public static String cleanPathForUrl(String path) {
-               StringTokenizer st = new StringTokenizer(path, "/");
-               StringBuilder sb = new StringBuilder();
-               while (st.hasMoreElements()) {
-                       sb.append('/');
-                       String encoded = URLEncoder.encode(st.nextToken(), StandardCharsets.UTF_8);
-                       encoded = encoded.replace("+", "%20");
-                       sb.append(encoded);
-
-               }
-               return sb.toString();
+               return CmsSwtUtils.cleanPathForUrl(getDataPath(node));
        }
 
        /** @deprecated Use rowData16px() instead. GridData should not be reused. */
index 5d3576f2719cc1edc05208cc6685053d2125eb94..6b54c0a7bd82afbb07ff4754f2485b0023d91952 100644 (file)
@@ -207,7 +207,7 @@ public class JcrComposite extends Composite {
                layout();
        }
 
-       public Session getSession() {
-               return session;
-       }
+//     public Session getSession() {
+//             return session;
+//     }
 }
index d2509a49d8a9c68642c3023b50ad8b3b90fb41de..d9f378329f59a3d8932910cf2328df97f4d27796 100644 (file)
@@ -8,6 +8,14 @@ public interface ProvidedContent extends Content {
 
        ContentProvider getProvider();
 
+       int getDepth();
+
+       /**
+        * An opaque ID which is guaranteed to uniquely identify this content within the
+        * session return by {@link #getSession()}. Typically used for UI.
+        */
+       String getSessionLocalId();
+
        default ProvidedContent getMountPoint(String relativePath) {
                throw new UnsupportedOperationException("This content doe not support mount");
        }
index 9e23ff663c0f0f3236caf5067f110d410fe3580c..1b2b0e9400390b8ad2605a6d8eda21b717b0d235 100644 (file)
@@ -1,5 +1,6 @@
 package org.argeo.cms.ux;
 
+import org.argeo.api.acr.Content;
 import org.argeo.api.acr.ContentRepository;
 import org.argeo.api.acr.ContentSession;
 import org.argeo.api.cms.CmsView;
@@ -10,6 +11,10 @@ public class CmsUxUtils {
                return CurrentUser.callAs(cmsView.getCmsSession().getSubject(), () -> contentRepository.get());
        }
 
+       public static String getTitle(Content content) {
+               return content.getName().getLocalPart();
+       }
+
        /** singleton */
        private CmsUxUtils() {
 
diff --git a/org.argeo.cms.ux/src/org/argeo/cms/ux/widgets/ContentPart.java b/org.argeo.cms.ux/src/org/argeo/cms/ux/widgets/ContentPart.java
new file mode 100644 (file)
index 0000000..5e91f5c
--- /dev/null
@@ -0,0 +1,12 @@
+package org.argeo.cms.ux.widgets;
+
+import org.argeo.api.acr.Content;
+
+/** A part displaying or editing a content. */
+public interface ContentPart {
+       Content getContent();
+
+       @Deprecated
+       Content getNode();
+
+}
diff --git a/org.argeo.cms.ux/src/org/argeo/cms/ux/widgets/EditablePart.java b/org.argeo.cms.ux/src/org/argeo/cms/ux/widgets/EditablePart.java
new file mode 100644 (file)
index 0000000..1257cfc
--- /dev/null
@@ -0,0 +1,8 @@
+package org.argeo.cms.ux.widgets;
+
+/** Manages whether an editable or non editable control is shown. */
+public interface EditablePart {
+       public void startEditing();
+
+       public void stopEditing();
+}
index b614a14cbb0702ef2723bd11e12ee0943adcb2d1..fa8062f7c12ea4fa7c596df69ffb89541112444d 100644 (file)
@@ -100,6 +100,18 @@ public abstract class AbstractContent extends AbstractMap<QName, Object> impleme
                collectAncestors(ancestors, content.getParent());
        }
 
+       @Override
+       public int getDepth() {
+               List<Content> ancestors = new ArrayList<>();
+               collectAncestors(ancestors, this);
+               return ancestors.size();
+       }
+       
+       @Override
+       public String getSessionLocalId() {
+               return getPath();
+       }
+
        /*
         * UTILITIES
         */