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

com.adobe.forms.foundation.service.handlers.FormsAbstractSavedSearchHandler Maven / Gradle / Ivy

/*************************************************************************
 *
 * ADOBE CONFIDENTIAL
 * __________________
 *
 *  Copyright 2016 Adobe Systems Incorporated
 *  All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of Adobe Systems Incorporated and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to Adobe Systems Incorporated and its
 * suppliers and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Adobe Systems Incorporated.
 **************************************************************************/
package com.adobe.forms.foundation.service.handlers;

import com.adobe.granite.omnisearch.api.core.OmniSearchException;
import com.adobe.granite.omnisearch.spi.core.SavedSearchHandler;
import com.day.cq.commons.jcr.JcrConstants;
import com.day.cq.commons.jcr.JcrUtil;
import com.day.cq.i18n.I18n;
import com.day.cq.search.PredicateGroup;
import com.day.cq.search.Query;
import com.day.cq.search.QueryBuilder;
import com.day.cq.search.result.SearchResult;
import org.apache.commons.lang3.StringUtils;
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.sling.api.resource.ModifiableValueMap;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.resource.collection.ResourceCollection;
import org.apache.sling.resource.collection.ResourceCollectionManager;
import org.apache.sling.servlets.post.SlingPostConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;

/**
 * Abstract saved search handler for forms consoles.
 */
public abstract class FormsAbstractSavedSearchHandler implements SavedSearchHandler {
    private static final Logger logger = LoggerFactory.getLogger(FormsAbstractSavedSearchHandler.class);

    /**
     * Request parameters
     */
    protected static final String REQUEST_PARAMETER_TITLE = "title";
    protected static final String REQUEST_PARAM_RESOURCE_PATH = "resourcePath";
    /**
     * Represents default request parameters, used while filtering out smart collection query parameters.
     */
    protected static final Set defaultReqParams = new HashSet();
    /**
     * The location of saved searches under user home.
     */
    private static final String PREFERENCES_SAVED_SEARCH_PATH = "/preferences/omnisearch/forms/";

    static {
        defaultReqParams.add(REQUEST_PARAMETER_TITLE);
        defaultReqParams.add(SlingPostConstants.RP_OPERATION);
        defaultReqParams.add(REQUEST_PARAM_RESOURCE_PATH);
    }
    static final String CREATE_COMMAND = "create";
    static final String UPDATE_COMMAND = "update";
    /**
     * Node property used to store saved query
     */
    private static final String NODE_SAVEDQUERY = "savedquery";
    /**
     * URL Param separator
     */
    private static final String PARAM_SEP = "&";
    /**
     * Key value pair separator
     */
    private static final String NAME_VALUE_SEPARATOR = "=";
    private static final String LOCATION_PARAM = "location";

    public boolean deleteSavedSearch(ResourceResolver resourceResolver, String path) {
        //We don't have to implement this, it is triggered directly at the resource
        //level using SlingPostServlet :operation="delete"
        return false;
    }

    @Override
    public Iterator getSavedSearches(ResourceResolver resourceResolver, long limit, long offset) {
        try {
            String path = getSavedSearchPath(resourceResolver);
            Map map = new HashMap();
            map.put("path", path);
            map.put("type", "nt:unstructured");
            map.put("property", "sling:resourceType");
            map.put("property.value", "sling/collection");

            Session session = resourceResolver.adaptTo(Session.class);
            Query query = getQueryBuilder().createQuery(PredicateGroup.create(map), session);
            SearchResult result = query.getResult();
            return result.getResources();
        } catch (RepositoryException e) {
            logger.error("Error retrieving saved searches for omnisearch location " + getID(), e);
            return Collections.emptyIterator();
        }
    }

