
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