Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*************************************************************************
*
* ADOBE CONFIDENTIAL
* __________________
*
* Copyright 2013 Adobe Systems Incorporated
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Adobe Systems Incorporated and its suppliers,
* if any. The intellectual and technical concepts contained
* herein are proprietary to Adobe Systems Incorporated and its
* suppliers and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Adobe Systems Incorporated.
**************************************************************************/
package com.adobe.granite.comments;
import java.io.InputStream;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.nodetype.NodeType;
import org.apache.commons.collections.Predicate;
import org.apache.commons.collections.iterators.FilterIterator;
import org.apache.commons.lang.StringUtils;
import org.apache.jackrabbit.JcrConstants;
import org.apache.jackrabbit.commons.JcrUtils;
import org.apache.jackrabbit.util.Text;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceUtil;
import org.osgi.annotation.versioning.ConsumerType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.adobe.granite.comments.internal.Util;
/**
* The AbstractCommentingProvider provides a default implementation for storing {@link Comment}s and {@link
* CommentCollection}s. {@link CommentingProvider}s are recommended to extend this abstract implementation and override
* where necessary.
*/
@ConsumerType
public abstract class AbstractCommentingProvider implements CommentingProvider {
private final Logger log = LoggerFactory.getLogger(AbstractCommentingProvider.class);
private final Predicate COMMENT_RESOURCE_TYPE_PREDICATE = new Predicate() {
@Override
public boolean evaluate(Object o) {
final Resource resource = (Resource) o;
return ResourceUtil.isA(resource, getCommentResourceType());
}
};
/**
* The name of the node holding comment collections below a target.
*/
public static final String RELATIVE_TARGET_ROOT = "comments";
/**
* The name of the property holding optional annotation data
*/
public static final String PN_ANNOTATIONDATA = "annotationData";
/**
* The property containing the optional creator of a comment
*/
public static final String PN_AUTHOR = "author";
/**
* The name of the property holding a comment's message ("comment").
*/
public static final String PN_MESSAGE = "jcr:description";
public static final String JCR_CREATED_BY = "jcr:createdBy";
public static final String SLING_RESOURCE_TYPE = "sling:resourceType";
/**
* Create the root of a {@link CommentCollection} for the given target {@link Resource}. Node creation may be
* overridden via {@link #createCollectionNode(String, javax.jcr.Session)}. If not already present on the node, this
* method also will set the {@link #JCR_CREATED_BY} and {@link JcrConstants#JCR_CREATED} properties. The last
* modified mixin will maintain the last modified date. Custom properties may be set on the collection node via
* {@link #customizeCollectionNode(Resource, javax.jcr.Node)}.
*
* @param target The target resource for which to create a collection root.
*
* @return The newly created resource representing the collection root.
*
* @throws CommentException If a collection already exists for the given target, or upon encountering an error
* writing to the repository.
*/
public final Resource createCollectionResource(final Resource target) {
final String rootPath = getCollectionResourcePath(target);
final Session session = target.getResourceResolver().adaptTo(Session.class);
try {
if (session.itemExists(rootPath)) {
throw new CommentException("Collection already exists: " + rootPath);
}
final Node collectionRoot = createCollectionNode(rootPath, session);
if (StringUtils.isNotBlank(getCollectionResourceType())) {
collectionRoot.setProperty(SLING_RESOURCE_TYPE, getCollectionResourceType());
}
collectionRoot.addMixin(NodeType.MIX_LAST_MODIFIED);
// these properties may already have been defined by a node type used in #createCollectionNode
if (!collectionRoot.hasProperty(JcrConstants.JCR_CREATED)) {
collectionRoot.setProperty(JcrConstants.JCR_CREATED, Calendar.getInstance());
}
if (!collectionRoot.hasProperty(JCR_CREATED_BY)) {
collectionRoot.setProperty(JCR_CREATED_BY, session.getUserID());
}
customizeCollectionNode(target, collectionRoot);
session.save();
return target.getResourceResolver().getResource(collectionRoot.getPath());
} catch (RepositoryException e) {
log.error("error while creating collection root for target [{}]: ", target.getPath(), e);
throw new CommentException("Could not create collection root for target: " + target.getPath(), e);
}
}
/**
* Add a {@link Comment} resource to the given {@link CommentCollection} resource, set the given
* message on the comment. Comment node creation may be overridden via {@link
* #createCommentNode(String, javax.jcr.Node, javax.jcr.Session)}. This method will forcibly set a {@link
* #SLING_RESOURCE_TYPE} property (if defined by {@link #getCommentResourceType()} ()}), the {@link #PN_MESSAGE}
* property. If not already present on the node, this method also will set the {@link #JCR_CREATED_BY} and {@link
* JcrConstants#JCR_CREATED} properties. Custom properties may be set on the comment resource via {@link
* #customizeCommentNode(Resource, javax.jcr.Node)} (javax.jcr.Node)}.
*
* @param collectionResource The collection within which to create the comment.
* @param message The message to store in the comment.
* @param author The author to store in the comment.
* @param annotationData The annotation data to store in the comment.
*
* @return The newly created comment {@link Resource}.
*
* @throws CommentException Upon encountering an error writing to the repository.
*/
public final Resource createCommentResource(final Resource collectionResource,
final String message,
final String author,
final String annotationData) {
try {
final ResourceResolver resolver = collectionResource.getResourceResolver();
final Session session = resolver.adaptTo(Session.class);
final Node collectionNode = session.getNode(collectionResource.getPath());
final String commentPath = getCommentResourcePath(collectionResource, message);
final Node commentNode = createCommentNode(commentPath, collectionNode, session);
if (StringUtils.isNotBlank(getCommentResourceType())) {
commentNode.setProperty(SLING_RESOURCE_TYPE, getCommentResourceType());
}
commentNode.setProperty(PN_MESSAGE, message);
customizeCommentNode(collectionResource, commentNode);
final Calendar now = Calendar.getInstance();
commentNode.addMixin(NodeType.MIX_LAST_MODIFIED);
if (!commentNode.hasProperty(JcrConstants.JCR_CREATED)) {
commentNode.setProperty(JcrConstants.JCR_CREATED, now);
}
if (!commentNode.hasProperty(JCR_CREATED_BY)) {
commentNode.setProperty(JCR_CREATED_BY, session.getUserID());
}
if (StringUtils.isNotBlank(author)) {
commentNode.setProperty(PN_AUTHOR, author);
}
if (StringUtils.isNotBlank(annotationData)) {
commentNode.setProperty(PN_ANNOTATIONDATA, annotationData);
}
collectionNode.setProperty(JcrConstants.JCR_LASTMODIFIED, now);
session.save();
return resolver.getResource(commentNode.getPath());
} catch (RepositoryException e) {
log.error("error while creating comment for collection [{}]: ", collectionResource.getPath(), e);
throw new CommentException("Could not x for collection: " + collectionResource.getPath(), e);
}
}
/**
* Returns an {@link Iterator} of {@link Resource}s, with each resource representing a {@link Comment} of the given
* {@link CommentCollection}.
*
* @param collectionResource The {@link CommentCollection} for which to retrieve the comment resources.
*
* @return The iterator containing the comment resources, or an empty iterator if no comments are present.
*
* @throws CommentException Upon encountering an error retrieving the comments.
*/
public Iterator getCommentResources(Resource collectionResource) {
final Iterator children = collectionResource.listChildren();
return new FilterIterator(children, COMMENT_RESOURCE_TYPE_PREDICATE);
}
/**
* Remove (delete) the given {@link Comment} from the repository.
*
* @param resource The comment resource to delete.
*
* @throws CommentException Upon encountering an error writing to the repository.
*/
public void removeCommentResource(final Resource resource) {
try {
final Session session = resource.getResourceResolver().adaptTo(Session.class);
final Node node = session.getNode(resource.getPath());
node.remove();
session.save();
} catch (RepositoryException e) {
log.error("error while removing comment [{}]: ", resource.getPath(), e);
throw new CommentException("Could not remove comment: " + resource.getPath(), e);
}
}
/**
* Remove (delete) the given {@link CommentCollection} from the repository.
*
* @param resource The collection to delete.
*
* @throws CommentException Upon encountering an error writing to the repository.
*/
public void removeCollectionResource(Resource resource) {
try {
final ResourceResolver resolver = resource.getResourceResolver();
if (null != resolver.getResource(resource.getPath())) {
final Session session = resolver.adaptTo(Session.class);
session.removeItem(resource.getPath());
session.save();
}
} catch (RepositoryException e) {
log.error("error while removing collection [{}]: ", resource.getPath(), e);
throw new CommentException("Could not remove collection: " + resource.getPath(), e);
}
}
/**
* Add an attachment to the given {@link Comment}. The attachment is represented by the given name
* (file name), an {@link InputStream} and the mime type, all of which are mandatory parameters. The relative path
* with which the attachment is created can be customized via {@link #getAttachmentResourcePath(String)}, also
* arbitrary properties can be set on the attachment's content node via {@link #customizeAttachmentNode(Resource,
* javax.jcr.Node)}.
*
* @param commentResource The comment to which to add the attachment.
* @param name The name (file name) for the attachment.
* @param inputStream The input stream containing the file data of the attachment.
* @param mimeType The mime type of the attachment data.
*
* @return The newly created attachment resource.
*
* @throws CommentException Upon encountering an error writing to the repository.
*/
public final Resource createAttachmentResource(Resource commentResource,
String name,
InputStream inputStream,
String mimeType) {
try {
final ResourceResolver resolver = commentResource.getResourceResolver();
final Session session = resolver.adaptTo(Session.class);
final Calendar time = Calendar.getInstance();
final Node node = session.getNode(commentResource.getPath());
final Node attachment = JcrUtils.getOrCreateUniqueByPath(node,
getAttachmentResourcePath(name),
JcrConstants.NT_FILE);
final Node content = attachment.addNode(JcrConstants.JCR_CONTENT, JcrConstants.NT_UNSTRUCTURED);
content.setProperty(JcrConstants.JCR_CREATED, time);
content.setProperty(JCR_CREATED_BY, node.getSession().getUserID());
content.setProperty(JcrConstants.JCR_LASTMODIFIED, time);
content.setProperty(JcrConstants.JCR_MIMETYPE, mimeType);
content.setProperty(JcrConstants.JCR_DATA, node.getSession().getValueFactory().createBinary(inputStream));
customizeAttachmentNode(commentResource, content);
session.save();
return resolver.getResource(attachment.getPath());
} catch (RepositoryException e) {
log.error("error while creating attachment for comment [{}]: ", commentResource.getPath(), e);
throw new CommentException("Could not create attachment for comment: " + commentResource.getPath(),
e);
}
}
/**
* Return the attachment of the given {@link Comment} as identified by its given name (file name).
*
* @param commentResource The comment from which to get the attachment.
* @param name The name of the attachment.
*
* @return The {@link Resource} representing the attachment, or null if no attachment with the given
* name was found.
*
* @throws CommentException Upon encountering an error retrieving the attachment.
*/
public Resource getAttachmentResource(Resource commentResource, String name) {
final Resource child = commentResource.getChild(getAttachmentResourcePath(name));
return (ResourceUtil.isA(child, JcrConstants.NT_FILE)) ? child : null;
}
/**
* Remove (delete) the attachment identified by the given name from the given {@link Comment}.
*
* @param commentResource The comment from which to remove the attachment.
* @param name The name of the attachment to remove.
*
* @throws CommentException Upon encountering an error writing to the repository.
*/
public void removeAttachmentResource(Resource commentResource, String name) {
try {
final Resource attachment = commentResource.getChild(getAttachmentResourcePath(name));
if (null != attachment) {
final Session session = attachment.getResourceResolver().adaptTo(Session.class);
session.removeItem(attachment.getPath());
session.save();
}
} catch (RepositoryException e) {
log.error("error while removing attachment from comment [{}]: ", commentResource.getPath(), e);
throw new CommentException("Could not remove attachment from comment: " + commentResource.getPath(),
e);
}
}
/**
* Return a {@link Map} containing the given {@link Comment}s attachments. The map key is the attachment's name
* (file name), the map value holds the {@link Resource} representing the attachment.
*
* @param commentResource The comment for which to retrieve the attachments.
*
* @return The map of attachments, or an empty map if no attachments are present.
*
* @throws CommentException Upon encountering an error retrieving the attachments.
*/
public final Map getAttachmentMap(Resource commentResource) throws CommentException {
final Map attachments = new HashMap();
final Iterator iterator = getAttachments(commentResource);
while (iterator.hasNext()) {
Resource child = iterator.next();
if (ResourceUtil.isA(child, JcrConstants.NT_FILE)) {
attachments.put(child.getName(), child);
}
}
return attachments;
}
/**
* Returns the {@link Resource} representing the root of the collection specific to the given target,
* or null if no collection exists for this target.
*
* @param target The target resource for which to retrieve the collection root.
*
* @return The resource repesenting the collection, or null if none is present.
*
* @throws CommentException Upon encountering an error retrieving the collection root.
*/
protected final Resource getCollectionResource(Resource target) throws CommentException {
return target.getResourceResolver().getResource(getCollectionResourcePath(target));
}
/**
* Creates the node of the collection. This method is called by {@link #createCollectionResource(org.apache.sling.api.resource.Resource)}
* while creating the resource and allows for customization of creation of the node that serves as the bases for the
* resource representing this collection. {@link #createCollectionResource(org.apache.sling.api.resource.Resource)}
* Note that {@link #createCollectionResource(org.apache.sling.api.resource.Resource)} will set mandatory properties
* of its own and that this method should not set any properties on its own, but rather later via {@link
* #customizeCollectionNode(Resource, javax.jcr.Node)}. Overriding implementations can use the session to create a
* node. The save-call is done by the calling method and is not required here.
*
* @param rootPath The complete path of the node to be created (as defined by {@link
* #getCollectionResourcePath(org.apache.sling.api.resource.Resource)}.
* @param session The session to write to the repository with.
*
* @return The newly created node representing the collection.
*/
protected Node createCollectionNode(final String rootPath, final Session session) {
try {
Node root = null;
String path = rootPath;
while (root == null && StringUtils.isNotBlank(path)) {
path = Text.getRelativeParent(path, 1);
if (session.nodeExists(path)) {
root = session.getNode(path);
}
}
if (root != null) {
String relPath = StringUtils.removeStart(rootPath, path + '/');
return JcrUtils.getOrCreateByPath(root, relPath, false, JcrConstants.NT_UNSTRUCTURED, JcrConstants.NT_UNSTRUCTURED, false);
} else {
throw new CommentException("Unable to access parent nodes of new collection");
}
} catch (RepositoryException e) {
throw new CommentException("Error creating collection root at: " + rootPath, e);
}
}
/**
* Creates the node of the comment below the given collectionNode. This method is called by {@link
* #createCommentResource(Resource, String, String, String)} while creating the resource and allows for
* customization of creation of the node that serves as the bases for the resource representing the comment.
* Overriding implementations can use the session to create a node. The save-call is done by the calling method and
* is not required here.
*
* @param commentPath The relative path of the node to be created (as defined by {@link
* #getCommentResourcePath(org.apache.sling.api.resource.Resource, String)}.
* @param collectionNode The node of the comment's collection node.
* @param session The session to write to the repository with.
*
* @return The newly created node representing the collection.
*/
protected Node createCommentNode(final String commentPath, final Node collectionNode, final Session session) {
try {
return JcrUtils.getOrCreateUniqueByPath(collectionNode, commentPath, JcrConstants.NT_UNSTRUCTURED);
} catch (RepositoryException e) {
throw new CommentException("Error creating comment node at: " + commentPath, e);
}
}
/**
* Allows customization of the given collection {@link Node}, by e.g. setting arbitrary properties. Overriding
* implementations need not save the session, this is done in the calling {@link
* #createCollectionResource(org.apache.sling.api.resource.Resource)} (org.apache.sling.api.resource.Resource,
* String)}. The default implementation does nothing.
*
* @param target The resource representing the target of the collection.
* @param collectionNode The node representing the collection.
*/
protected void customizeCollectionNode(final Resource target, final Node collectionNode) {
}
/**
* Allows customization of the given comment {@link Node}, by e.g. setting arbitrary properties. Overriding
* implementations need not save the session, this is done in the calling {@link #createCommentResource(Resource,
* String, String, String)} (org.apache.sling.api.resource.Resource, String)}. The default implementation does
* nothing.
*
* @param collectionResource The resource representing the collection.
* @param commentNode The node representing the comment.
*/
protected void customizeCommentNode(final Resource collectionResource, final Node commentNode) {
}
/**
* Allows customization of the given attachment content {@link Node}, by e.g. setting arbitrary properties.
* Overriding implementations need not save the session, this is done in the calling {@link
* #createCommentResource(Resource, String, String, String)} (org.apache.sling.api.resource.Resource, String)}. The
* default implementation does nothing.
*
* @param commentResource The resource representing the comment this attachment belongs to.
* @param attachmentContentNode The node representing the attachment content.
*/
protected void customizeAttachmentNode(final Resource commentResource, final Node attachmentContentNode) {
}
/**
* Calculates the path of the root of a {@link CommentCollection}. This may be overriden. The default implementation
* appends "jcr:content" (if the given target has such a child node) and a "/" and {@link #RELATIVE_TARGET_ROOT} to
* the path of the target.
*
* @param target The target for which to calculate the path of the collection.
*
* @return The path.
*/
protected String getCollectionResourcePath(final Resource target) {
final Resource contentResource = target.getChild(JcrConstants.JCR_CONTENT);
final String path = (null != contentResource) ? contentResource.getPath() : target.getPath();
return path + "/" + RELATIVE_TARGET_ROOT;
}
/**
* Calculates the relative path of a comment below a collection. The default simply transform the given
* message into a valid node name and returns it.
*
* @param collectionResource The collection within which the comment belongs.
* @param message The comment message.
*
* @return The path.
*/
protected String getCommentResourcePath(final Resource collectionResource, final String message) {
return Util.createValidName(message);
}
/**
* Calculates the relative path of an attachment, based on the given name, below a comment. The default simply
* returns the given name.
*
* @param name The attachment name (e.g. filename)
*
* @return The path.
*/
protected String getAttachmentResourcePath(final String name) {
return name;
}
/**
* Provides an iterator of resources representing attachments of a given comment (resource). The default simply
* lists the children of the given comment resource.
*
* @param commentResource The resource representing the comment to which the attachment belongs.
*
* @return The iterator of attachment resources.
*/
protected Iterator getAttachments(final Resource commentResource) {
return commentResource.listChildren();
}
/**
* Returns an optional resource type to set for newly created collections.
*
* @return The resource type or null if none shall be set.
*/
public abstract String getCollectionResourceType();
/**
* Returns an optional resource type to set for newly created comments.
*
* @return The resource type or null if none shall be set.
*/
public abstract String getCommentResourceType();
}