X-Git-Url: http://git.argeo.org/?a=blobdiff_plain;f=org.argeo.cms%2Fsrc%2Forg%2Fargeo%2Fcms%2Facr%2FContentUtils.java;h=facb5933bf3ebe743ce99eeed8e57c300d0cb11b;hb=091d43fef5f8e88c7081340138eb1bb33c5862a2;hp=17144cfdb660cfceaf84c54016b46b6f44c1ac8e;hpb=c0342975a37c70895c2e8f6b341d790700168d7f;p=lgpl%2Fargeo-commons.git diff --git a/org.argeo.cms/src/org/argeo/cms/acr/ContentUtils.java b/org.argeo.cms/src/org/argeo/cms/acr/ContentUtils.java index 17144cfdb..facb5933b 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/ContentUtils.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/ContentUtils.java @@ -1,9 +1,13 @@ package org.argeo.cms.acr; import java.io.PrintStream; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.StringJoiner; +import java.util.StringTokenizer; import java.util.function.BiConsumer; import javax.security.auth.login.LoginContext; @@ -13,11 +17,15 @@ import javax.xml.namespace.QName; import org.argeo.api.acr.Content; import org.argeo.api.acr.ContentRepository; import org.argeo.api.acr.ContentSession; -import org.argeo.api.acr.CrName; +import org.argeo.api.acr.DName; import org.argeo.api.cms.CmsAuth; -import org.argeo.cms.CmsUserManager; -import org.argeo.osgi.useradmin.UserDirectory; -import org.argeo.util.CurrentSubject; +import org.argeo.api.cms.CmsConstants; +import org.argeo.api.cms.CmsSession; +import org.argeo.api.cms.directory.CmsDirectory; +import org.argeo.api.cms.directory.CmsUserManager; +import org.argeo.api.cms.directory.HierarchyUnit; +import org.argeo.api.cms.directory.UserDirectory; +import org.argeo.cms.util.CurrentSubject; import org.osgi.service.useradmin.Role; /** Utilities and routines around {@link Content}. */ @@ -63,7 +71,8 @@ public class ContentUtils { // } public static final char SLASH = '/'; - public static final String ROOT_SLASH = "" + SLASH; + public static final String SLASH_STRING = Character.toString(SLASH); + public static final String EMPTY = ""; /** * Split a path (with '/' separator) in an array of length 2, the first part @@ -83,7 +92,7 @@ public class ContentUtils { } if (parentIndex == -1) // no '/' - return new String[] { "", path }; + return new String[] { EMPTY, path }; return new String[] { parentIndex != 0 ? path.substring(0, parentIndex) : "" + SLASH, path.substring(parentIndex + 1) }; @@ -98,7 +107,7 @@ public class ContentUtils { public static List toPathSegments(String path) { List res = new ArrayList<>(); - if ("".equals(path) || ROOT_SLASH.equals(path)) + if (EMPTY.equals(path) || Content.ROOT_PATH.equals(path)) return res; collectPathSegments(path, res); return res; @@ -106,10 +115,10 @@ public class ContentUtils { private static void collectPathSegments(String path, List segments) { String[] parent = getParentPath(path); - if ("".equals(parent[1])) // root + if (EMPTY.equals(parent[1])) // root return; segments.add(0, parent[1]); - if ("".equals(parent[0])) // end + if (EMPTY.equals(parent[0])) // end return; collectPathSegments(parent[0], segments); } @@ -119,14 +128,49 @@ public class ContentUtils { throw new IllegalArgumentException("Path " + path + " contains //"); } + /** The last element of a path. */ + public static String lastPathElement(String path) { + if (path.charAt(path.length() - 1) == '/') + throw new IllegalArgumentException("Path " + path + " cannot end with '/'"); + int index = path.lastIndexOf('/'); + if (index < 0) + return path; + return path.substring(index + 1); + } + + /* + * DIRECTORY + */ + public static Content roleToContent(CmsUserManager userManager, ContentSession contentSession, Role role) { UserDirectory userDirectory = userManager.getDirectory(role); - String path = CmsContentRepository.DIRECTORY_BASE + SLASH + userDirectory.getName() + SLASH - + userDirectory.getRolePath(role); + String path = directoryPath(userDirectory) + userDirectory.getRolePath(role); Content content = contentSession.get(path); return content; } + public static Content hierarchyUnitToContent(ContentSession contentSession, HierarchyUnit hierarchyUnit) { + CmsDirectory directory = hierarchyUnit.getDirectory(); + StringJoiner relativePath = new StringJoiner(SLASH_STRING); + buildHierarchyUnitPath(hierarchyUnit, relativePath); + String path = directoryPath(directory) + relativePath.toString(); + Content content = contentSession.get(path); + return content; + } + + /** The path to this {@link CmsDirectory}. Ends with a /. */ + private static String directoryPath(CmsDirectory directory) { + return CmsContentRepository.DIRECTORY_BASE + SLASH + directory.getName() + SLASH; + } + + /** Recursively build a relative path of a {@link HierarchyUnit}. */ + private static void buildHierarchyUnitPath(HierarchyUnit current, StringJoiner relativePath) { + if (current.getParent() == null) // directory + return; + buildHierarchyUnitPath(current.getParent(), relativePath); + relativePath.add(current.getHierarchyUnitName()); + } + /* * CONSUMER UTILS */ @@ -134,7 +178,7 @@ public class ContentUtils { public static Content createCollections(ContentSession session, String path) { if (session.exists(path)) { Content content = session.get(path); - if (!content.isContentClass(CrName.collection.qName())) { + if (!content.isContentClass(DName.collection.qName())) { throw new IllegalStateException("Content " + path + " already exists, but is not a collection"); } else { return content; @@ -142,12 +186,12 @@ public class ContentUtils { } else { String[] parentPath = getParentPath(path); Content parent = createCollections(session, parentPath[0]); - Content content = parent.add(parentPath[1], CrName.collection.qName()); + Content content = parent.add(parentPath[1], DName.collection.qName()); return content; } } - public static ContentSession openDataAdminSession(ContentRepository repository) { + public static ContentSession openDataAdminSession(ContentRepository contentRepository) { LoginContext loginContext; try { loginContext = CmsAuth.DATA_ADMIN.newLoginContext(); @@ -160,12 +204,60 @@ public class ContentUtils { ClassLoader currentCl = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader(ContentUtils.class.getClassLoader()); - return CurrentSubject.callAs(loginContext.getSubject(), () -> repository.get()); + return CurrentSubject.callAs(loginContext.getSubject(), () -> contentRepository.get()); } finally { Thread.currentThread().setContextClassLoader(currentCl); } } + public static ContentSession openSession(ContentRepository contentRepository, CmsSession cmsSession) { + return CurrentSubject.callAs(cmsSession.getSubject(), () -> contentRepository.get()); + } + + /** + * Constructs a relative path between a base path and a given path. + * + * @throws IllegalArgumentException if the base path is not an ancestor of the + * path + */ + public static String relativize(String basePath, String path) throws IllegalArgumentException { + Objects.requireNonNull(basePath); + Objects.requireNonNull(path); + if (!path.startsWith(basePath)) + throw new IllegalArgumentException(basePath + " is not an ancestor of " + path); + String relativePath = path.substring(basePath.length()); + if (relativePath.length() > 0 && relativePath.charAt(0) == '/') + relativePath = relativePath.substring(1); + return relativePath; + } + + /** A path in the node repository */ + public static String getDataPath(Content node) { + // TODO make it more configurable? + StringBuilder buf = new StringBuilder(CmsConstants.PATH_API_ACR); + buf.append(node.getPath()); + return buf.toString(); + } + + /** A path in the node repository */ + public static String getDataPathForUrl(Content 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(); + } + /** Singleton. */ private ContentUtils() {