com.day.cq.dam.commons.util.DamUtil Maven / Gradle / Ivy
Show all versions of aem-sdk-api Show documentation
/*
* Copyright 1997-2008 Day Management AG
* Barfuesserplatz 6, 4001 Basel, Switzerland
* All Rights Reserved.
*
* This software is the confidential and proprietary information of
* Day Management AG, ("Confidential Information"). You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with Day.
*/
package com.day.cq.dam.commons.util;
import java.awt.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nonnull;
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
import javax.jcr.ValueFactory;
import javax.jcr.query.Query;
import javax.jcr.query.QueryManager;
import javax.jcr.query.QueryResult;
import javax.jcr.query.Row;
import javax.jcr.query.RowIterator;
import javax.jcr.query.qom.Constraint;
import javax.jcr.query.qom.DynamicOperand;
import javax.jcr.query.qom.JoinCondition;
import javax.jcr.query.qom.QueryObjectModel;
import javax.jcr.query.qom.QueryObjectModelConstants;
import javax.jcr.query.qom.QueryObjectModelFactory;
import javax.jcr.query.qom.Source;
import javax.jcr.query.qom.StaticOperand;
import javax.jcr.security.Privilege;
import org.apache.commons.imaging.ImageReadException;
import org.apache.commons.imaging.Imaging;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.jackrabbit.api.security.JackrabbitAccessControlManager;
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.api.security.user.User;
import org.apache.jackrabbit.api.security.user.UserManager;
import org.apache.jackrabbit.commons.JcrUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceUtil;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.api.scripting.SlingScriptHelper;
import org.apache.sling.resource.collection.ResourceCollection;
import org.apache.sling.tenant.Tenant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.adobe.cq.dam.cfm.ContentFragment;
import com.adobe.internal.pdftoolkit.core.util.Utility;
import com.day.cq.commons.jcr.JcrConstants;
import com.day.cq.commons.jcr.JcrUtil;
import com.day.cq.dam.api.Asset;
import com.day.cq.dam.api.DamConstants;
import com.day.cq.dam.api.Rendition;
import com.day.cq.dam.api.thumbnail.ThumbnailConfig;
import com.day.cq.dam.commons.util.impl.AssetCacheImpl;
import com.day.cq.replication.Agent;
import com.day.cq.replication.AgentManager;
import com.day.cq.replication.ReplicationAction;
import com.day.cq.replication.ReplicationQueue;
import static com.day.cq.commons.jcr.JcrConstants.JCR_CONTENT;
import static com.day.cq.commons.jcr.JcrConstants.NT_FILE;
import static com.day.cq.dam.api.DamConstants.COLLECTION_SLING_RES_TYPE;
import static com.day.cq.dam.api.DamConstants.METADATA_FOLDER;
import static com.day.cq.dam.api.DamConstants.MOUNTPOINT_ASSETS;
import static com.day.cq.dam.api.DamConstants.MOUNTPOINT_BINARIES;
import static com.day.cq.dam.api.DamConstants.NT_DAM_ASSET;
import static com.day.cq.dam.api.DamConstants.ORIGINAL_FILE;
import static com.day.cq.dam.api.DamConstants.PREFIX_ASSET_THUMBNAIL;
import static com.day.cq.dam.api.DamConstants.RENDITIONS_FOLDER;
import static com.day.cq.dam.api.DamConstants.SMART_COLLECTION_SLING_RES_TYPE;
import static com.day.cq.dam.api.DamConstants.SUBASSETS_FOLDER;
import static com.day.cq.dam.commons.util.DamConfigurationConstants.DAM_ASSETS_ROOT;
import static com.day.cq.dam.commons.util.DamConfigurationConstants.DAM_COLLECTION_HOME;
import static com.day.cq.dam.commons.util.DamConfigurationConstants.DAM_TENANT_USER_HOME;
/**
* This class provides various utility methods pertaining to DAM.
*
* @author djaeggi
* @since CQ 5.4.0
*/
public class DamUtil {
/**
* Group for sharing collection publicly. Copied from
* CollectionHelper/DamCollectionManager in dam-core. TODO better to make
* configurable
*/
private static final String DEFAULT_PUBLIC_GROUP = "dam-users";
private static final Logger log = LoggerFactory.getLogger(DamUtil.class);
private static final String[] IMG_MIME_TYPES = {
DamConstants.THUMBNAIL_MIMETYPE, "image/jpeg", "image/tiff", "image/png",
"image/bmp", "image/gif", "image/pjpeg", "image/x-portable-anymap",
"image/x-portable-bitmap", "image/x-portable-graymap",
"image/x-portable-pixmap", "image/x-rgb", "image/x-xbitmap",
"image/x-xpixmap", "image/x-icon", "image/photoshop",
"image/x-photoshop", "image/psd", "application/photoshop", "application/psd",
"image/vnd.adobe.photoshop"};
private static final String[] VID_MIME_TYPES = { "video/m4v", "video/flv",
"video/avi", "video/mov", "video/3gpp", "application/x-troff-msvideo",
"video/vnd", "model/vnd.mts", "video/ts", "video/vnd", "video/dvd",
"video/x-ms-wmv", "video/msvideo", "video/x-msvideo", "video/x-flv",
"video/mpeg", "video/x-mpeg", "video/x-m4v", "video/mpg",
"video/x-mpg", "video/mpeg2", "video/x-mpeg2a", "video/mts",
"video/x-ms-asf", "video/3gpp2", "video/x-f4v", "video/f4v",
"video/m2p", "video/mp2t", "video/avchd-stream", "video/m2ts",
"video/mp2t", "video/vnd.dlna.mpeg-tts", "video/m2v",
"video/quicktime", "video/x-quicktime", "video/mp4",
"video/ogg", "video/x-mxf", "application/mxf", "video/x-matroska", "video/mj2",
"video/vnd.rn-realvideo", "application/vnd.rn-realmedia", "video/webm", "video/x-ms-vob" };
private static final List IMG_MIME_TYPE = Arrays.asList(IMG_MIME_TYPES);
private static final List VID_MIME_TYPE = Arrays.asList(VID_MIME_TYPES);
private static final int MAGIC_SIZE = 1024;
private static final String DM_OOTB_VIDEO_PROFILE_DIR = "/libs/settings/dam/dm/presets/video";
private static final String DM_CUSTOM_VIDEO_PROFILE_DIR = "/conf/global/settings/dam/dm/presets/video";
private static final String MIME_TYPE_ZIP = "application/zip";
private static final String FILE_EXT_ZIP = ".zip";
public static final String LIVEFYRE_ID = "livefyreId";
public static final String LIVEFYRE_THUMBNAIL = "livefyreThumbnail";
public static final String AGENT_ID_PREVIEW = "preview";
/**
* Translates the path of an asset to its congruent DAM binary path, by replacing the /content/dam path
* prefix with /var/dam. If the given path does not start with DAM's asset mountpoint, null
is
* returned.
*
* DamUtil.assetToBinaryPath("/content/dam/myfolder/test.jpg") = "/var/dam/myfolder/test.jpg"
* DamUtil.assetToBinaryPath(null) = null
*
*
* @param path The path to translate.
* @return The binary equivalent path or null
if the given path is not a DAM asset path.
* @see com.day.cq.dam.api.DamConstants#MOUNTPOINT_BINARIES
* @see com.day.cq.dam.api.DamConstants#MOUNTPOINT_ASSETS
*/
public static String assetToBinaryPath(final String path) {
String binaryPath = null;
if (StringUtils.startsWith(path, MOUNTPOINT_ASSETS)) {
binaryPath = StringUtils.replaceOnce(path, MOUNTPOINT_ASSETS, MOUNTPOINT_BINARIES);
}
return binaryPath;
}
/**
* Translates the path of a DAM binary to its congruent asset path, by replacing the /var/dam path prefix
* with /content/dam. If the given path does not start with DAM's binary mountpoint, null
is
* returned.
*
* DamUtil.binaryToAssetPath("/var/dam/myfolder/test.jpg") = "/content/dam/myfolder/test.jpg"
* DamUtil.binaryToAssetPath(null) = null
*
*
* @param path The path to translate.
* @return The metadata equivalent path or null
if the given path is not a DAM binary path.
* @see com.day.cq.dam.api.DamConstants#MOUNTPOINT_BINARIES
* @see com.day.cq.dam.api.DamConstants#MOUNTPOINT_ASSETS
*/
public static String binaryToAssetPath(final String path) {
String assetPath = null;
if (StringUtils.startsWith(path, MOUNTPOINT_BINARIES)) {
assetPath = StringUtils.replaceOnce(path, MOUNTPOINT_BINARIES, MOUNTPOINT_ASSETS);
}
return assetPath;
}
/**
* This method determines whether the given {@link Node} represents a thumbnail of an asset.
*
* @param file The node to check.
* @return true
if the node represent's an asset's thumbnail, false
otherwise.
*/
public static boolean isThumbnail(Node file) {
try {
return (file.isNodeType(NT_FILE) && file.getName().startsWith(PREFIX_ASSET_THUMBNAIL));
} catch (RepositoryException re) {
// ignore
}
return false;
}
/**
* This method creates a Map containing list of asset paths pending in agent queues
* @param sling
* @return Map will contains asset path, queue position in agent queue and action type
*/
public static Map> buildEntryDepthMap(SlingScriptHelper sling) {
return computeEntryDepthMap(sling, null);
}
/**
* This method creates a Map containing list of asset paths pending in preview agent queues
* @param sling
* @return Map will contains asset path, queue position in preview agent queue and action type
*/
public static Map> buildEntryDepthMapPreview(SlingScriptHelper sling) {
return computeEntryDepthMap(sling, AGENT_ID_PREVIEW);
}
/**
* This method creates a Map containing list of asset paths pending in agent queues.
* If agentId is not null
then create Map of that agent (if present).
* @param sling
* @param agentId
* @return Map will contains asset path, queue position in agent queue and action type
*/
private static Map> computeEntryDepthMap(SlingScriptHelper sling, String agentId) {
Map> entryDepth = new HashMap>();
try {
long startTime = (new Date()).getTime();
int assetsTraversed = 0;
AgentManager agentManager = sling.getService(AgentManager.class);
for (Agent agent : agentManager.getAgents().values()) {
if (!agent.isInMaintenanceMode()) {
if (StringUtils.isEmpty(agentId) && !agent.getId().equals(AGENT_ID_PREVIEW)) {
assetsTraversed += computeAgentPendingEntryMap(entryDepth, agent);
} else if (StringUtils.equals(agentId, agent.getId())) {
assetsTraversed += computeAgentPendingEntryMap(entryDepth, agent);
break;
}
}
}
long endTime = (new Date()).getTime();
log.debug("Time Elapsed \t" + (endTime - startTime));
log.debug("assetsTraversed \t" + assetsTraversed);
}
catch (Exception e) {
log.warn("Cannot build entryDepthMap" + (StringUtils.isEmpty(agentId) ? "" : "_" + agentId), e);
}
return entryDepth;
}
private static int computeAgentPendingEntryMap(Map> entryDepth, Agent agent) {
ReplicationQueue queue = agent.getQueue();
int assetsTraversed = 0;
if (queue != null) {
for (ReplicationQueue.Entry entry : queue.entries()) {
assetsTraversed++;
ReplicationAction action = entry.getAction();
String path = action.getPath();
String actionType = action.getType().getName();
int currentItemPosition = entry.getQueuePosition();
if (!entryDepth.containsKey(path) || entryDepth.get(path).getValue() < currentItemPosition) {
entryDepth.put(path, new MutablePair (actionType, currentItemPosition));
}
}
}
return assetsTraversed;
}
/**
* @deprecated This method is no longer valid since its' inconsistent with assets ui and AEM Desktop, use {@link
* #getSanitizedFolderName(String) getValidFolderPath} instead
*
* This method determines whether the folder name contains special characters and replaces them with underscore ("_")
*/
//Reference: https://git.corp.adobe.com/CQ/dam/blob/master/content/jcr_root/libs/dam/gui/coral/components/admin/clientlibs/damutil/js/util.js#L458
@Deprecated
public static String getValidFolderName(String name) {
log.warn("Deprecated DamUtil.getValidFolderName is used");
name = name.replaceAll("[\\\\%#{}/^;+:*?.|&\\t\\[\\] ]","_");
return name;
}
/**
* This method determines whether the folder name contains special characters(\%#{}/^;+:*?.|@\t[] ") and replaces them with dash ("-"),
* and convert to lower case
*/
public static String getSanitizedFolderName(String name) {
return getSanitizedFolderName(name, true);
}
/**
* This method determines whether the folder name contains special characters(\%#{}/^;+:*?.|@\t[] ") and replaces them with dash ("-").
* Lower case conversion is based on useLowerCase parameter.
*/
public static String getSanitizedFolderName(String name, boolean useLowerCase) {
String sanitizedFolderName = name.replaceAll("[\\\\%#{}/^;+:*?.|&\\t\\[\\] \"]", "-");
if (useLowerCase) {
return sanitizedFolderName.toLowerCase();
}
return sanitizedFolderName;
}
/**
* This method determines whether the file name contains special characters and replaces them
* with underscore ("_")
*/
//Reference: https://git.corp.adobe.com/CQ/dam/blob/master/content/jcr_root/libs/dam/gui/coral/components/admin/clientlibs/damutil/js/util.js#L454
public static String getValidFileName(String name) {
name = name.replaceAll("[*/:\\[\\]\\\\|#%{}?&]", "_");
return name;
}
/**
* This method determines whether the provided asset is actually a zip asset or not
* @param asset the asset to check
* @return returns true if asset is a zip file or else false
*/
public static boolean isZipTypeAsset(Asset asset) {
return asset != null && asset.getName().endsWith(FILE_EXT_ZIP) && MIME_TYPE_ZIP.equals(asset.getMimeType());
}
/**
* Returns the name of a thumbnail in the DAM thumbnail name format, respecting the given dimensions of the
* thumbnail. E.g. providing a width
of 100 and a height
of 100 would return the thumbnail
* name cq5dam.thumbnail.100.100.png. As a thumbnail represents a rendition of an {@link
* com.day.cq.dam.api.Asset}, the name can be used for easy retrieval of the thumbnail:
*
* ...
* final String thumbnailName = DamUtil.getThumbnailName(100, 100);
* final Resource thumbnail = asset.getRendition(thumbnailName);
* ...
*
*
* @param width The width of the thumbnail.
* @param height The height of the thumbnail.
* @return The rendition name of the thumbnail.
*/
public static String getThumbnailName(final int width, final int height) {
return getThumbnailName(width, height, null);
}
/**
* Returns the expected rendition/thumbnail name based on the given thumbnail configuration.
*
* @param config The {@link com.day.cq.dam.api.thumbnail.ThumbnailConfig}
* @return The {@link com.day.cq.dam.api.Rendition} name.
*/
public static String getThumbnailName(final ThumbnailConfig config) {
final String[] selectors = (config.doCenter()) ? new String[]{"margin"} : null;
return getThumbnailName(config.getWidth(), config.getHeight(), selectors);
}
/*
* Validate or create the node at specified path
*/
public static Node validateFolderNode(String path, Session serviceSession) throws RepositoryException{
Node node = null;
if (serviceSession.nodeExists(path) == false) {
node = JcrUtils.getOrCreateByPath(path, JcrConstants.NT_UNSTRUCTURED, JcrConstants.NT_UNSTRUCTURED, serviceSession, false);
} else {
node = serviceSession.getNode(path);
}
return node;
}
/*
* Add an empty Archive child node at specified path with specified name
*
* @param path specifies the path where the child node need to be added
* @param name specifies name of new Archive node
* @param serviceSession specifies service Session
* @return returns created empty Archive node.
*/
@Deprecated
public static Node createEmptyArchiveNode (Resource configResource, String name, final Session serviceSession) throws RepositoryException {
throw new UnsupportedOperationException("The support was reverted - The method was designed specifically for a caller that has been reverted.");
}
/**
* Behaves like {@link #getThumbnailName(int, int)}. Additionally and optionally a string array of selectors to be
* added to the thumbnail name can be specified. E.g. providing width
= 100, height
= 100
* and selectors
= {"a", "b", "c"} would return the thumbnail name
* cq5dam.thumbnail.100.100.a.b.c.png. Example:
*
* ...
* final String[] selectors = {"a", "b", "c"};
* final String thumbnailName = DamUtil.getThumbnailName(100, 100, selectors);
* final Resource thumbnail = asset.getRendition(thumbnailName);
* ...
*
*
* @param width The width of the thumbnail.
* @param height The height of the thumbnail.
* @param selectors An array of selectors to be added. May be null (ignored).
* @return The rendition name of the thumbnail.
*/
public static String getThumbnailName(final int width, final int height, String[] selectors) {
selectors = (null != selectors) ? selectors : new String[0];
String selectorString = StringUtils.join(selectors, '.');
if (!"".equals(selectorString)) {
selectorString = "." + selectorString;
}
return PREFIX_ASSET_THUMBNAIL + "." + String.valueOf(width) + "." + String.valueOf(height) + selectorString
+ ".png";
}
/**
* Checks whether the given {@link Resource} represents a {@link com.day.cq.dam.api.Rendition} of an {@link
* com.day.cq.dam.api.Asset}. The requirements are that given resource
is stored within the
* renditions folder of an asset and that it's node type is nt:file.
*
* @param resource The resource to check.
* @return true
if the resource is a rendition.
*/
public static boolean isRendition(final Resource resource) {
if (null == resource) {
return false;
}
final Resource parent = ResourceUtil.getParent(resource);
if (null == parent) {
return false;
}
final Resource content = ResourceUtil.getParent(parent);
if (null == content) {
return false;
}
final Resource asset = ResourceUtil.getParent(content);
if (null == asset) {
return false;
}
// resource type check was removed with introduction of proxied renditions - see GRANITE-1764
// since the resource type is no longer strictly NT_FILE but can be different as is for
// proxied renditions - the NT_FILE will remain for legacy purposes but anything else
// becomes an implementation detail of Granite Asset Core implementation.
return RENDITIONS_FOLDER.equals(ResourceUtil.getName(parent))
&& (isAsset(asset) || isFrozenNode(asset)); //Added frozen node check for CQ-8592
}
/**
* Returns true if {@link Asset} can be adapted from
* dam:asset/jcr:content/metadata resource else returns false.
*/
public static boolean isMetadataRes(final Resource resource) {
if (null == resource
|| !METADATA_FOLDER.equals(ResourceUtil.getName(resource))) {
return false;
}
final Resource content = ResourceUtil.getParent(resource);
if (null == content) {
return false;
}
final Resource asset = ResourceUtil.getParent(content);
if (null == asset) {
return false;
}
// resource type check was removed with introduction of proxied
// renditions - see GRANITE-1764
// since the resource type is no longer strictly NT_FILE but can be
// different as is for
// proxied renditions - the NT_FILE will remain for legacy purposes but
// anything else
// becomes an implementation detail of Granite Asset Core
// implementation.
return (isAsset(asset) || isFrozenNode(asset)); // Added frozen node
// check for CQ-8592
}
/**
* Returns {@link Asset} for the dam:asset/jcr:content/metadata resource
* else returns null.
*/
public static Asset getAssetFromMetaRes(final Resource resource) {
if (null == resource
|| !METADATA_FOLDER.equals(ResourceUtil.getName(resource))) {
return null;
}
final Resource content = ResourceUtil.getParent(resource);
if (null == content) {
return null;
}
final Resource asset = ResourceUtil.getParent(content);
if (null == asset) {
return null;
}
// resource type check was removed with introduction of proxied
// renditions - see GRANITE-1764
// since the resource type is no longer strictly NT_FILE but can be
// different as is for
// proxied renditions - the NT_FILE will remain for legacy purposes but
// anything else
// becomes an implementation detail of Granite Asset Core
// implementation.
if (isAsset(asset) || isFrozenNode(asset)) {
return asset.adaptTo(Asset.class);
}
return null;
}
/**
* Checks whether the given {@link Resource} represents a {@link com.day.cq.dam.api.Asset}. The requirements are
* that the given resource
is of node type dam:Asset.
*
* @param resource The resource to check.
* @return true
if the resource is an asset.
*/
public static boolean isAsset(final Resource resource) {
return null != resource && NT_DAM_ASSET.equals(resource.getResourceType());
}
/**
* Checks whether the give {@link Resource} represents a frozen node. The requirements are
* that the given resource
is of node type nt:frozenNode.
* @param resource The resource to check
* @return true
if the resource is an asset.
*/
public static boolean isFrozenNode(final Resource resource) {
return null != resource && "nt:frozenNode".equals(resource.getResourceType());
}
/**
* Indicates whether the given resource
is an {@link Asset}'s sub-asset.
*
* @param resource The {@link Resource} to check.
* @return true
if this asset is a sub-asset.
*/
public static boolean isSubAsset(final Resource resource) {
return isAsset(resource) && SUBASSETS_FOLDER.equals(ResourceUtil.getName(ResourceUtil.getParent(resource)));
}
/**
* Checks whether the given resource
is an asset, and if not, travels upwards the resource hierarchy
* until a resource is an asset.
*
* @param resource The resource to check.
* @return The {@link Asset} or null
if no asset was found.
*/
public static Asset resolveToAsset(final Resource resource) {
if (null != resource && isAsset(resource)) {
return resource.adaptTo(Asset.class);
} else {
final Resource parent = ResourceUtil.getParent(resource);
if (null != parent) {
return resolveToAsset(parent);
}
}
return null;
}
/**
* This method updates the "last modified" information of the given {@link Asset}.
*
* @param asset The asset to update.
* @param user The username of who updated the asset.
* @param date The date/time the updated happened.
*/
public static void setModified(final Asset asset, final String user, final Calendar date) {
try {
final Node contentNode = asset.adaptTo(Node.class).getNode(JcrConstants.JCR_CONTENT);
contentNode.setProperty(JcrConstants.JCR_LAST_MODIFIED_BY, user);
contentNode.setProperty(JcrConstants.JCR_LASTMODIFIED, date);
contentNode.getSession().save();
} catch (RepositoryException e) {
// ignore
}
}
/**
* Find all dam assets which are getting expired between lowerBound and upperBound.
*
* @param session
* @param lowerBound the lower bound of date where asset expiration is computed. If null it is not considered as
* query criterion
* @param upperBound the upper bound of date where asset expiration is computed. If null it is not considered as
* query criterion
* @throws RepositoryException
*/
public static List findExpiringAssets(Session session, Calendar lowerBound, Calendar upperBound)
throws RepositoryException {
List results = null;
QueryObjectModel qom = null;
try {
QueryManager queryManager = session.getWorkspace().getQueryManager();
QueryObjectModelFactory qomf = queryManager.getQOMFactory();
Source damAsset = qomf.selector(DamConstants.NT_DAM_ASSET, "selector_0");
Source assetContent = qomf.selector(DamConstants.NT_DAM_ASSETCONTENT, "selector_1");
ValueFactory vf = session.getValueFactory();
DynamicOperand expiryDateOprnd = qomf.propertyValue("selector_1", DamConstants.PN_OFF_TIME);
Constraint expiryDateConstrnt = null;
Constraint upperBndConstrnt = null;
if (upperBound != null) {
StaticOperand upperBndOprnd = qomf.literal(vf.createValue(upperBound));
upperBndConstrnt = qomf.comparison(expiryDateOprnd, QueryObjectModelConstants.JCR_OPERATOR_LESS_THAN,
upperBndOprnd);
expiryDateConstrnt = upperBndConstrnt;
}
if (lowerBound != null) {
StaticOperand lowerBndOprnd = qomf.literal(vf.createValue(lowerBound));
Constraint lowerBndConstrnt = qomf.comparison(expiryDateOprnd,
QueryObjectModelConstants.JCR_OPERATOR_GREATER_THAN_OR_EQUAL_TO, lowerBndOprnd);
if(upperBndConstrnt != null ) {
expiryDateConstrnt = qomf.and(upperBndConstrnt, lowerBndConstrnt);
}else {
expiryDateConstrnt = lowerBndConstrnt;
}
}
JoinCondition joinAssetToJcrContent = qomf.sameNodeJoinCondition("selector_1", "selector_0", JcrConstants.JCR_CONTENT);
assetContent = qomf.join(damAsset, assetContent, QueryObjectModelConstants.JCR_JOIN_TYPE_INNER,
joinAssetToJcrContent);
qom = qomf.createQuery(assetContent, expiryDateConstrnt, null, null);
if (log.isDebugEnabled()) {
log.debug("Expiring assets query [{}].", qom.getStatement());
}
long startTime = 0L;
if (log.isTraceEnabled()) {
startTime = System.currentTimeMillis();
}
QueryResult result = qom.execute();
if (log.isTraceEnabled()) {
long endTime = System.currentTimeMillis();
log.trace("Time taken to execute query [{}] ms", new Long(endTime - startTime));
}
RowIterator rowIter = result.getRows();
long resultSize = rowIter.getSize();
results = new ArrayList(new Long(resultSize).intValue());
if (log.isDebugEnabled()) {
log.debug("ResultSet size [{}].", resultSize);
}
if (log.isTraceEnabled()) {
log.trace("Logging search result set");
}
while (rowIter.hasNext()) {
Row row = rowIter.nextRow();
Node node = row.getNode("selector_0");
String path = node.getPath();
if (log.isTraceEnabled()) {
log.trace("path [{}]", path);
}
results.add(path);
}
} catch (RepositoryException e) {
if (qom != null) {
log.error("Expired assets query [{}].", qom.getStatement());
}
log.error("Error in finding expired assets", e);
throw e;
}
return results;
}
/**
* Returns whether the asset has expired.
* @param asset
* @return true, if the asset has expired
*/
public static boolean isExpiredAsset(Asset asset) {
boolean isAssetExpired = false;
if (null != asset) {
Calendar now = Calendar.getInstance();
Calendar assetExpiryTime = getExpiryTime(asset);
if (null != assetExpiryTime) {
isAssetExpired = (assetExpiryTime.getTimeInMillis() - now.getTimeInMillis()) < 0;
}
}
return isAssetExpired;
}
/**
* Returns whether the resource has expired.
* @param resource
* @return true, if the resource has expired
*/
public static boolean isExpiredAsset(Resource resource) {
return isExpiredAsset(resource.adaptTo(Asset.class));
}
/**
* Returns whether the resource has any subasset which has expired.
*
* @param resource
* @return true, if any of the subasset of the asset has expired
*/
public static boolean isExpiredSubAsset(Resource resource) {
boolean isAssetExpired = false;
boolean isSubAssetExpired = false;
Asset asset = resource.adaptTo(Asset.class);
if (null != asset) {
isAssetExpired = isExpiredAsset(asset);
if (!isAssetExpired) {
Calendar now = Calendar.getInstance();
Collection subAssets = asset.getSubAssets();
List refSubAssets = DamUtil.getReferencedSubAssets(resource);
if (null != refSubAssets) {
subAssets.addAll(refSubAssets);
}
ResourceResolver resolver = resource.getResourceResolver();
for (Asset each : subAssets) {
Resource subRes = resolver.getResource(each.getPath());
Node subNode = subRes.adaptTo(Node.class);
if (null != subNode) {
Calendar subAssetExpiryTime = DamUtil.getExpiryTime(each);
if (null != subAssetExpiryTime) {
isSubAssetExpired = (subAssetExpiryTime.getTimeInMillis() - now.getTimeInMillis()) < 0;
if(isSubAssetExpired)
break;
}
}
}
}
}
return isSubAssetExpired;
}
/**
* Returns the single property value from the given node n
with
* name
. If there is no single-valued property for the given
* name
, then the defaultValue
is returned.
*
* @param n a node.
* @param name a property name.
* @param defaultValue the default value.
* @return the value for the given property name or the default value.
* @throws RepositoryException if value cannot be retrieved
*/
public static String getValue(Node n, String name, String defaultValue)
throws RepositoryException {
if (n == null) {
return defaultValue;
}
try {
if (n.hasProperty(name)) {
Property p = n.getProperty(name);
if (!p.isMultiple()) {
return p.getString();
} else {
/*
* CQ5-13581 Multi value dc:title cause Title not to show up
* in UI.
*/
Value[] values = p.getValues();
String val = "";
boolean first = true;
for (Value a : values) {
if (!first) {
val += ", ";
} else {
first = false;
}
val += a.getString();
}
return val;
}
}
} catch (PathNotFoundException e) {
// property does not exist
}
return defaultValue;
}
/**
* To check weather asset falls under Image category.
* @param asset
* @return True if asset is of type Image.
*/
public static boolean isImage(Asset asset){
return IMG_MIME_TYPE.contains(asset.getMimeType());
}
/**
* To check whether given mime type is an image mime type.
* @param mimeType
* @return True if mimeType is an Image mime type.
*/
public static boolean isImage(String mimeType){
return IMG_MIME_TYPE.contains(mimeType);
}
/**
* To check weather asset falls under ThreeD category (Dimensional 3D asset).
* @param asset
* @return True if asset is of type ThreeD.
*/
public static boolean isThreeD(final Asset asset) {
return Boolean.parseBoolean(asset.getMetadataValue("dam:interactive3DAsset"));
}
/**
* To check weather asset falls under Video category.
* @param asset
* @return True if asset is of type video.
*/
public static boolean isVideo(final Asset asset) {
//CQ-3440 mimetype not extracted for certain formats
String errantTypes[] = {"mpv" ,"m2ts" , "m2t" , "m2p" , "vob" ,"ts", "webm", "mkv" };
List errantList = Arrays.asList(errantTypes);
return VID_MIME_TYPE.contains(asset.getMimeType()) ||
(asset.getMimeType()==null && errantList.contains(StringUtils.substringAfterLast(asset.getName(), ".").toLowerCase()));
}
/**
* Checks if given resource represent a smart collection
* @param resource an instance of {@link Resource}
* @return true
if resource
represents a smart collection
* false
if resource
does not represents a smart collection
*/
public static boolean isSmartCollection(final Resource resource) {
return resource.isResourceType(DamConstants.SMART_COLLECTION_SLING_RES_TYPE);
}
/**
* Checks if given resource represents a collection.
*
* @param resource an instance of {@link Resource}
* @return true if resource represents any collection type
*/
public static boolean isCollection(Resource resource) {
return resource != null
&& (resource.adaptTo(ResourceCollection.class) != null
|| resource.isResourceType(COLLECTION_SLING_RES_TYPE)
|| resource.isResourceType(SMART_COLLECTION_SLING_RES_TYPE));
}
/**
* Checks if a given resource is readable by the tenant specific group. If
* not defined checks for the dam-users group.
*
* @param resource DAM resource like collection, snippet
* @return True if readable by tenant default group
* @throws RepositoryException Error reading permissions
*/
public static boolean isPublic(Resource resource)
throws RepositoryException {
ResourceResolver resolver = resource.getResourceResolver();
String publicGroup = getPublicGroup(resolver);
Set principals = getPrincipalsForGroups(resolver,
publicGroup, "everyone");
JackrabbitAccessControlManager acm = getAccessControlManager(resolver);
return acm.hasPrivileges(resource.getPath(), principals,
new Privilege[] { acm.privilegeFromName(Privilege.JCR_READ) });
}
private static String getPublicGroup(ResourceResolver resolver) {
Tenant tenant = resolver.adaptTo(Tenant.class);
String tenantPublicGroup = tenant == null
? null
: (String) tenant.getProperty(
DamConfigurationConstants.DAM_ALL_USERS_GROUP_ID);
return StringUtils.defaultIfEmpty(tenantPublicGroup,
DEFAULT_PUBLIC_GROUP);
}
private static Set getPrincipalsForGroups(
ResourceResolver resolver, String... groups)
throws RepositoryException {
Set principals = new HashSet();
UserManager userManager = resolver.adaptTo(UserManager.class);
for (String group : groups) {
Authorizable authorizable = userManager.getAuthorizable(group);
if (authorizable != null) {
principals.add(authorizable.getPrincipal());
}
}
return principals;
}
private static JackrabbitAccessControlManager getAccessControlManager(
ResourceResolver resolver) throws RepositoryException {
Session session = resolver.adaptTo(Session.class);
return (JackrabbitAccessControlManager) session.getAccessControlManager();
}
/**
* Get a specific Processing profile which may be applied to an asset
* @param asset The asset we get from payload of DamUpdateAsset Workflow
* @param profileType The type of profile to get (metadata/video/image)
* @param session To retrieve nodes from JCR
* @return {Node} the Processing Profile, null if not found
*/
public static Node getApplicableProfile(final Asset asset, final String profileType, final Session session) {
Resource resource = asset.adaptTo(Resource.class);
return getApplicableProfile(resource,profileType,session);
}
/**
* Get a specific Processing profile which may be applied to an asset
* @param resource The resource we get from payload of DamUpdateAsset Workflow
* @param profileType The type of profile to get (metadata/video/image)
* @param session To retrieve nodes from JCR
* @return {Node} the Processing Profile, null if not found
*/
public static Node getApplicableProfile(final Resource resource, final String profileType, final Session session) {
try {
Node assetNode = resource.adaptTo(Node.class);
Node parentNode;
String path = "";
SlingHttpServletRequest req = resource.adaptTo(SlingHttpServletRequest.class);
if (null != assetNode) {
parentNode = assetNode;
while (!parentNode.getPath().equals("/content/dam")) {
if (!parentNode.isNodeType(DamConstants.NT_DAM_ASSET)
&& parentNode.hasProperty(JcrConstants.JCR_CONTENT + "/" + profileType)) {
path = parentNode.getProperty(JcrConstants.JCR_CONTENT + "/" + profileType).getString();
// if it's not a valid ignore it
if (path.startsWith("/")) {
if (session.nodeExists(path)) {
return session.getNode(path);
} else if (DamConstants.VIDEO_PROFILE.equals(profileType)&& !session.nodeExists(path)) {
// pre-6.4 upgrade fallback
String name = path.substring(path.lastIndexOf("/"));
name = name.replaceAll(" ", "_");
String ootbVideoProfile = DM_OOTB_VIDEO_PROFILE_DIR + name;
String customVideoProfile = DM_CUSTOM_VIDEO_PROFILE_DIR + name;
if (session.nodeExists(ootbVideoProfile)) {
return session.getNode(ootbVideoProfile);
} else if (session.nodeExists(customVideoProfile)) {
return session.getNode(customVideoProfile);
}
}
}
}
parentNode = parentNode.getParent();
}
}
} catch (RepositoryException e) {
log.error("Unable to retrieve applicable profile", e);
}
return null;
}
/**
* Processing Profile overview has been removed. Now individual PPs are applied to folders.
* return null
* use @getApplicableProfile
*/
@Deprecated
public static String getAppliedProcessingProfilePath(final Asset asset) {
return null;
}
/**
* Returns an iterator to all the assets contained in or represented by the resource.
* If resource is a folder, then all its folders are navigated recursively and assets are listed
* If a resource is {@link ResourceCollection}, then all its member resources are consulted and navigated recursively if any of the member
* represent folder or {@link ResourceCollection}.
* If the resource represents an asset itself, an iterator of single item i.e. Collections.singletonList(res.adaptTo(Asset.class)).iterator();
* @param res a sling {@link Resource}
* @return an {@link Iterator} of {@link Asset}
*/
public static Iterator getAssets(Resource res) {
Asset asset = res.adaptTo(Asset.class);
if (asset == null) {
return new FolderAssetIterator(res);
} else {
return Collections.singletonList(asset).iterator();
}
}
public static boolean checkforAIFile(Asset asset) {
InputStream in = null;
try {
if (asset != null && asset.getOriginal() != null) {
in = asset.getOriginal().getStream();
PushbackInputStream pin = new PushbackInputStream(in, MAGIC_SIZE);
byte[] data = new byte[MAGIC_SIZE];
int len;
try {
// prefetch some bytes to find correct handler
if ((len = pin.read(data)) <= 0) {
// no content
return false;
}
pin.unread(data, 0, len);
// check if it is an isAIFile with "application/postscript" as
// mimetype
byte[] pdfMarker = {'%', 'P', 'D', 'F', '-'};
int size = 1024;
if (size > len) size = len;
byte[] header = new byte[size];
System.arraycopy(data, 0, header, 0, size);
long result = Utility.KMPFindFirst(pdfMarker,
Utility.ComputeKMPNextArray(pdfMarker), header);
return (result >= 0);
} catch (IOException e) {
log.warn("I/O error while getting metadata.", e);
}
}
} finally {
IOUtils.closeQuietly(in);
}
return false;
}
/**
* It iterates all renditions available and find the closest (preferably higher side) rendition and return the same.
*
* @param width
* @param renditions
* @return rendition
*/
public static Rendition getBestFitRendition(int width,
List renditions) {
PrefixRenditionPicker prefixPicker = new PrefixRenditionPicker(
DamConstants.PREFIX_ASSET_THUMBNAIL + "." + width);
Rendition bestFitRendition = prefixPicker.getRendition(renditions.iterator());
if(bestFitRendition == null) {
prefixPicker = new PrefixRenditionPicker(
DamConstants.PREFIX_ASSET_WEB + "." + width);
bestFitRendition = prefixPicker.getRendition(renditions.iterator());
}
if (bestFitRendition != null) {
return bestFitRendition;
}
DamUtil.WidthBasedRenditionComparator comp = new DamUtil.WidthBasedRenditionComparator();
Collections.sort(renditions, comp);
Iterator itr = renditions.iterator();
Rendition bestFit = null;
Rendition highResRend = null;
long bestFitRenditionSize = Long.MAX_VALUE;
int renditionMaxWidth = Integer.MIN_VALUE;
while (itr.hasNext()) {
Rendition rend = itr.next();
if (UIHelper.canRenderOnWeb(rend.getMimeType())) {
if (rend.getName().equals(ORIGINAL_FILE)) {
continue;
}
int currentRenditionWidth = UIHelper.getWidth(rend);
long currentRenditionSize = rend.getSize();
if (currentRenditionWidth > renditionMaxWidth) {
renditionMaxWidth = currentRenditionWidth;
highResRend = rend;
}
if (currentRenditionWidth >= width && bestFitRenditionSize > currentRenditionSize) {
bestFit = rend;
bestFitRenditionSize = currentRenditionSize;
}
}
}
if (width > renditionMaxWidth) {
bestFit = highResRend;
}
return bestFit;
}
/**
*
* @param asset The asset whose best fit fpo rendition is sought
* @param renditionPreferenceList The list of renditions in order of preference
* @return The best fit rendition if found per the specified preference list else null
*/
public static Rendition getBestFitFPORendition(Asset asset, String[] renditionPreferenceList) {
Rendition bestFitFPO = null;
if(asset == null || renditionPreferenceList == null) {
log.debug("Invalid arguments for getBestFitFPORendition");
return null;
}
log.debug("FPO Rendition Preference List : {}", Arrays.toString(renditionPreferenceList));
for(String rendition : renditionPreferenceList) {
PrefixRenditionPicker prefixRenditionPicker = new PrefixRenditionPicker(rendition);
bestFitFPO = prefixRenditionPicker.getRendition(asset.listRenditions());
if(bestFitFPO != null) {
break;
}
}
log.debug("Best Fit FPO: {}", bestFitFPO != null ? bestFitFPO.getPath() : "null");
return bestFitFPO;
}
/**
* getImageDimension : To get actual width and height of the rendition or image for proper UI rendering of
* thumbnails , earlier new Layer () API was being used which was inefficient.
*/
public static Dimension getImageDimension(InputStream is, String filename) {
Dimension imageDimension = new Dimension(0, 0);
try {
imageDimension = Imaging.getImageSize(is, filename);
} catch (ImageReadException ex) {
log.error("Error in getting image dimension for rendition : " + filename, ex);
return imageDimension;
} catch (IOException ex) {
log.error("Error in getting image dimension for rendition : " + filename, ex);
return imageDimension;
}
return imageDimension;
}
/**
* getImageDimension : NUI To get actual width and height of the rendition or image for proper UI rendering of
* thumbnails , earlier new Layer () API was being used which was inefficient.
*/
public static Dimension getImageDimension(Rendition rendition) {
Dimension imageDimension = null;
// get properties
ValueMap props = rendition.getProperties();
// first look to the rendition properties
if (props.containsKey("width") && props.containsKey("height")) {
// get wid and hei from rendition properties
int wid = Integer.parseInt(props.get("width").toString());
int hei = Integer.parseInt(props.get("height").toString());
// save dimension
imageDimension = new Dimension(wid, hei);
}
// if not found, attempt get dimensions from rendition name
if (imageDimension == null) {
imageDimension = getDimensionsFromRenditionName(rendition.getName());
}
// if rendition is the original, check the image metadata
if (imageDimension == null && rendition.getName().equals(ORIGINAL_FILE)) {
Asset asset = rendition.getAsset();
String widthProp = "";
String heightProp = "";
widthProp = asset.getMetadataValue("tiff:ImageWidth");
heightProp = asset.getMetadataValue("tiff:ImageLength");
if (StringUtils.isBlank(widthProp) || StringUtils.isBlank(heightProp)) {
widthProp = asset.getMetadataValue("dam:ExifImageWidth");
heightProp = asset.getMetadataValue("dam:ExifImageHeight");
}
if (StringUtils.isBlank(widthProp) || StringUtils.isBlank(heightProp)) {
widthProp = asset.getMetadataValue("exif:PixelXDimension");
heightProp = asset.getMetadataValue("exif:PixelYDimension");
}
if (StringUtils.isBlank(widthProp) || StringUtils.isBlank(heightProp)) {
widthProp = "0";
heightProp = "0";
}
imageDimension = new Dimension(Integer.parseInt(widthProp), Integer.parseInt(heightProp));
}
return imageDimension;
}
public static boolean expiryStatus (Asset asset) {
return false;
}
public static List getReferencedSubAssets(Resource resource) {
return getReferencedResources(resource, "links", Asset.class);
}
public static List getReferencedCollections(
Resource resource) {
return getReferencedResources(resource, "collection-links",
ResourceCollection.class);
}
private static List getReferencedResources(Resource resource,
String relatedName, Class type) {
List references = new ArrayList();
Node node = resource.adaptTo(Node.class);
try {
String propertyPath = String.format(
"jcr:content/related/%s/sling:members/sling:resources",
JcrUtil.escapeIllegalJcrChars(relatedName));
if (node == null || !node.hasProperty(propertyPath)) {
return references;
}
Value[] referencePaths = node.getProperty(propertyPath).getValues();
for (Value resPath : referencePaths) {
Resource reference = resource.getResourceResolver().getResource(
resPath.getString());
if (reference != null && reference.adaptTo(type) != null) {
references.add(reference.adaptTo(type));
}
}
} catch (RepositoryException ex) {
log.warn("Error getting referenced resources", ex);
}
return references;
}
public static Collection getSubAssets (Resource resource) {
Collection assets = new LinkedList();
if (null != resource) {
Asset asset = resource.adaptTo(Asset.class);
if (null != asset) {
assets = asset.getSubAssets();
}
}
return assets;
}
public static Collection getRefererAssets (ResourceResolver resolver, String path) {
String sub = "select a.[jcr:path] as [jcr:path], a.[jcr:score] as [jcr:score], a.* " +
"from [dam:Asset] as a " +
"inner join [nt:base] as jc on ischildnode(jc, a) " +
"inner join [nt:base] as related on ischildnode(related, jc) " +
"inner join [nt:base] as links on ischildnode(links, related) " +
"inner join [nt:base] as members on ischildnode(members, links) " +
"inner join [nt:base] as e on ischildnode(e, members) " +
"where isdescendantnode(a, '%s') " +
"and name(jc) = 'jcr:content' " +
"and name(related) = 'related' " +
"and name(links) = 'links' " +
"and name(members) = 'sling:members' " +
"and e.[dam:resolvedPath] = '%s'";
//Escape single quote in in-bound asset paths
String query = String.format(sub, DamConstants.MOUNTPOINT_ASSETS, path.replaceAll("'", "''"));
log.debug("Find Referer Query: " + query);
Iterator iter = resolver.findResources(query, Query.JCR_SQL2);
List assets = new ArrayList<>();
while (iter.hasNext()) {
Resource res = iter.next();
Asset asset = null;
if (null != res) {
asset = res.adaptTo(Asset.class);
}
if (null != asset) {
assets.add(asset);
}
}
return assets;
}
public static Calendar getExpiryTime(Asset asset) {
if(asset == null){return null;}
Resource assetRes = asset.adaptTo(Resource.class);
Calendar assetExpiryTime = assetRes.adaptTo(ValueMap.class).get("jcr:content/metadata/prism:expirationDate", Calendar.class);
return assetExpiryTime;
}
public static Asset getParentAsset (Resource resource) {
if (null != resource && isSubAsset(resource)) {
return resource.getParent().getParent().adaptTo(Asset.class);
} else {
if (null == resource) {
log.error("Resource NULL.");
} else {
log.debug("Resource {} is not a sub-asset", resource.getPath());
}
return null;
}
}
/**
* Checks if the asset is valid. It is valid if it has content and if the
* on-/off time range spans the current time.
* @return true
if the page is valid; false
* otherwise.
*/
public static boolean isValid(Asset asset) throws RepositoryException {
return isValid(asset.adaptTo(Node.class));
}
/**
* Obtain Tenant Asset's root for specified ResourceResolver
* @return the tenant root-path
*/
public static String getTenantAssetsRoot(ResourceResolver resolver) {
User user = resolver.adaptTo(User.class);
String mountPoint = MOUNTPOINT_ASSETS;
if (user != null && !user.isSystemUser()) { // added a explicit check to
// avoid adapting
// system-user-resolver to
// Tenant, as it generates a
// NPE in log file.
Tenant tenant = resolver.adaptTo(Tenant.class);
if (tenant != null) {
mountPoint = (String) tenant.getProperty(DAM_ASSETS_ROOT);
}
}
return mountPoint;
}
/**
* Obtain Tenant Asset's root for specified asset path and ResourceResolver
* This returns the tenant for the session associated with the resource resolver,
* not the tenant associated with the asset if provided assetPath
* can't be resolved to an Asset
* @return the tenant root-path
*/
public static String getTenantAssetsRoot(ResourceResolver resolver, String assetPath) {
Resource assetResource = resolver.getResource(assetPath);
if (assetResource == null) {
return MOUNTPOINT_ASSETS;
}
Tenant tenant = assetResource.adaptTo(Tenant.class);
if (tenant == null) {
return MOUNTPOINT_ASSETS;
} else {
return (String) tenant.getProperty(DAM_ASSETS_ROOT);
}
}
/**
* Obtain Tenant Asset's root for specified Sling resource
* @return the tenant root-path
*/
public static String getTenantAssetsRoot(Resource resource) {
if (resource == null) {
return MOUNTPOINT_ASSETS;
}
Tenant tenant = resource.adaptTo(Tenant.class);
if (tenant == null) {
return MOUNTPOINT_ASSETS;
} else {
return (String) tenant.getProperty(DAM_ASSETS_ROOT);
}
}
/**
* Obtain collection path for specified tenant and user.
*
* @param resolver Adaptable for tenant and user
* @return path for user collections
*/
public static String getUserCollectionsPath(ResourceResolver resolver) {
String collectionHome = getTenantProperty(resolver, DAM_COLLECTION_HOME,
MOUNTPOINT_ASSETS + "/collections");
try {
String userHome = resolver.adaptTo(User.class).getPath();
String tenantUserHome = getTenantProperty(resolver,
DAM_TENANT_USER_HOME, "/home/users");
userHome = StringUtils.substringAfter(userHome, tenantUserHome);
return collectionHome + userHome;
} catch (RepositoryException ex) {
log.warn("Can't retrieve user path. Will return tenant collections path", ex);
}
return collectionHome;
}
private static String getTenantProperty(ResourceResolver resolver,
String property, String defaultValue) {
if (resolver == null) {
return defaultValue;
}
Tenant tenant = resolver.adaptTo(Tenant.class);
if (tenant == null) {
return defaultValue;
}
String value = (String) tenant.getProperty(property);
return StringUtils.defaultIfEmpty(value, defaultValue);
}
/**
* Obtain Asset
provided the jcr:uuid
* @return the Asset
, or null if asset with specified ID wasn't found
*
* @throws RepositoryException
*/
public static Asset getAssetFromID(ResourceResolver resolver, String id) throws RepositoryException {
try {
Node assetNode = resolver.adaptTo(Session.class).getNodeByIdentifier(id);
return resolver.getResource(assetNode.getPath()).adaptTo(Asset.class);
} catch (ItemNotFoundException ign) {
// ignore
return null;
} catch (RepositoryException ignoreOrRethrow) {
if (ignoreOrRethrow.getCause() instanceof IllegalArgumentException) {
// may happen for non-UUID "IDs" being supplied for lookup. ignore
return null;
} else {
throw ignoreOrRethrow;
}
}
}
/**
* Obtain Asset's path relative to specified Asset's root
* @return Asset's Relative Path (see {@link DamConstants#DAM_ASSET_RELATIVE_PATH}
*/
public static String findRelativePathOfAssetNode (Node assetNode, String assetsRoot)
throws RepositoryException {
String assetAbsPath = assetNode.getPath();
int idx = -1;
if (0 <= (idx = assetAbsPath.indexOf(assetsRoot))) {
idx += assetsRoot.length();
}
return assetAbsPath.substring(idx + 1);
}
/**
* Checks if the asset is valid. It is valid if it has content and if the
* on-/off time range spans the current time.
* @return true
if the page is valid; false
* otherwise.
*/
private static boolean isValid(Node assetNode) throws RepositoryException {
return timeUntilValid(assetNode) == 0;
}
/**
* Returns the number of milliseconds when this asset gets valid. If the
* asset is already valid, 0
is returned. If the page is out
* dated, i.e. the offTime is in the past, a negative number is returned. If
* this page has no content {@link Long#MIN_VALUE} is returned.
* @return milliseconds until page gets valid.
*/
private static long timeUntilValid(Node assetNode) throws RepositoryException {
if (!hasContent(assetNode)) {
return Long.MIN_VALUE;
}
Calendar onTime = getOnTime(assetNode);
Calendar offTime = getOffTime(assetNode);
if (onTime == null && offTime == null) {
return 0;
}
long now = System.currentTimeMillis();
long timeUntilOn = onTime == null ? 0 : onTime.getTimeInMillis() - now;
if (timeUntilOn > 0) {
return timeUntilOn;
}
long timeUntilOff = offTime == null ? 0 : offTime.getTimeInMillis()
- now;
if (timeUntilOff < 0) {
return timeUntilOff;
}
return 0;
}
/**
* Checks if the asset has content attached.
* @return true
if the asset has content; false
* otherwise.
*/
private static boolean hasContent(Node assetNode) throws RepositoryException {
return assetNode.hasNode(JcrConstants.JCR_CONTENT);
}
/**
* Returns the onTime
of the asset. The onTime defines after
* which time it is valid. If no onTime is specified null
is
* returned and the onTime is not respected in the {@link #isValid(Asset)}
* calculation.
* @return onTime or null
*/
private static Calendar getOnTime(Node assetNode) throws RepositoryException {
if (hasContent(assetNode)
&& assetNode.getNode(JcrConstants.JCR_CONTENT).hasProperty(
DamConstants.PN_ON_TIME)) {
return assetNode.getNode(JcrConstants.JCR_CONTENT).getProperty(
DamConstants.PN_ON_TIME).getDate();
}
return null;
}
/**
* Returns the offTime
of the asset. The offTime defines before
* which time it is valid. If no offTime is specified null
is
* returned and the offTime is not respected in the {@link #isValid(Asset)}
* calculation.
* @return offTime or null
*/
private static Calendar getOffTime(Node assetNode) throws RepositoryException {
if (hasContent(assetNode)
&& assetNode.getNode(JcrConstants.JCR_CONTENT).hasProperty(
DamConstants.PN_OFF_TIME)) {
return assetNode.getNode(JcrConstants.JCR_CONTENT).getProperty(
DamConstants.PN_OFF_TIME).getDate();
}
return null;
}
/**
* This method searches for the property in the provided contentPath
* resource. Moves up the repository path until finds the property on the
* node. If not found or can't convert the found value to given type,
* returns null
*
* @param property
* the property user is looking for.
* @param resource
* the resource representing the path where the property has to
* be searched
* @param type
* The class of the type
* @return the value configured on the node hierarchy. null if property not
* found on the path hierarchy
*/
private static T getInheritedProperty(String property, Resource resource, Class type) {
if (null == type) {
log.debug("type argument can't be null");
return null;
}
if (StringUtils.isBlank(property)) {
log.debug("property name can't be empty");
return null;
}
if (null == resource) {
log.debug("content resource could not be null");
return null;
}
T value = null;
while (null != resource) {
ValueMap propertiesMap = resource.adaptTo(ValueMap.class);
if (null != propertiesMap && propertiesMap.containsKey(property)) {
value = propertiesMap.get(property, type);
break;
}
resource = resource.getParent();
}
if (null == value) {
if (null == resource) {
log.debug("property {} not found on the content path", property);
} else {
log.debug("property value retrived could not be converted to {}", type.getName());
}
return null;
}
return value;
}
/**
* This method searches for the property in the provided contentPath
* resource. Moves up the repository path until finds the property on the
* node. Tries to convert the found value to type of default value. If
* can't, returns the defaultValue
*
* @param property
* the property user is looking for.
* @param resource
* the resource representing the path where the property has to
* be searched
* @param defaultValue
* @return the value configured on the node hierarchy converted to type of
* default value. defaultValue if property not found on the path
* hierarchy
*/
public static T getInheritedProperty(String property, Resource resource, T defaultValue) {
if (null == defaultValue) {
log.debug("defaultValue argument cant be null");
return null;
}
@SuppressWarnings("unchecked")
T value = DamUtil.getInheritedProperty(property, resource, (Class) defaultValue.getClass());
if (null != value) {
return value;
}
return defaultValue;
}
/**
* This function determines if an asset has its DamConstants.DAM_ASSET_STATE
property is set to
* DamConstants.DAM_ASSET_STATE_PROCESSING
. Presently this returns true only if in DAM update
* asset workflow.
*
* @param resource
* the resource to check
* @return true if DamConstants.DAM_ASSET_STATE
property is set to DamConstants.DAM_ASSET_STATE_PROCESSING
or DamConstants.DAM_ASSET_STATE_CUSTOM_WF_PROCESSING
* false otherwise
* @deprecated This method has been deprecated in favor of com.day.cq.dam.api.processingstate.provider.AssetProcessingStateProvider.isProcessing()
**/
@Deprecated
public static boolean isInRunningWorkflow(Resource resource) {
boolean retVal = false;
try {
Resource jcrContent = resource.getResourceResolver().getResource(resource.getPath() + "/" + JCR_CONTENT);
ValueMap properties = jcrContent.getValueMap();
if (properties.containsKey(DamConstants.DAM_ASSET_STATE) && properties.get(DamConstants.DAM_ASSET_STATE).equals(DamConstants.DAM_ASSET_STATE_PROCESSING)) {
retVal = true;
}
} catch (Exception e) {
} finally {
return retVal;
}
}
private static class WidthBasedRenditionComparator implements Comparator {
@Override
public int compare(Rendition r1, Rendition r2) {
int w1 = UIHelper.getWidth(r1);
int w2 = UIHelper.getWidth(r2);
if (w1 < w2) {
return -1;
} else if (w1 == w2) {
return 0;
} else {
return 1;
}
}
}
/**
* Get an instance of {@link AssetCache} for access to cached rendition contents and
* aggregated {@link InputStream} and temporary {@link java.io.File} cleanups.
* @return a suitable asset cache
*/
public static AssetCache getAssetCache() {
return AssetCacheImpl.getCache();
}
/**
* A helper class that iterates recursively all child resources of a parent resource (folder or collection) that are asset.
*/
private static class FolderAssetIterator implements Iterator {
private Resource parentResource;
private Iterator it = null;
private List parentNodes = new ArrayList();
private Asset next = null;
private Iterator getChildren(Resource res) {
ResourceCollection rc = res.adaptTo(ResourceCollection.class);
if (rc != null) {
return rc.getResources();
}
return res.listChildren();
}
public FolderAssetIterator(Resource resource) {
this.parentResource = resource;
it = getChildren(this.parentResource);
next = getNext();
}
private Asset getNext() {
while (!it.hasNext() && !parentNodes.isEmpty()) {
it = getChildren(parentNodes.remove(0));
}
while (it.hasNext()) {
Resource member = it.next();
Node n = member.adaptTo(Node.class);
try {
if (n.isNodeType(NT_DAM_ASSET)) {
return member.adaptTo(Asset.class);
} else if (n.isNodeType(JcrConstants.NT_FOLDER) || member.adaptTo(ResourceCollection.class) != null) {
parentNodes.add(member);
}
} catch (RepositoryException e) {
log.error("unexpected exception ", e);
//try the next one
}
}
if (!parentNodes.isEmpty()) {
return getNext();
}
return null;
}
@Override
public boolean hasNext() {
return (next != null);
}
@Override
public Asset next() {
Asset toReturn = next;
next = getNext();
return toReturn;
}
@Override
public void remove() {
throw new UnsupportedOperationException("remove not allowed for asset iterator on folder listing");
}
}
/**
* This function returns image dimensions based on rendition name
*
* @param name the rendition name to check
*
* @return Dimension if string has all numeric values false otherwise
**/
private static Dimension getDimensionsFromRenditionName(String name) {
Pattern pattern = Pattern.compile(".*?(\\d+)\\.(\\d+).+");
Matcher matcher = pattern.matcher(name);
if (matcher.matches()) {
int wid = Integer.parseInt(matcher.group(1));
int hei = Integer.parseInt(matcher.group(2));
if (wid > 0 && hei > 0) {
return new Dimension(wid, hei);
}
}
return null;
}
/**
* This function determines if an asset is livefyre asset.
*
* @param resource
* the resource to check
* @return
* true if LIVEFYRE_ID
property is avaliable,
* false otherwise
**/
public static boolean isLivefyreFragment(Resource resource) {
boolean flag = false;
Resource livefyreMetadata = resource.getChild(JCR_CONTENT + "/" + METADATA_FOLDER);
if ( null != livefyreMetadata ) {
ValueMap properties = livefyreMetadata.getValueMap();
if (properties.get(LIVEFYRE_ID, String.class) != null) {
flag = true;
}
}
return flag;
}
/**
* This method searches for the content property in the provided contentPath
* resource. Moves up the repository path until finds the property on the
* node. Tries to convert the found value to type of default value. If
* can't, returns the defaultValue
*
* @param property
* the property user is looking for.
* @param resource
* the resource representing the path where the property has to
* be searched
* @param defaultValue
* @return the value configured on the node hierarchy converted to type of
* default value. defaultValue if property not found on the path
* hierarchy
*/
public static T getInheritedContentProperty(String property, Resource resource, T defaultValue) {
if (null == defaultValue) {
log.debug("defaultValue argument cant be null");
return null;
}
@SuppressWarnings("unchecked")
T value = DamUtil.getInheritedContentProperty(property, resource, (Class) defaultValue.getClass());
if (null != value) {
return value;
}
return defaultValue;
}
/**
* This method searches for the property in the provided contentPath
* resource. Moves up the repository path until finds the property on the
* node. If not found or can't convert the found value to given type,
* returns null
*
* @param property
* the property user is looking for.
* @param resource
* the resource representing the path where the property has to
* be searched
* @param type
* The class of the type
* @return the value configured on the node hierarchy. null if property not
* found on the path hierarchy
*/
private static T getInheritedContentProperty(String property, Resource resource, Class type) {
if (null == type) {
log.debug("type argument can't be null");
return null;
}
if (StringUtils.isBlank(property)) {
log.debug("property name can't be empty");
return null;
}
if (null == resource) {
log.debug("content resource could not be null");
return null;
}
ResourceResolver resolver = resource.getResourceResolver();
T value = null;
while (null != resource && !DamConstants.MOUNTPOINT_ASSETS.equals(resource.getPath())) {
Resource contentResource = resolver.getResource(
resource.getPath() + "/" + JcrConstants.JCR_CONTENT);
if (null == contentResource) {
log.debug("resource content for resrouce {} could not be null", resource.getPath());
return null;
}
ValueMap propertiesMap = contentResource.adaptTo(ValueMap.class);
if (null != propertiesMap && propertiesMap.containsKey(property)) {
value = propertiesMap.get(property, type);
break;
}
resource = resource.getParent();
}
if (null == value) {
if (null == resource) {
log.debug("property {} not found on the content path", property);
} else {
log.debug("property value retrived could not be converted to {}", type.getName());
}
return null;
}
return value;
}
/**
* this skips the number of bytes that represents the BOM from the
* inputstream, so that BOM does not appear as invalid characters in the
* output rendition. Otherwise Layer will try to render BOM also as a
* character and eventually ends up in showing a invalid character as the
* first character in the rendition.
*/
public static void skipBOM(InputStream stream, String encodingFormat)
throws IOException {
if (encodingFormat != null) {
if (encodingFormat.contains("UTF-16")) {
stream.skip(2);
} else if (encodingFormat.contains("UTF-32")) {
stream.skip(4);
}
}
}
/**
* This method returns the title for a given resource based on type.
* If resource is of type asset, it returns jcr:content/metadata/dc:title.
* and jcr:title for other types.
*/
public static String getTitle(@Nonnull Resource resource) {
String title = "";
Asset asset = resource.adaptTo(Asset.class);
if (asset != null && resource.adaptTo(ContentFragment.class) == null) { // for assets, only consult dc:title
title = asset.getMetadataValueFromJcr(DamConstants.DC_TITLE);
} else { // check jcr:title for non-asset type
Resource content = resource.getChild(JcrConstants.JCR_CONTENT);
if (content == null) {
content = resource;
}
ValueMap vm = content.adaptTo(ValueMap.class);
title = vm.get(JcrConstants.JCR_TITLE, "");
}
return title;
}
public static boolean isAudio(Asset asset) {
if (asset == null) {
return false;
}
String mimeType = asset.getMimeType();
return StringUtils.isBlank(mimeType) ? false : mimeType.startsWith("audio/");
}
public static boolean isVtt(Asset asset) {
if (asset == null) {
return false;
}
String mimeType = asset.getMimeType();
return StringUtils.isBlank(mimeType) ? false : mimeType.equalsIgnoreCase("text/vtt");
}
public static boolean isVideoSubAsset(Asset asset) {
boolean isAudioOrVtt = DamUtil.isAudio(asset) || DamUtil.isVtt(asset);
if (!isAudioOrVtt || !asset.isSubAsset()) {
return false;
}
Asset parentAsset = DamUtil.getParentAsset(asset.adaptTo(Resource.class));
return parentAsset != null && DamUtil.isVideo(parentAsset);
}
}