    @Override
    public Map getSavedSearchParameters(ResourceResolver resourceResolver, String path) {
        Resource resource = null;
        if (!StringUtils.isEmpty(path)) {
            resource = resourceResolver.getResource(path);
        }

        if (resource == null) {
            return Collections.emptyMap();
        }

        Session session = resourceResolver.adaptTo(Session.class);
        String queryPath = resource.getPath() + "/" + NODE_SAVEDQUERY;
        try {
            Query storedQuery = getQueryBuilder().loadQuery(queryPath, session);
            String queryParams = storedQuery.getPredicates().toURL();
            return parse(queryParams);
        } catch (Exception e) {
            logger.error("Unable to get saved search parameters", e);
            return Collections.emptyMap();
        }
    }

    @Override
    public Resource createOrUpdateSavedSearch(ResourceResolver resourceResolver, Map requestParameters) throws OmniSearchException {
        String operation = requestParameters.get(SlingPostConstants.RP_OPERATION);
        Resource res = null;
        try {
            if (CREATE_COMMAND.equals(operation) || StringUtils.isEmpty(operation)) {
                res = createSavedSearch(resourceResolver, requestParameters);
            } else if (UPDATE_COMMAND.equals(operation)) {
                res = updateSavedSearch(resourceResolver, requestParameters);
            } else {
                logger.warn("Unknown saved search operation: " + operation);
            }
        } catch (Exception e) {
            throw new OmniSearchException(e.getMessage(), e);
        }
        return res;
    }

