All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.day.cq.dam.commons.util.DamUtil Maven / Gradle / Ivy

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show newest version
/*
 * 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); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy