/*
* EDITION
*/
+ /** Switch this content (and its subtree) to editing mode. */
+ default void openForEdit(ProvidedSession session, String relativePath) {
+ throw new UnsupportedOperationException();
+ }
+
+ /** Switch this content (and its subtree) to frozen mode. */
+ default void freeze(ProvidedSession session, String relativePath) {
+ throw new UnsupportedOperationException();
+ }
+
+ /** Whether this content (and its subtree) are in editing mode. */
+ default boolean isOpenForEdit(ProvidedSession session, String relativePath) {
+ throw new UnsupportedOperationException();
+ }
+
/**
* Called when an edition cycle is completed. Does nothing by default.
*
public interface ProvidedContent extends Content {
final static String ROOT_PATH = "/";
+ /** The related {@link ProvidedSession}. */
ProvidedSession getSession();
+ /** The {@link ContentProvider} this {@link Content} belongs to. */
ContentProvider getProvider();
+ /** Depth relative to the root of the repository. */
int getDepth();
/**
*/
String getSessionLocalId();
+ /**
+ * The {@link Content} within the same {@link ContentProvider} which can be used
+ * to mount another {@link ContentProvider}.
+ */
default ProvidedContent getMountPoint(String relativePath) {
throw new UnsupportedOperationException("This content doe not support mount");
}
+ @Override
default ProvidedContent getContent(String path) {
Content fileNode;
if (path.startsWith(ROOT_PATH)) {// absolute
return true;
}
+ /** Whether the related session can open this content for edit. */
+ default boolean canEdit() {
+ return false;
+ }
}
void notifyModification(ProvidedContent content);
- UUID getUuid();
+ UUID uuid();
// Content getSessionRunDir();
final static String SESSION_UUID = "entryUUID";
final static String SESSION_LOCAL_ID = "uniqueIdentifier";
- UUID getUuid();
+ UUID uuid();
String getUserRole();
--- /dev/null
+package org.argeo.api.uuid;
+
+import java.util.UUID;
+
+/**
+ * An object identified by a {@link UUID}. Typically used to fasten indexing and
+ * comparisons of objects or records. THe method to implement is {@link #uuid()}
+ * so that any record with an <code>uuid</code> field can easily be enriched
+ * with this interface.
+ */
+public interface UuidIdentified {
+ /** The UUID identifier. */
+ UUID uuid();
+
+ /** The UUID identifier, for compatibility with beans accessors. */
+ default UUID getUuid() {
+ return uuid();
+ }
+
+ /**
+ * Helper to implement the equals method of an {@link UuidIdentified}.<br/>
+ *
+ * <pre>
+ * @Override
+ * public boolean equals(Object o) {
+ * return UuidIdentified.equals(this, o);
+ * }
+ * </pre>
+ */
+ static boolean equals(UuidIdentified uuidIdentified, Object o) {
+ assert uuidIdentified != null;
+ if (o == null)
+ return false;
+ if (uuidIdentified == o)
+ return true;
+ if (o instanceof UuidIdentified u)
+ return uuidIdentified.uuid().equals(u.uuid());
+ else
+ return false;
+ }
+
+ /**
+ * Helper to implement the hash code method of an {@link UuidIdentified}.<br/>
+ *
+ * <pre>
+ * @Override
+ * public int hashCode() {
+ * return UuidIdentified.hashCode(this);
+ * }
+ * </pre>
+ */
+ static int hashCode(UuidIdentified uuidIdentified) {
+ assert uuidIdentified != null;
+ return uuidIdentified.getUuid().hashCode();
+ }
+
+}
import org.argeo.api.cms.ux.CmsEditionEvent;
import org.argeo.api.cms.ux.CmsEditionListener;
+/**
+ * Base class for implementing {@link CmsEditable}, mostly managing
+ * {@link CmsEditionListener}s.
+ */
public abstract class AbstractCmsEditable implements CmsEditable {
private IdentityHashMap<CmsEditionListener, Object> listeners = new IdentityHashMap<>();
+ /** Notifies listeners of a {@link CmsEditionEvent}. */
protected void notifyListeners(CmsEditionEvent e) {
if (CmsEditionEvent.START_EDITING == e.getType()) {
for (CmsEditionListener listener : listeners.keySet())
--- /dev/null
+package org.argeo.cms.ux.acr;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.spi.ContentProvider;
+import org.argeo.api.acr.spi.ProvidedContent;
+import org.argeo.api.acr.spi.ProvidedSession;
+import org.argeo.api.cms.ux.CmsEditable;
+import org.argeo.cms.acr.ContentUtils;
+import org.argeo.cms.ux.AbstractCmsEditable;
+
+/** {@link CmsEditable} semantics for a {@link Content}. */
+public class ContentCmsEditable extends AbstractCmsEditable {
+
+ private final boolean canEdit;
+ /** The path of this content, relative to its content provider. */
+ private final String relativePath;
+ private final ProvidedSession session;
+ private final ContentProvider provider;
+
+ public ContentCmsEditable(Content content) {
+ ProvidedContent providedContent = (ProvidedContent) content;
+ canEdit = providedContent.canEdit();
+ session = providedContent.getSession();
+ provider = providedContent.getProvider();
+ relativePath = ContentUtils.relativize(provider.getMountPath(), content.getPath());
+ }
+
+ @Override
+ public Boolean canEdit() {
+ return canEdit;
+ }
+
+ @Override
+ public Boolean isEditing() {
+ return provider.isOpenForEdit(session, relativePath);
+ }
+
+ @Override
+ public void startEditing() {
+ provider.openForEdit(session, relativePath);
+ }
+
+ @Override
+ public void stopEditing() {
+ provider.freeze(session, relativePath);
+ }
+
+}
CmsSession cmsSession = CurrentUser.getCmsSession();
CmsContentSession contentSession = userSessions.get(cmsSession);
if (contentSession == null) {
- final CmsContentSession newContentSession = new CmsContentSession(this, cmsSession.getUuid(),
+ final CmsContentSession newContentSession = new CmsContentSession(this, cmsSession.uuid(),
cmsSession.getSubject(), locale, uuidFactory);
cmsSession.addOnCloseCallback((c) -> {
newContentSession.close();
import org.argeo.api.acr.spi.ProvidedRepository;
import org.argeo.api.acr.spi.ProvidedSession;
import org.argeo.api.uuid.UuidFactory;
+import org.argeo.api.uuid.UuidIdentified;
import org.argeo.cms.CurrentUser;
/** Implements {@link ProvidedSession}. */
-class CmsContentSession implements ProvidedSession {
+class CmsContentSession implements ProvidedSession, UuidIdentified {
final private AbstractContentRepository contentRepository;
private final UUID uuid;
throw new IllegalArgumentException(path + " is not an absolute path");
ContentProvider contentProvider = contentRepository.getMountManager().findContentProvider(path);
String mountPath = contentProvider.getMountPath();
- String relativePath = extractRelativePath(mountPath, path);
+ String relativePath = ContentUtils.relativize(mountPath, path);
ProvidedContent content = contentProvider.get(CmsContentSession.this, relativePath);
return content;
}
throw new IllegalArgumentException(path + " is not an absolute path");
ContentProvider contentProvider = contentRepository.getMountManager().findContentProvider(path);
String mountPath = contentProvider.getMountPath();
- String relativePath = extractRelativePath(mountPath, path);
+ String relativePath = ContentUtils.relativize(mountPath, path);
return contentProvider.exists(this, relativePath);
}
- private String extractRelativePath(String mountPath, String path) {
- String relativePath = path.substring(mountPath.length());
- if (relativePath.length() > 0 && relativePath.charAt(0) == '/')
- relativePath = relativePath.substring(1);
- return relativePath;
- }
-
@Override
public Subject getSubject() {
return subject;
}
@Override
- public UUID getUuid() {
+ public UUID uuid() {
return uuid;
}
return sessionRunDir;
}
+ /*
+ * OBJECT METHODS
+ */
+
@Override
public boolean equals(Object o) {
- if (o instanceof CmsContentSession session)
- return uuid.equals(session.uuid);
- return false;
+ return UuidIdentified.equals(this, o);
+ }
+
+ @Override
+ public int hashCode() {
+ return UuidIdentified.hashCode(this);
}
@Override
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.StringJoiner;
import java.util.function.BiConsumer;
}
}
+ /**
+ * 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;
+ }
+
/** Singleton. */
private ContentUtils() {
import org.argeo.api.cms.CmsConstants;
import org.argeo.api.cms.CmsLog;
import org.argeo.api.cms.CmsSession;
+import org.argeo.api.uuid.UuidIdentified;
import org.argeo.cms.internal.runtime.CmsContextImpl;
import org.osgi.service.useradmin.Authorization;
/** Default CMS session implementation. */
-public class CmsSessionImpl implements CmsSession, Serializable {
+public class CmsSessionImpl implements CmsSession, Serializable, UuidIdentified {
private static final long serialVersionUID = 1867719354246307225L;
private final static CmsLog log = CmsLog.getLog(CmsSessionImpl.class);
}
@Override
- public UUID getUuid() {
+ public UUID uuid() {
return uuid;
}
views.put(uid, view);
}
+ /*
+ * OBJECT METHODS
+ */
+
+ @Override
+ public boolean equals(Object o) {
+ return UuidIdentified.equals(this, o);
+ }
+
+ @Override
+ public int hashCode() {
+ return UuidIdentified.hashCode(this);
+ }
+
+ @Override
public String toString() {
return "CMS Session " + userDn + " localId=" + localSessionId + ", uuid=" + uuid;
}