    private Resource createSavedSearch(ResourceResolver resourceResolver, Map requestParameters) throws Exception {
        String title = requestParameters.get(REQUEST_PARAMETER_TITLE);
        if (StringUtils.isEmpty(title)) {
            String errorMessage = I18n.get(resourceResolver.adaptTo(ResourceBundle.class), "Invalid request, empty name not allowed.");
            throw new OmniSearchException(errorMessage);
        }

        Node parentNode;
        Session session = resourceResolver.adaptTo(Session.class);
        String resourcePath = getSavedSearchPath(resourceResolver);
        if (!session.nodeExists(resourcePath)) {
            parentNode = JcrUtil.createPath(resourcePath, JcrConstants.NT_UNSTRUCTURED, JcrConstants.NT_UNSTRUCTURED, session, false);
        } else {
            parentNode = session.getNode(resourcePath);
        }

        Resource resource = resourceResolver.getResource(resourcePath);
        Map params = new HashMap();
        params.put(JcrConstants.JCR_TITLE, title);
        String nodeName = JcrUtil.createValidName(title);
        String name = JcrUtil.createValidChildName(parentNode, nodeName);
        if (!name.equalsIgnoreCase(nodeName)) {
            String errorMessage = I18n.get(resourceResolver.adaptTo(ResourceBundle.class), "Invalid request, duplicate name not allowed.");
            throw new OmniSearchException(errorMessage);
        } else {
            params.put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED);

            String resourceType = requestParameters.get(ResourceResolver.PROPERTY_RESOURCE_TYPE);
            if (StringUtils.isEmpty(resourceType)) {
                //fallback backward compatibility
                resourceType = ResourceCollection.RESOURCE_TYPE;
            }
            params.put(ResourceResolver.PROPERTY_RESOURCE_TYPE, resourceType);

            // Create resource collection
            ResourceCollectionManager rcMgr = resourceResolver.adaptTo(ResourceCollectionManager.class);
            ResourceCollection rc = rcMgr != null ? rcMgr.createCollection(resource, name, params) : null;
            Resource collRes = null;
            // Save the query if it is a smart collection
            if(rc != null) {
                resourcePath = rc.getPath();
                saveQuery(resourceResolver, resourcePath, requestParameters);
                collRes = resourceResolver.getResource(resource, name);
                addMixinType(collRes);
                session.save();
            }
            return collRes;
        }
    }

    private Resource updateSavedSearch(ResourceResolver resourceResolver, Map requestParameters) throws Exception {
        String resourcePath = requestParameters.get(REQUEST_PARAM_RESOURCE_PATH);
        Session session = resourceResolver.adaptTo(Session.class);
        if (StringUtils.isEmpty(resourcePath)) {
            String errorMessage = I18n.get(resourceResolver.adaptTo(ResourceBundle.class), "Invalid request, empty resource path not allowed.");
            throw new OmniSearchException(errorMessage);
        }

        Resource resource = resourceResolver.getResource(resourcePath);
        String title = requestParameters.get(REQUEST_PARAMETER_TITLE);
        //update
        ModifiableValueMap mvm = resource.adaptTo(ModifiableValueMap.class);
        if (!StringUtils.isEmpty(title)) {
            String existingTitle = mvm.get(JcrConstants.JCR_TITLE, "");
            if (!existingTitle.equals(title)) {
                mvm.put(JcrConstants.JCR_TITLE, title);
            }
        }
        //update the query
        saveQuery(resourceResolver, resource.getPath(), requestParameters);
        session.save();

        return resource;
    }

    /**
     * returns the search path of query respective to console
     */
    private String getSavedSearchPath(ResourceResolver resourceResolver) throws RepositoryException {
        String path = null;
        Authorizable user = resourceResolver.adaptTo(Authorizable.class);
        if(user != null) {
            path = user.getPath() + PREFERENCES_SAVED_SEARCH_PATH + getID();
        }
        return path;
    }

    /**
     * parse the query string.
     *
     * @param query query string.
     * @return map of key value pairs.
     * @throws UnsupportedEncodingException if encoding is unsupported
     */
    private Map parse(final String query) throws UnsupportedEncodingException {
        Map params = new HashMap();
        if (!StringUtils.isBlank(query)) {
            String[] pairs = query.split(PARAM_SEP);
            for (String pair : pairs) {
                int idx = pair.indexOf(NAME_VALUE_SEPARATOR);
                if (idx >= 0) {
                    String name = URLDecoder.decode(pair.substring(0, idx), "UTF-8");
                    String value = URLDecoder.decode(pair.substring(idx + 1), "UTF-8");
                    params.put(name, value);
                }
            }
            if(params.get(LOCATION_PARAM) == null) {
                params.put(LOCATION_PARAM, getID());
            }
        }
        return params;
    }

    /**
     * Store query as a property on the node
     *
     * @param resolver resource resolver
     * @param resPath the target path where the query is saved
     * @throws IOException
     * @throws RepositoryException
     */
    private void saveQuery(ResourceResolver resolver, String resPath, Map requestParams) throws IOException, RepositoryException {
        Session session = resolver.adaptTo(Session.class);

        final Map params = new HashMap();

        for (final Object nameObj : requestParams.keySet()) {
            String name = (String) nameObj;
            if (!ignoreParameter(name)) {
                params.put(name, requestParams.get(name));
            }
        }
        PredicateGroup predicates = PredicateGroup.create(params);
        Query query = getQueryBuilder().createQuery(predicates, session);

        getQueryBuilder().storeQuery(query, resPath + "/" + NODE_SAVEDQUERY, false, session);
    }

    /**
     * Skip default request parameters in query processing
     *
     * @param parameter parameter name
     * @return true if the request parameter should be ignored, false otherwise
     */
    private boolean ignoreParameter(String parameter) {
        return (defaultReqParams.contains(parameter)
                || parameter.startsWith("jcr:")
                || parameter.startsWith("sling:")
                || parameter.startsWith("cq:"));
    }

    /**
     * Add created and last modified mixin type to node so that created, created by, lastmodified and lastmodified by properties sets to node
     * @param res
     * @throws OmniSearchException 
     */
    private void addMixinType(Resource res) throws OmniSearchException {
        Node contentNode = res.adaptTo(Node.class);
        if(contentNode != null) {
            try {
                if(contentNode.canAddMixin(JcrConstants.MIX_CREATED)) {
                    contentNode.addMixin(JcrConstants.MIX_CREATED);
                }
                if(contentNode.canAddMixin(JcrConstants.MIX_LAST_MODIFIED)) {
                    contentNode.addMixin(JcrConstants.MIX_LAST_MODIFIED);
                }
            } catch (Exception e) {
                logger.error("unable to add mixin type");
                throw new OmniSearchException(e);
            }
        }
    }

    abstract public QueryBuilder getQueryBuilder();
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy