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

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

/*
 * 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.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.jcr.Binary;
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.jackrabbit.api.binary.BinaryDownload;
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.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.resource.collection.ResourceCollection;
import org.apache.sling.tenant.Tenant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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 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" };

    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";

    /**
     * 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 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 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) { try { Node assetNode = asset.adaptTo(Node.class); Node parentNode; String path = ""; if (null != assetNode) { parentNode = assetNode.getParent(); while (!parentNode.getPath().equals("/content")) { if (!parentNode.getPrimaryNodeType().getName().equals(DamConstants.NT_DAM_ASSET) && parentNode.hasProperty(JcrConstants.JCR_CONTENT + "/" + profileType)) { path = parentNode.getProperty(JcrConstants.JCR_CONTENT + "/" + profileType).getString(); 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; int bestFitRenditionWidth = Integer.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); if (currentRenditionWidth > renditionMaxWidth) { renditionMaxWidth = currentRenditionWidth; highResRend = rend; } if (currentRenditionWidth >= width && bestFitRenditionWidth > currentRenditionWidth) { bestFit = rend; bestFitRenditionWidth = currentRenditionWidth; } } } if (width > renditionMaxWidth) { bestFit = highResRend; } return bestFit; } /** * 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) { int wid = 0; int hei = 0; Dimension imageDimension = null; // attempt to get binary to see if nui is enabled Binary binary = rendition.getBinary(); // check to see if nui is enabled if (binary instanceof BinaryDownload) { // get properties ValueMap props = rendition.getProperties(); // if with and height cannot be retrieved from properties attempt to get them from rendition name if (props.containsKey("width") && props.containsKey("height")) { // get wid and hei from rendition name wid = Integer.parseInt(props.get("width").toString()); hei = Integer.parseInt(props.get("height").toString()); // save dimension imageDimension = new Dimension(wid, hei); } } // attempt to figure out dimensions from rendition name if (imageDimension == null) { imageDimension = getDimensionsFromRenditionName(rendition.getName()); } // fall back on previous approach if (imageDimension == null) { imageDimension = getImageDimension(rendition.getStream(), rendition.getName()); } 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 root = DamConstants.MOUNTPOINT_ASSETS; 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'"; String query = String.format(sub, root, path); 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 (IllegalArgumentException ign) { // may happen for non-UUID "IDs" being supplied for lookup. ignore return null; } } /** * 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 * false otherwise **/ 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; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy