/* * #%L * ACS AEM Commons Bundle * %% * Copyright (C) 2013 Adobe * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * #L% */ package com.adobe.acs.commons.contentfinder.querybuilder.impl; import com.day.cq.commons.jcr.JcrConstants; 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.commons.util.DamUtil; import com.day.cq.search.result.Hit; import com.day.cq.wcm.api.NameConstants; import com.day.cq.wcm.api.Page; import org.apache.commons.lang.StringUtils; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ValueMap; import javax.jcr.RepositoryException; import java.util.Date; import java.util.LinkedHashMap; import java.util.Map; public final class ContentFinderHitBuilder { private static final Long ONE_MILLION = 1000000L; private ContentFinderHitBuilder() { } private static final int ELLIPSE_LENGTH = 3; private static final int MAX_EXCERPT_LENGTH = 32; private static final String DAM_THUMBNAIL = "cq5dam.thumbnail.48.48.png"; /** * Builds the result object that will representing a CF view record for the provided hit. * <p> * This method will generate the result object data points based on if the hit is: * 1) a Page * 2) an Asset * 3) Other * * @param hit a hit * @return a result object * @throws RepositoryException if something goes wrong */ public static Map<String, Object> buildGenericResult(final Hit hit) throws RepositoryException { Map<String, Object> map = new LinkedHashMap<String, Object>(); final Resource resource = hit.getResource(); /** * Apply custom properties based on the "type" */ // Assets final Asset asset = DamUtil.resolveToAsset(resource); if (asset != null) { return addAssetData(asset, hit, map); } // Pages final Page page = getPage(resource); if (page != null) { return addPageData(page, hit, map); } // Other return addOtherData(hit, map); } /** * Derives and adds Page related information to the map representing the hit. * * @param hit * @param map * @return * @throws javax.jcr.RepositoryException */ private static Map<String, Object> addPageData(final Page page, final Hit hit, Map<String, Object> map) throws RepositoryException { // Title String title = page.getName(); if (StringUtils.isNotBlank(page.getTitle())) { title = page.getTitle(); } else if (StringUtils.isNotBlank(page.getPageTitle())) { title = page.getPageTitle(); } else if (StringUtils.isNotBlank(page.getNavigationTitle())) { title = page.getNavigationTitle(); } // Excerpt String excerpt = hit.getExcerpt(); if (StringUtils.isBlank(hit.getExcerpt())) { excerpt = StringUtils.stripToEmpty(page.getDescription()); if (excerpt.length() > MAX_EXCERPT_LENGTH) { excerpt = StringUtils.substring(excerpt, 0, (MAX_EXCERPT_LENGTH - ELLIPSE_LENGTH)) + "..."; } } map.put("path", page.getPath()); map.put("name", page.getName()); map.put("title", title); map.put("excerpt", excerpt); map.put("ddGroups", "page"); map.put("type", "Page"); map.put("lastModified", getLastModified(page)); return map; } /** * Derives and adds Asset related information to the map representing the hit. * * @param hit * @param map * @return * @throws javax.jcr.RepositoryException */ private static Map<String, Object> addAssetData(final Asset asset, final Hit hit, Map<String, Object> map) throws RepositoryException { String title = asset.getName(); if (StringUtils.isNotBlank(asset.getMetadataValue(DamConstants.DC_TITLE))) { title = asset.getMetadataValue(DamConstants.DC_TITLE); } // Excerpt String excerpt = hit.getExcerpt(); if (StringUtils.isBlank(hit.getExcerpt())) { excerpt = StringUtils.stripToEmpty(asset.getMetadataValue(DamConstants.DC_DESCRIPTION)); if (excerpt.length() > MAX_EXCERPT_LENGTH) { excerpt = StringUtils.substring(excerpt, 0, (MAX_EXCERPT_LENGTH - ELLIPSE_LENGTH)) + "..."; } } map.put("path", asset.getPath()); map.put("name", asset.getName()); map.put("title", title); map.put("excerpt", excerpt); map.put("mimeType", asset.getMimeType()); map.put("size", getSize(asset)); map.put("ck", getCK(asset)); map.put("type", "Asset"); map.put("lastModified", getLastModified(asset)); return map; } /** * Derives and adds Other (non-Page, non-Asset) related information to the map representing the hit. * * @param hit * @param map * @return * @throws javax.jcr.RepositoryException */ private static Map<String, Object> addOtherData(final Hit hit, Map<String, Object> map) throws RepositoryException { final Resource resource = hit.getResource(); final ValueMap properties = resource.adaptTo(ValueMap.class); map.put("path", resource.getPath()); map.put("name", resource.getName()); map.put("title", properties.get("jcr:title", resource.getName())); map.put("excerpt", hit.getExcerpt()); map.put("lastModified", getLastModified(resource)); map.put("type", "Data"); return map; } /** * Get the last modified date for an Asset. * * @param asset * @return */ private static long getLastModified(final Asset asset) { if (asset.getLastModified() > 0L) { return asset.getLastModified(); } else { final Object obj = asset.getMetadata().get(JcrConstants.JCR_LASTMODIFIED); if (obj != null && obj instanceof Date) { return ((Date) obj).getTime(); } else { return 0L; } } } /** * Get the last modified date for a Page. * * @param page * @return */ private static long getLastModified(final Page page) { if (page.getLastModified() != null) { return page.getLastModified().getTimeInMillis(); } else { final ValueMap properties = page.getProperties(); Date lastModified = properties.get(NameConstants.PN_PAGE_LAST_MOD, Date.class); if (lastModified != null) { return lastModified.getTime(); } else { return 0L; } } } /** * Get the last modified date for a generic resource. * * @param resource * @return */ private static long getLastModified(final Resource resource) { final ValueMap properties = resource.adaptTo(ValueMap.class); final Date cqLastModified = properties.get(NameConstants.PN_PAGE_LAST_MOD, Date.class); if (cqLastModified != null) { return cqLastModified.getTime(); } final Date jcrLastModified = properties.get(JcrConstants.JCR_LASTMODIFIED, Date.class); if (jcrLastModified != null) { return jcrLastModified.getTime(); } return 0L; } /** * Get the size of the Asset (the original rendition). * * @param asset * @return */ private static long getSize(final Asset asset) { final Rendition original = asset.getOriginal(); if (original == null) { return 0; } return original.getSize(); } /** * Get the timestamp for the last change to the thumbnail. * * @param asset * @return */ private static long getCK(final Asset asset) { try { Resource resource = asset.getRendition(DAM_THUMBNAIL); Resource contentResource = resource.getChild(JcrConstants.JCR_CONTENT); ValueMap properties = contentResource.adaptTo(ValueMap.class); return properties.get(JcrConstants.JCR_LASTMODIFIED, 0L) / ONE_MILLION; } catch (Exception ex) { return 0L; } } /** * Gets the Page object corresponding the with the resource. * Will resolve to a Page if the result is a cq:Page or a cq:Page's jcr:content node. * * @param resource The resource to covert to a Page * @return a Page if the resource is Page like (cq:Page or a cq:Page's jcr:content node), else null */ private static Page getPage(final Resource resource) { if (resource == null) { return null; } // If resource is a cq:Page node; then return the Page if (resource.adaptTo(Page.class) != null) { return resource.adaptTo(Page.class); } // If the resource is a cq:Page/jcr:content node, then return the cq:Page page if (StringUtils.equals(resource.getName(), JcrConstants.JCR_CONTENT)) { final Resource parent = resource.getParent(); if (parent != null) { return parent.adaptTo(Page.class); } } return null; } }