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

com.adobe.aemds.guide.utils.GuidePropertyProviderUtils Maven / Gradle / Ivy

/*************************************************************************
 *
 * ADOBE CONFIDENTIAL
 * __________________
 *
 *  Copyright 2015 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 may be covered by U.S. and Foreign Patents,
 * patents in process, 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.aemds.guide.utils;

import com.adobe.aemds.guide.model.ReCaptchaConfiguration;
import com.adobe.aemds.guide.service.GuideException;
import com.adobe.aemds.guide.service.GuideLCServiceConnector;
import com.adobe.aemds.guide.service.GuideProgressiveStrategyManager;
import com.adobe.aemds.guide.service.external.FormDataModelService;
import com.adobe.aemds.guide.service.internal.AdobeSignConfigurationService;
import com.adobe.aemds.guide.service.internal.ReCaptchaConfigService;
import com.adobe.aemds.guide.service.internal.ReCaptchaConfigurationService;
import com.adobe.aemds.guide.service.internal.TypekitConfigurationService;
import com.adobe.aemds.guide.themes.GuideThemeConstants;
import com.adobe.forms.common.service.DataProviderBase;
import com.adobe.forms.common.service.FormDataProviderRegistry;
import com.adobe.forms.foundation.cloudconfig.ICloudConfigurationResourceProvider;
import com.adobe.granite.resourceresolverhelper.ResourceResolverHelper;
import com.adobe.granite.ui.clientlibs.HtmlLibraryManager;
import com.adobe.granite.xss.XSSAPI;
import com.day.cq.commons.jcr.JcrConstants;
import com.day.cq.i18n.I18n;
import com.day.cq.wcm.api.NameConstants;
import com.day.cq.wcm.api.Template;
import com.day.cq.wcm.webservicesupport.Configuration;
import com.day.cq.wcm.webservicesupport.ConfigurationManager;
import org.apache.commons.lang.StringUtils;
import org.apache.felix.scr.annotations.*;
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.ResourceResolverFactory;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.commons.json.JSONArray;
import org.apache.sling.commons.json.JSONException;
import org.apache.sling.commons.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.Node;
import javax.jcr.RepositoryException;
import java.util.*;
import java.util.Map.Entry;
/**
 * @pad.exclude Exclude from Published API.
 */
@Service(value=GuidePropertyProviderUtils.class)
@Component(immediate = true, metatype = false)
public class GuidePropertyProviderUtils {

    private static final String COMPONENT_PATH = "component-path";
    private final Logger logger = LoggerFactory.getLogger(GuidePropertyProviderUtils.class);

    @Reference(policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.OPTIONAL_UNARY)
    private GuideLCServiceConnector guideLCServiceConnector;

    @Reference(policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.MANDATORY_UNARY)
    private GuideProgressiveStrategyManager guideProgressiveStrategyManager;

    @Reference(policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.OPTIONAL_UNARY)
    protected FormDataModelService formDataModelService;

    @Reference
    private HtmlLibraryManager htmlLibraryManager;

    @Reference
    ResourceResolverHelper resourceResolverHelper;

    @Reference
    ResourceResolverFactory resourceResolverFactory;

    @Reference
    FormDataProviderRegistry prefillRegistry;

    @Reference
    private ReCaptchaConfigService reCaptchaConfigService;

    @Reference
    private ReCaptchaConfigurationService reCaptchaConfigurationService;

    @Reference
    private TypekitConfigurationService typekitConfigurationService;

    @Reference(policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.OPTIONAL_UNARY)
    private AdobeSignConfigurationService adobeSignConfigurationService;

    @Reference(policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.OPTIONAL_UNARY)
    private ICloudConfigurationResourceProvider configurationResourceProvider;

    private static String SUBMITACTION = "submitAction";
    private static String CAPTCHA_SERVICE = "CaptchaService";


    /**
     * @param searchPaths
     * @param options
     */
     public JSONArray queryAppearance(String[] searchPaths, Map options) {
         try {
             String guideNodeClass = options.get(GuideConstants.GUIDE_NODE_CLASS);
             ResourceResolver resolver = resourceResolverHelper.getResourceResolver();
             JSONArray response = new JSONArray();
             JSONObject firstObj = new JSONObject();
             firstObj.put("text", "none");
             firstObj.put("value", JSONObject.NULL);
             response.put(firstObj);
             if (guideNodeClass != null &&
                     !guideNodeClass.isEmpty() &&
                     GuideUtils.listContains(GuideConstants.GUIDE_FIELDS_CLASS_NAMES, guideNodeClass,Boolean.TRUE)) {
                 for (int i = 0; i < searchPaths.length; i++) {
                     String query = "SELECT * FROM nt:base WHERE "+GuideConstants.FD_GUIDE_COMPONENT_TYPE+"= 'fd/af/components/appearance/"+guideNodeClass+"' AND jcr:path LIKE '"+searchPaths[i]+"%'";
                     Iterator appearances = resolver.findResources(query, "sql");
                     if (appearances != null) {
                         while (appearances.hasNext()) {
                             Resource r = appearances.next();
                             String path = r.getPath();
                             // TODO: Add notion of resource merger here
                             if (path.startsWith("/libs/") || path.startsWith("/apps/")) {
                                 path = path.substring(6);
                             }
                             if (!StringUtils.isEmpty(path)) {
                                 String text = NodeStructureUtils.getLayoutDescription(r);
                                 if (StringUtils.isEmpty(text)) {
                                     text = path;
                                 }
                                 String qtip = NodeStructureUtils.getLayoutQtip(r);
                                 if (StringUtils.isEmpty(qtip)) {
                                     qtip = text;
                                 }
                                 JSONObject respObj = new JSONObject();
                                 respObj.put("text", text);
                                 respObj.put("value", path);
                                 respObj.put("qtip", qtip);
                                 response.put(respObj);
                             }
                         }
                     }
                 }
             }
             return response;
         } catch (JSONException e) {
             logger.error(e.getMessage(),e);
             throw new GuideException(e);
         }

     }

     /**
      * @param formType It can be AF,AD or Fragment
      * @param resourceType
      * @param options
      */
     public JSONObject queryFragFinder(String formType, String resourceType, Map options) {
         String searchPathsForFragments[] = new String[10];
        final ResourceResolver resolver = resourceResolverHelper.getResourceResolver();
        final String QUERY = "query";
        final String START = "start";
        final String LIMIT = "limit";

        final String QUERY_LANG = "xpath";
        final String QUERY_ROOT = "/jcr:root";
        final String QUERY_ELEMENT_TYPE = "//element(*, dam:Asset)";
        // sort by jcr:content/jcr:lastModified property
        final String SORT_BY_LAST_MODIFIED = " order by jcr:content/@jcr:lastModified descending";
        final String TYPE_PREDICATE = "jcr:content/metadata/@formmodel=";
        final String TAG_PREDICATE = "jcr:content/metadata/@cq:tags=";
        final String FORM_PREDICATE = "jcr:content/@type='" + formType + "'";
        final String ASSET_TYPE_NONE = "'none'";
        final String ASSET_TYPE_XSD = "'xmlschema'";
        final String ASSET_TYPE_JSON = "'jsonschema'";
        final String ASSET_TYPE_SCHEMA = "'schema'";
        final String ASSET_TYPE_FORM_DATA_MODEL = "'formdatamodel'";
        final String ASSET_TYPE_XFA = "'formtemplates'";

        try {
            JSONArray fragRefs = new JSONArray();
            String nameOrTitle = options.get(QUERY);
            String alreadySentNodesString = options.get(START);
            String filterPath = options.get(GuideConstants.FILTER_PATH);
            String maxLimitToSendString = options.get(LIMIT);
            List typePredicates = new ArrayList();
            List tagPredicates = new ArrayList();
            String assetTypeParam = options.get(GuideConstants.ASSET_TYPE);
            String tagParam = options.get(GuideConstants.META_TAGS);
            String combinedTypePredicates = "";

            if (StringUtils.isNotBlank(assetTypeParam)) {
                for (String assetType : StringUtils.remove(assetTypeParam, ' ').split(",")) {
                    String filterType = null;
                    switch (assetType) {
                        case GuideConstants.BASIC:
                            filterType = ASSET_TYPE_NONE;
                            break;
                        case GuideConstants.XFA_BASED:
                            filterType = ASSET_TYPE_XFA;
                            break;
                        case GuideConstants.SCHEMA:
                            filterType = ASSET_TYPE_SCHEMA;
                            break;
                        case GuideConstants.FDM:
                            filterType = ASSET_TYPE_FORM_DATA_MODEL;
                            break;
                    }
                    if (filterType != null) { // no predicate filter if assetType unspecified
                        if(filterType.equals(ASSET_TYPE_SCHEMA)){
                            typePredicates.add(TYPE_PREDICATE + ASSET_TYPE_XSD);
                            typePredicates.add(TYPE_PREDICATE + ASSET_TYPE_JSON);
                        } else {
                            typePredicates.add(TYPE_PREDICATE + filterType);
                        }
                    }
                }
            }

            if (StringUtils.isNotBlank(tagParam)) {
                for (String tag : StringUtils.remove(tagParam, ' ').split(",")) {
                    tagPredicates.add(TAG_PREDICATE + "'" + tag + "'");
                }
            }

            // adding asset type predicates to the query, combinedTypePredicate contains query for tags as well as asset type
            if (typePredicates.size() > 0) {
                combinedTypePredicates += " and " + "( " + StringUtils.join(typePredicates, " or ") + " )";
            }

            // adding tag predicates to the query, combinedTypePredicate contains query for tags as well as asset type
            if (tagPredicates.size() > 0) {
                combinedTypePredicates += " and " + "( " + StringUtils.join( tagPredicates, " or ") + " )";
            }

            // if no path filter is provided then default path will be used
            searchPathsForFragments[0] = StringUtils.isNotBlank(filterPath) ? filterPath : "/content/dam/formsanddocuments";
            for (String searchPathsForFragment : searchPathsForFragments) {
                // using indexed property dam:Asset
                // Also relying on FM meta property  affragment=1
                final String query = QUERY_ROOT + searchPathsForFragment + QUERY_ELEMENT_TYPE +
                        "[" +
                        FORM_PREDICATE +
                        combinedTypePredicates +
                        "]" +
                        SORT_BY_LAST_MODIFIED;
                Iterator fragmentNodes = resolver.findResources(query, QUERY_LANG);
                //  GuideFragment Library additionally can send query for search, and start to tell where to
                // start sending assets from and limit to indicate how much to send
                int alreadySentNodes = -1,
                        maxLimitToSend = -1,
                        nodesLeft = -1;

                if (maxLimitToSendString != null && maxLimitToSendString.length() > 0) {
                    maxLimitToSend = Integer.parseInt(maxLimitToSendString);
                }
                if (alreadySentNodesString != null && alreadySentNodesString.length() > 0) {
                    alreadySentNodes = Integer.parseInt(alreadySentNodesString);
                    nodesLeft = maxLimitToSend - alreadySentNodes;
                }

                if (fragmentNodes != null) {
                    // this code handles scroll  request
                    // where more nodes are demanded
                    if (alreadySentNodes != -1) {
                        for (int node = 0; node < alreadySentNodes; node++) {
                            if (fragmentNodes.hasNext()) {
                                fragmentNodes.next();
                            }
                        }
                    }
                    while (fragmentNodes.hasNext()) {
                        //  decreasing node count by one
                        // if there were some left
                        if (nodesLeft != -1) {
                            if (nodesLeft-- == 0) {
                                break;
                            }
                        }
                        Resource r = fragmentNodes.next();
                        // Again relying on FM node structure
                        // need to check name and titile against metadate node
                        final ValueMap properties = r.getChild(GuideConstants.JCR_CONTENT_NODENAME)
                                .getChild(GuideConstants.METADATA_NODENAME).getValueMap();

                        boolean titleOrNameMatches = false;
                        if (nameOrTitle != null && nameOrTitle.length() > 0) {
                            titleOrNameMatches = StringUtils.containsIgnoreCase((String) properties.get(GuideConstants.NAME), nameOrTitle) ||
                                    StringUtils.containsIgnoreCase((String) properties.get((String) GuideConstants.TITLE_NODENAME), nameOrTitle);
                        }
                        if (nameOrTitle == null || nameOrTitle.isEmpty() || titleOrNameMatches) {
                            JSONObject fragObj = new JSONObject();
                            String name = properties.get(GuideConstants.TITLE_NODENAME, "");
                            if (name.length() == 0) {
                                name = r.getName();
                            }
                            String resourcePath = r.getPath();
                            // Relying on asset structure for displaying thumbnail of fragment
                            String imageRef = resourcePath + GuideConstants.JCR_CONTENT + GuideConstants.PATH_TO_THUMBNAIL_319_BY_319;
                            String fragRef = resourcePath;
                            // FM needs us to remove jcr:content
                            fragRef = StringUtils.substringBefore(fragRef, GuideConstants.JCR_CONTENT);
                            fragObj.put(GuideConstants.NAME, name);
                            fragObj.put(GuideConstants.JCR_PRIMARY_TYPE, GuideConstants.NT_UNSTRUCTURED);
                            fragObj.put(GuideConstants.SLING_RESOURCE_TYPE, resourceType);
                            Resource componentResource = resolver.getResource(resourceType);
                            fragObj.put(COMPONENT_PATH, componentResource.getPath());
                            fragObj.put(GuideConstants.IMAGE, imageRef);
                            fragObj.put(GuideConstants.PATH, fragRef);
                            fragRefs.put(fragObj);
                        }

                    }
                }
            }
            JSONObject response = new JSONObject();
            response.put("fragRefs", fragRefs);
            return response;
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new GuideException(e);
        }
    }

     /**
      * @param searchPaths
      * @param options
      * @param request
      */
    public JSONArray queryLayout(String[] searchPaths, Map options, SlingHttpServletRequest request) {
        JSONArray response = new JSONArray();
        String layoutType = "",isAfField = "";
        if (options.get("layoutType") != null && options.get("layoutType").length() > 0) {
            layoutType = options.get("layoutType");
            // parameter to check if the request comes from a field or container
            isAfField = options.get("isAfField");
        }

        if (layoutType.length() > 0) {
            try {
                if (GuideConstants.LAYOUT_FIELD.equals(layoutType)) {
                    JSONObject firstObj = new JSONObject();
                    firstObj.put("text", "");
                    firstObj.put("value", "");
                    response.put(firstObj);
                }

                ResourceResolver resolver = resourceResolverHelper.getResourceResolver();
                ArrayList listOfLayouts = new ArrayList();
                for (int i = 0; i < searchPaths.length; i++) {
                    String query = "SELECT * FROM nt:base WHERE " + GuideConstants.FD_GUIDE_COMPONENT_TYPE + "='" + layoutType + "' AND jcr:path LIKE '" + searchPaths[i] + "%'";
                    Iterator layouts = resolver.findResources(query, "sql");
                    if (layouts != null) {
                        while (layouts.hasNext()) {
                            Resource r = layouts.next();
                            String path = r.getPath();
                            // TODO: Add notion of resource merger here
                            if (path.startsWith("/libs/") || path.startsWith("/apps/")) {
                                path = path.substring(6);
                            }
                            String desc = NodeStructureUtils.getLayoutDescription(r);
                            if(!listOfLayouts.contains(path)) {
                                JSONObject respObj = new JSONObject();
                                // adding guideNavigatorTabproperty only for panel layoutType
                                if(GuideConstants.LAYOUT_PANEL.equals(layoutType)) {
                                    respObj.put(GuideConstants.LAYOUT_TAB_TYPE_PROPERTY, NodeStructureUtils.getGuideNavigatorTabProperty(r));
                                }
                                // if its a field and defaultLayout has been selected, alter the description to "inherit layout from container"
                                // else let it remain as it is
                                I18n i18n = new I18n(request.getResourceBundle(new Locale(request.getLocale().getLanguage())));
                                desc= ("true".equals(isAfField) && path != null && path.indexOf(GuideConstants.DEFAULT_FIELD_LAYOUT_NAME)>0)?
                                        i18n.get(GuideConstants.DESC_FOR_DEFAULT_FIELD_LAYOUT) : desc;
                                respObj.put("text", desc == null ? r.getPath() : desc);
                                String qtip = NodeStructureUtils.getLayoutQtip(r);
                                respObj.put("value", path);
                                if(GuideConstants.LAYOUT_FIELD.equals(layoutType)) {
                                    XSSAPI xssapi = request.adaptTo(XSSAPI.class);
                                    qtip = GuideUtils.encodeForHtml(qtip, xssapi);
                                }
                                respObj.put("qtip", qtip);
                                // for new layouts, there would always be granite:data node
                                // for old layouts, we will check the actual content structure for the nodes
                                Resource graniteData = r.getChild("granite:data");
                                JSONObject graniteDataJson = null;
                                if (graniteData != null) {
                                    // we don't save the properties with default values in node structure
                                    graniteDataJson = new JSONObject(graniteData.getValueMap());
                                } else {
                                    // fallback is to support custom layout written under /apps
                                    // only these properties would be supported for old layouts
                                    // if any new properties are required, please add it as part of granite:data node
                                    graniteDataJson = new JSONObject();
                                    // only if these properties are present in the layout resource add it else leave it
                                    if(r.getValueMap().get(GuideConstants.LAYOUT_TAB_TYPE_PROPERTY) != null){
                                        graniteDataJson.put(GuideConstants.LAYOUT_TAB_TYPE_PROPERTY, r.getValueMap().get(GuideConstants.LAYOUT_TAB_TYPE_PROPERTY, "tab"));
                                    }
                                    if(r.getValueMap().get(GuideConstants.LAYOUT_NON_NAVIGABLE_PROPERTY) != null){
                                        graniteDataJson.put(GuideConstants.LAYOUT_NON_NAVIGABLE_PROPERTY, r .getValueMap().get(GuideConstants.LAYOUT_NON_NAVIGABLE_PROPERTY, false));
                                    }
                                    if(r.getValueMap().get(GuideConstants.LAYOUT_ENABLE_LAYOUT_OPTIMIZATION) != null){
                                        graniteDataJson.put(GuideConstants.LAYOUT_ENABLE_LAYOUT_OPTIMIZATION, r.getValueMap().get(GuideConstants.LAYOUT_ENABLE_LAYOUT_OPTIMIZATION, false));
                                    }
                                }
                                respObj.put("granite:data", graniteDataJson);
                                listOfLayouts.add(path);
                                response.put(respObj);
                            }
                        }
                    }
                }
            } catch (JSONException e) {
                logger.error(e.getMessage(), e);
                throw new GuideException(e);
            }
        } else {
            logger.warn("Request did not have layout type mentioned.So, not returning any layout types");
        }
        return response;
    }

    public JSONArray queryPrefillServiceProviders() {
        JSONArray result = new JSONArray();
        try {
            // adding first value as blank for prefill service providers
            JSONObject firstObj = new JSONObject();
            firstObj.put("text", "");
            firstObj.put("value", "");
            firstObj.put("qtip", "");
            result.put(firstObj);
            List providers = prefillRegistry.getProviders();
            Iterator providerIterator = providers.iterator();
            while (providerIterator.hasNext()) {
                DataProviderBase provider = providerIterator.next();
                String serviceName = provider.getServiceName();
                String serviceDescription = provider.getServiceDescription();
                if(StringUtils.isNotBlank(serviceName)) {
                    JSONObject obj = new JSONObject();
                    obj.put("text", serviceDescription);
                    obj.put("value", serviceName);
                    obj.put("qtip", serviceDescription);
                    result.put(obj);
                }
            }
        } catch(JSONException e) {
            logger.error(e.getMessage(), e);
            throw new GuideException(e);
        }
        return result;
    }

    /**
     * Queries for theme assets present in the system
     */
    public JSONArray queryTheme() {
        try {
            ResourceResolver resolver = resourceResolverHelper.getResourceResolver();
            String searchPathsForThemes[] = {GuideThemeConstants.FD_GUIDE_THEME_PATH};
            JSONArray response = new JSONArray();
            //To be added along with theme changes
            JSONObject firstObj = new JSONObject();
            firstObj.put("text", "");
            firstObj.put("value", "");
            firstObj.put("name", "");
            response.put(firstObj);

            for (int i = 0; i < searchPathsForThemes.length; i++) {
                Iterator themes = resolver.findResources("SELECT * FROM [dam:Asset] as t WHERE ISDESCENDANTNODE(t, '" + searchPathsForThemes[i] + "')", "JCR-SQL2");
                if (themes != null) {
                    while (themes.hasNext()) {
                        Resource r = themes.next();
                        Resource metadataResource = r.getChild("jcr:content/metadata");
                        Node node = metadataResource.adaptTo(Node.class);
                        try {
                            Node themeNode = node.getParent().getParent();
                            String themeName = node.hasProperty("title") ? node.getProperty("title").getString() : themeNode.getName();
                            String themeNodePath = themeNode.getPath();
                            JSONObject respObj = new JSONObject();
                            respObj.put("text", themeName);
                            respObj.put("value", themeNodePath);
                            respObj.put("name", themeName);
                            response.put(respObj);
                        } catch (RepositoryException e) {
                            logger.error(e.getMessage(), e);
                            throw new GuideException(e);
                        }
                    }
                }
            }
            return response;
        } catch (JSONException e) {
            logger.error(e.getMessage(), e);
            throw new GuideException(e);
        }
    }

    /**
     * Queries for submit actions present in the system
     * @param searchPaths
     */
    public JSONArray queryAction(String[] searchPaths) {
        Map resourceMappings = new HashMap();
        try {
            ResourceResolver resolver = resourceResolverHelper.getResourceResolver();
            JSONArray response = new JSONArray();
            for (int i = 0; i < searchPaths.length; i++) {
                Iterator layouts = resolver.findResources("SELECT *  FROM nt:base  WHERE " +
                        GuideConstants.FD_GUIDE_COMPONENT_TYPE + " = 'fd/af/components/action' AND jcr:path LIKE '"+ searchPaths[i] + "%'", "sql");
                if (layouts != null) {
                    resourceMappings.clear();
                    while (layouts.hasNext()) {
                        Resource r = layouts.next();
                        String path = r.getPath();
                        if (path.startsWith("/libs/") || path.startsWith("/apps/")) {
                            path = path.substring(6);
                        }
                        if (!resourceMappings.containsKey(path) || r.getPath().startsWith("/apps/")) {
                            resourceMappings.put(path, r);
                        }
                    }
                    for (java.util.Map.Entry entry : resourceMappings.entrySet()) {
                        Resource resource = entry.getValue();
                        JSONObject respObj = new JSONObject();
                        String desc = NodeStructureUtils.getLayoutDescription(resource);
                        respObj.put("text", desc == null ? entry.getKey() : desc);
                        respObj.put("value", entry.getKey());
                        respObj.put("name", resource.getName());
                        respObj.put("path", resource.getPath());
                        if (resource.getResourceSuperType() != null) {
                            respObj.put("sling:resourceSuperType", resource.getResourceSuperType());
                        }
                        respObj.put("templatePath", this.getComponentTemplatePath(resource));
                        response.put(respObj);
                    }
                }
            }
            return response;
        } catch (JSONException e) {
            logger.error(e.getMessage(), e);
            throw new GuideException(e);
        }
    }

    /**
     * Query to get all progressive strategies
     * @param searchPaths
     */
    public JSONArray queryProgressiveStrategies(String[] searchPaths) {
        //this provides the list of all the implementations registered as services for GuideProgressiveStrategy
        try {
            ResourceResolver resolver = resourceResolverHelper.getResourceResolver();
            JSONArray response = new JSONArray();
            for (String searchPath : searchPaths) {
                Iterator services = resolver.findResources("SELECT *  FROM nt:base  WHERE " +
                        GuideConstants.FD_GUIDE_COMPONENT_TYPE + " = '/libs/fd/af/components/progressiveDataCapture' AND jcr:path LIKE '" + searchPath + "%'", "sql");
                if (services != null) {
                    while (services.hasNext()) {
                        Resource r = services.next();
                        String value = r.getPath();
                        ValueMap properties = r.getValueMap();
                        String displayName = NodeStructureUtils.getLayoutDescription(r);
                        String strategyName = (String) properties.get("strategyName", "");

                        JSONObject respObj = new JSONObject();
                        respObj.put("text", displayName);
                        respObj.put("qtip", displayName);
                        respObj.put("value", value);
                        respObj.put("strategyName", strategyName);
                        respObj.put("type", "service");
                        response.put(respObj);
                    }
                }
            }
            return response;
        } catch (JSONException e) {
            logger.error(e.getMessage(), e);
            throw new GuideException(e);
        }
    }

    /**
     * Queries the progressive layouts present in the system
     * @param searchPaths
     */
    public JSONArray queryProgressiveLayout(String[] searchPaths) {
        // this provides list of all the layouts available for progressive data capture
        try {
            ResourceResolver resolver = resourceResolverHelper.getResourceResolver();
            JSONArray response = new JSONArray();
            for (int i = 0; i < searchPaths.length; i++) {
                Iterator layouts = resolver.findResources("SELECT *  FROM nt:base  WHERE " + GuideConstants.FD_GUIDE_COMPONENT_TYPE + " = 'fd/af/layouts/progressive' AND jcr:path LIKE '" + searchPaths[i] + "%'", "sql");

                if (layouts != null) {
                    while (layouts.hasNext()) {
                        Resource r = layouts.next();
                        String desc = NodeStructureUtils.getLayoutDescription(r);
                        JSONObject respObj = new JSONObject();
                        respObj.put("text", desc == null ? r.getPath() : desc);
                        String qtip = NodeStructureUtils.getLayoutQtip(r);
                        respObj.put("qtip", qtip);
                        String path = r.getPath();
                        respObj.put("value", path + "/layout.html");
                        response.put(respObj);
                    }
                }
            }
            return response;
        } catch (JSONException e) {
            logger.error(e.getMessage(), e);
            throw new GuideException(e);
        }
    }

    /**
     * Queries for validators present in the system
     * @param guideNodeClass
     * @param searchPaths
     */
    public JSONArray queryValidators(String guideNodeClass, String[] searchPaths) {
        return queryType(guideNodeClass, searchPaths, "validator");
    }

    /**
     * Queries for formatters present in the system
     * @param guideNodeClass
     * @param searchPaths
     */
    public JSONArray queryFormatters(String guideNodeClass, String[] searchPaths) {
        return queryType(guideNodeClass, searchPaths, "formatter");
    }

    /**
     * Queries for particular type (formatters/validators) present in the system
     * @param guideNodeClass
     * @param searchPaths
     * @param type
     */
    private JSONArray queryType(String guideNodeClass, String[] searchPaths, String type) {
        try {
            ResourceResolver resolver = resourceResolverHelper.getResourceResolver();
            JSONArray response = new JSONArray();
            JSONObject firstObj = new JSONObject();

            if (!GuideUtils.listContains(GuideConstants.GUIDE_HTML5_INPUT_TYPES, guideNodeClass,Boolean.TRUE)) {
                firstObj.put("text", "Select");
                firstObj.put("value", "");
            }
            else {
                firstObj.put("text", "Default");
                firstObj.put("value", GuideUtils.getDefaultPattern(guideNodeClass));
            }
            response.put(firstObj);

            if (guideNodeClass != null && !guideNodeClass.isEmpty() &&
                    (GuideUtils.listContains(GuideConstants.GUIDE_FIELDS_CLASS_NAMES, guideNodeClass,Boolean.TRUE) ||
                     GuideUtils.listContains(GuideConstants.GUIDE_HTML5_INPUT_TYPES, guideNodeClass,Boolean.TRUE))) {
                    for (int i = 0; i < searchPaths.length; i++) {
                        String query = "SELECT * FROM nt:base WHERE " + GuideConstants.FD_GUIDE_COMPONENT_TYPE + "= 'fd/af/components/" + type + "/" + guideNodeClass + "' AND jcr:path LIKE '" + searchPaths[i] + "%'";
                        Iterator formats = resolver.findResources(query, "sql");
                        while (formats.hasNext()) {
                           Resource format = formats.next();
                           if (format != null) {
                              ValueMap properties = format.getValueMap();
                              Iterator> it = properties.entrySet().iterator();
                              while (it.hasNext()) {
                                  Map.Entry pairs = (Map.Entry) it.next();
                                  String key = (String) pairs.getKey();
                                  //This condition is sufficient for property guideComponentType to get ignored
                                  if (key.startsWith("pattern")) {
                                       JSONObject respObj = new JSONObject();
                                       String value = (String) pairs.getValue();
                                       // Split the pattern into key and value(key=value)
                                       String[] arr = value.split("=", 2);
                                       respObj.put("text", arr[0]);
                                       respObj.put("value", arr[1]);
                                       response.put(respObj);
                              }
                            }
                        }
                        break;
                    }
                }
            }
            JSONObject lastObj = new JSONObject();
            lastObj.put("text", "Custom");
            lastObj.put("value", "custom");
            response.put(lastObj);
            return response;
        } catch (JSONException e) {
            logger.error(e.getMessage(), e);
            throw new GuideException(e);
        }
    }

    /**
     * Queries for submit actions or auto save action based on the given parameters
     * @param searchPaths
     * @return
     */
    public JSONArray queryCaptchaService(String[] searchPaths) {
        String guideComponentType = GuideConstants.GUIDE_COMPONENT_TYPE_CAPTCHA;

        try {
            ResourceResolver resolver = resourceResolverHelper.getResourceResolver();
            List listOfCaptchaServices = new ArrayList();
            JSONArray response = new JSONArray();
            //for (int i = 0; i < searchPaths.length; i++) {
            for (String searchPath : searchPaths) {
                Iterator actions = resolver.findResources("SELECT *  FROM nt:base  WHERE " + GuideConstants.FD_GUIDE_COMPONENT_TYPE + " = '" + guideComponentType + "' AND jcr:path LIKE '" + searchPath + "%'", "sql");
                if (actions != null) {
                    while (actions.hasNext()) {
                        Resource r = actions.next();
                        String value = r.getPath();
                        if (value.startsWith("/libs/") || value.startsWith("/apps/")) {
                            value = value.substring(6);
                        }
                        ValueMap properties = r.getValueMap();
                        String text = (String) properties.get("jcr:description", "");
                        Resource graniteData = r.getChild("granite:data");
                        JSONObject graniteDataJson = null;
                        String captchaServiceProvider = "";
                        if (graniteData != null) {
                            graniteDataJson = new JSONObject(graniteData.getValueMap());
                            try{
                                captchaServiceProvider =(String) graniteDataJson.get("captcha.service.provider");
                            }catch (Exception e){
                                logger.error("captcha.service.provider property not found for " + r.getPath() + ". skipping this.", e);
                            }

                            if( StringUtils.isEmpty(captchaServiceProvider) ){
                                continue;
                            }
                        }else{
                            continue;
                        }

                        // In case of overlay, we honor only one
                        if (!listOfCaptchaServices.contains(value)) {
                            JSONObject respObj = new JSONObject();
                            respObj.put("text", text);
                            listOfCaptchaServices.add(value);
                            respObj.put("value", captchaServiceProvider);

                            graniteDataJson.put("captcha-path", value);
                            respObj.put("granite:data", graniteDataJson);

                            response.put(respObj);
                        }
                    }
                }
            }
            return response;
        } catch (JSONException e) {
            throw new GuideException(e);
        }
    }

    /**
     *
     * @param searchPaths
     * @param options
     */
    public JSONArray querySubmitOrAutoSaveAction(String[] searchPaths, Map options) {
        String guideComponentType,
               type = options.get("type"),
               requestedDataModel = options.get("guideDataModel");
        if(type.equalsIgnoreCase(SUBMITACTION)) {
            guideComponentType = "fd/af/components/guidesubmittype";
        } else {
            guideComponentType = "fd/af/components/guideautosavetype";
        }
        try {
            ResourceResolver resolver = resourceResolverHelper.getResourceResolver();
            ArrayList listOfSubmitAction = new ArrayList();
            JSONArray response = new JSONArray();
            for (int i = 0; i < searchPaths.length; i++) {
                Iterator actions = resolver.findResources("SELECT *  FROM nt:base  WHERE " + GuideConstants.FD_GUIDE_COMPONENT_TYPE + " = '" + guideComponentType + "' AND jcr:path LIKE '" + searchPaths[i] + "%'", "sql");
                if (actions != null) {
                    while (actions.hasNext()) {
                        Resource r = actions.next();
                        String value = r.getPath();
                        if (value.startsWith("/libs/") || value.startsWith("/apps/")) {
                            value = value.substring(6);
                        }
                        ValueMap properties = r.getValueMap();
                        String text = (String) properties.get("jcr:description", "");
                        String qtip = properties.get("qtip", "");
                        Resource graniteData = r.getChild("granite:data");
                        JSONObject graniteDataJson = new JSONObject();
                        String submitService = properties.get(GuideConstants.SUBMIT_SERVICE, "");
                        if (graniteData != null) {
                            graniteDataJson = new JSONObject(graniteData.getValueMap());
                        }
                        graniteDataJson.append(GuideConstants.SUBMIT_SERVICE, submitService);
                        Boolean addOption = true;
                        if (requestedDataModel != null) {
                            String dataModel = (String) properties.get("guideDataModel", "");
                            if (dataModel.toLowerCase().contains(requestedDataModel.toLowerCase())) {
                                addOption = true;
                            } else {
                                addOption = false;
                            }
                        }
                        // In case of overlay, we honor only one
                        if (addOption && !listOfSubmitAction.contains(value)) {
                            JSONObject respObj = new JSONObject();
                            respObj.put("text", text);
                            listOfSubmitAction.add(value);
                            respObj.put("value", value);
                            if (graniteDataJson != null) {
                                respObj.put("granite:data", graniteDataJson);
                            }
                            respObj.put("qtip", qtip);
                            response.put(respObj);
                        }
                    }
                }
            }
            return response;
        } catch (JSONException e) {
            logger.error(e.getMessage(), e);
            throw new GuideException(e);
        }
    }

    /**
     * Queries for LC services present in the system
     * @return JSON array containing the LC Service description
     */
    public JSONArray queryLcProcess() {
        try {
            JSONArray response = new JSONArray();
            List services = guideLCServiceConnector.getServices();
            for (String s : services) {
                JSONObject respObj = new JSONObject();
                respObj.put("text", s);
                respObj.put("value", s);
                response.put(respObj);
            }
            return response;
        } catch (Exception e) {
            logger.error("Cannot fetch LCServices", e);
            throw new GuideException(e);
        }
    }

    /**
     * Queries for mobile layouts present in the system
     * @param searchPaths
     */
    public JSONArray queryMobileLayout(String[] searchPaths) {
        try {
            ResourceResolver resolver = resourceResolverHelper.getResourceResolver();
            JSONArray response = new JSONArray();
            ArrayList listOfMobileLayoutPath = new ArrayList();
            for (int i = 0; i < searchPaths.length; i++) {
                Iterator layouts = resolver.findResources("SELECT *  FROM nt:base  WHERE " + GuideConstants.FD_GUIDE_COMPONENT_TYPE + " = 'fd/af/layouts/mobile' AND jcr:path LIKE '" + searchPaths[i] + "%'", "sql");
                if (layouts != null) {
                    while (layouts.hasNext()) {
                        Resource r = layouts.next();
                        String path = r.getPath();
                        String desc = NodeStructureUtils.getLayoutDescription(r);
                        // todo: This approach would take text and qtip from the first resource which is found, it may be /libs or /apps
                        // Ideally we should honor the properties from libs
                        if (path.startsWith("/libs/") || path.startsWith("/apps/")) {
                            path = path.substring(6);
                        }
                        // if the path doesn't exists in the list, only then create an object for it
                        // This is done so that we don't show the overlay
                        if(!listOfMobileLayoutPath.contains(path)) {
                            JSONObject respObj = new JSONObject();
                            respObj.put("text", desc == null ? r.getPath() : desc);
                            String qtip = NodeStructureUtils.getLayoutQtip(r);
                            respObj.put("qtip", qtip);
                            listOfMobileLayoutPath.add(path);
                            respObj.put("value", path);
                            response.put(respObj);
                        }
                    }
                }
            }
            return response;
        } catch (JSONException e) {
            logger.error(e.getMessage(), e);
            throw new GuideException(e);
        }
    }


     public JSONArray queryChartReducer(String[] searchPaths) {
        JSONArray response = new JSONArray();
        // this provides list of all the chart reducer functions
        try {
            JSONObject firstObj = new JSONObject();
            firstObj.put("text", "None");
            firstObj.put("value", "none");
            firstObj.put("qtip", "None");
            response.put(firstObj);

            ResourceResolver resolver = resourceResolverHelper.getResourceResolver();

            for (int i = 0; i < searchPaths.length; i++) {
                Iterator reducerFunctions = resolver.findResources("SELECT *  FROM nt:base  WHERE " +
                        GuideConstants.FD_GUIDE_COMPONENT_TYPE + " = 'fd/af/reducer' AND jcr:path LIKE '" + searchPaths[i] + "%'", "sql");
                if (reducerFunctions != null) {
                    while (reducerFunctions.hasNext()) {
                        Resource r = reducerFunctions.next();
                        ValueMap properties = r.getValueMap();
                        String val = (String) properties.get("value");
                        if (!StringUtils.isEmpty(val)) {
                            String text = NodeStructureUtils.getLayoutDescription(r);
                            if (StringUtils.isEmpty(text)) {
                                text = val;
                            }
                            String qtip = NodeStructureUtils.getLayoutQtip(r);
                            if (StringUtils.isEmpty(qtip)) {
                                qtip = text;
                            }
                            JSONObject respObj = new JSONObject();
                            respObj.put("text", text);
                            respObj.put("value", val);
                            respObj.put("qtip", qtip);
                            response.put(respObj);
                        }
                    }
                }
            }
            return response;
        } catch (JSONException e) {
            logger.error(e.getMessage(), e);
            throw new GuideException(e);
        }
    }

    /**
     * Fetches all the recaptcha cloud service configuration applicable for captcha resource
     * @param captchaResource captcha resource
     */
    public JSONArray fetchRecaptchaCloudServiceConfiguration(Resource captchaResource) {
        JSONArray response = new JSONArray();

        try {
            Collection cloudConfigs = reCaptchaConfigurationService.getConfigurationCollection(captchaResource);
            Iterator iterator = cloudConfigs.iterator();

            //Putting an empty option in the dropdown option so that author choose the configuration for the first time.
            JSONObject emptyObject = new JSONObject();
            emptyObject.put("text", "");
            emptyObject.put("value", "");
            response.put(emptyObject);

            while (iterator.hasNext()) {
                ReCaptchaConfiguration reCaptchaConfiguration = iterator.next();
                JSONObject responseObject = new JSONObject();
                responseObject.put("text", StringUtils.isNotEmpty(reCaptchaConfiguration.title()) ?
                        reCaptchaConfiguration.title() : reCaptchaConfiguration.name());
                responseObject.put("value", reCaptchaConfiguration.name());
                responseObject.put("icon", reCaptchaConfiguration.thumbnailPath());
                responseObject.put("description", reCaptchaConfiguration.description());
                JSONObject graniteDataJson = new JSONObject();
                graniteDataJson.put("site.key", reCaptchaConfiguration.siteKey());
                responseObject.put("granite:data", graniteDataJson);
                response.put(responseObject);
            }
        } catch (Exception e) {
            logger.error("Unexpected error while listing cloud service configurations from /conf", e);
        }

        return response;
    }

    /**
     * Fetches all the recaptcha cloud service configuration from /etc/cloudservices/recaptcha and
     * those that are applicable for captcha resource from /conf
     * @param rootPath path pointing to root directory for captcha providers in /etc e.g. /etc/cloudservices/recaptcha
     * @param captchaResource captcha resource on the form
     */
    public JSONArray fetchRecaptchaCloudServiceConfiguration(String rootPath, Resource captchaResource) {
        JSONArray configurations = new JSONArray();

        JSONArray responseFromConf = fetchRecaptchaCloudServiceConfiguration(captchaResource);
        JSONArray responseFromEtc = fetchRecaptchaCloudServiceConfiguration(rootPath);

        try {
            configurations = GuideUtils.getMergedJSONArray(responseFromConf, responseFromEtc);
        } catch (JSONException e) {
            logger.error("Error while retrieving recaptcha cloud configurations", e);
        }

        return configurations;
    }

    /**
     * Fetches all the recaptcha cloud service configuration from the system
     * @param rootPath
     */
    @Deprecated
    public JSONArray fetchRecaptchaCloudServiceConfiguration(String rootPath) {
            JSONArray response = new JSONArray();
            ResourceResolver serviceResourceResolver = null;
            try {
                serviceResourceResolver = GuideUtils.getServiceResourceResolver(resourceResolverFactory);
                ConfigurationManager configManager = serviceResourceResolver.adaptTo(ConfigurationManager.class);
                if (rootPath != null) {
                    com.day.cq.wcm.webservicesupport.Service service = configManager.getService(configManager.getServiceName(rootPath));
                    Iterator iter = configManager.getConfigurations(rootPath);
                    JSONObject graniteDataJson = null;
                    Map graniteMap = null;
                    while (iter.hasNext()) {
                        Configuration cfg = iter.next();
                        graniteMap = new HashMap();
                        //Reading site key from RecaptchaService for the given configPath
                        graniteMap.put("site.key", reCaptchaConfigService.getSiteKey(cfg.getPath()));
                        graniteDataJson = new JSONObject(graniteMap);
                        Template template = cfg.getTemplate();
                        if (template != null && service != null && service.isSelectableChild(template.getPath())) {
                            String description = (cfg.getDescription() != null) ? cfg.getDescription() : template.getDescription();

                            JSONObject respObj = new JSONObject();
                            respObj.put("text", cfg.getTitle());
                            respObj.put("value", cfg.getPath());
                            respObj.put("icon", cfg.getIconPath());
                            respObj.put("description", description);
                            respObj.put("granite:data", graniteDataJson);
                            response.put(respObj);
                        }
                    }
                } else {
                    logger.warn("Missing parameter for 'rootPath'");
                }
            } catch (Exception e) {
                logger.error("Unexpected error while listing cloudservice configs: ", e);
            }
            return response;
    }
    /**
     * Fetches all adobe sign cloud service configuration from /conf, /libs, /apps, /etc without a resource
     * Used by workflow esigns step
     */

    public JSONArray fetchEsignCloudServiceConfigurations() {
        JSONArray response = new JSONArray();
        ResourceResolver serviceResourceResolver = null;
        try {
            String [] echoSignGroup = {"echosign"};
            serviceResourceResolver = GuideUtils.getCloudServiceUserResourceResolver(resourceResolverFactory);
            Iterator iterator = configurationResourceProvider.getAllCloudConfigs(serviceResourceResolver, echoSignGroup);
            while (iterator.hasNext()) {
                Resource configuration = iterator.next().getChild(JcrConstants.JCR_CONTENT);
                ValueMap properties = configuration.getValueMap();
                JSONObject responseObject = new JSONObject();
                responseObject.put("text", properties.get(JcrConstants.JCR_TITLE, properties.get("name", "")));
                responseObject.put("value", configuration.getParent().getPath());
                responseObject.put("icon", properties.get("thumbnailPath", ""));
                response.put(responseObject);
            }
        } catch (Exception e) {
            logger.error("Unexpected error while listing echosogn cloud service configurations", e);
        } finally {
            serviceResourceResolver.close();
        }
        return response;

    }

    /**
     * Fetches all adobe sign cloud service configuration from /conf applicable for passed resource
     * @param guideContainer resource
     */
    public JSONArray fetchEsignCloudServiceConfiguration(Resource guideContainer) {
        JSONArray response = new JSONArray();

        try {
            List cloudConfigs = adobeSignConfigurationService.getConfigurationCollection(guideContainer);
            Iterator iterator = cloudConfigs.iterator();
            while (iterator.hasNext()) {
                Resource configuration = iterator.next().getChild(JcrConstants.JCR_CONTENT);
                ValueMap properties = configuration.getValueMap();
                JSONObject responseObject = new JSONObject();
                responseObject.put("text", properties.get(JcrConstants.JCR_TITLE, properties.get("name", "")));
                responseObject.put("value", configuration.getParent().getPath());
                responseObject.put("icon", properties.get("thumbnailPath", ""));
                response.put(responseObject);
            }
        } catch (Exception e) {
            logger.error("Unexpected error while listing cloud service configurations from /conf", e);
        }

        return response;
    }

    /**
     * Fetches all the adobe sign cloud service configuration from /conf and /etc/cloudservices/echosign
     * @param rootPath /etc/cloudservices/echosign
     */
    public JSONArray fetchEsignCloudServiceConfiguration(String rootPath, Resource guideContainer) {
        JSONArray configurations = new JSONArray();

        JSONArray responseFromConf = fetchEsignCloudServiceConfiguration(guideContainer);
        JSONArray responseFromEtc = fetchEsignCloudServiceConfiguration(rootPath);

        try {
            configurations = GuideUtils.getMergedJSONArray(responseFromConf, responseFromEtc);
        } catch (JSONException e) {
            logger.error("Error while retrieving Adobe Sign cloud configurations", e);
        }

        return configurations;
    }

    /**
     * Fetches all the esign cloud service configuration from the system
     * @param rootPath
     */
    @Deprecated
    public JSONArray fetchEsignCloudServiceConfiguration(String rootPath) {
        JSONArray response = new JSONArray();
        ResourceResolver serviceResourceResolver = null;
        try {
            serviceResourceResolver = GuideUtils.getServiceResourceResolver(resourceResolverFactory);
            ConfigurationManager configManager = serviceResourceResolver.adaptTo(ConfigurationManager.class);

            if (rootPath != null) {
                com.day.cq.wcm.webservicesupport.Service service = configManager.getService(configManager.getServiceName(rootPath));
                Iterator iter = configManager.getConfigurations(rootPath);
                while (iter.hasNext()) {
                    Configuration cfg = iter.next();
                    Template template = cfg.getTemplate();
                    if(template != null && service != null && service.isSelectableChild(template.getPath())) {
                        String description = (cfg.getDescription() != null) ? cfg.getDescription() : template.getDescription();

                        JSONObject respObj = new JSONObject();
                        respObj.put("text", cfg.getTitle());
                        respObj.put("value", cfg.getPath());
                        respObj.put("icon", cfg.getIconPath());
                        respObj.put("description", description);
                        response.put(respObj);
                    }
                }
            } else {
                logger.warn("Missing parameter for 'rootPath'");
            }
        } catch (Exception e) {
            logger.error("Unexpected error while listing cloudservice configs: ", e);
        } finally {
            if (serviceResourceResolver != null) {
                serviceResourceResolver.close();
            }
        }
        return response;
    }

    private String getComponentTemplatePath(Resource componentResource) {
        try {
            Node componentNode = componentResource.adaptTo(Node.class);
            ValueMap componentProperties = componentResource.adaptTo(ValueMap.class);
            // set template path
            // 1. explicit property has precedence
            String templatePath = (String) componentProperties.get(NameConstants.PN_TEMPLATE_PATH);
            // 2. template subnode for convenience
            if (templatePath == null && componentNode.hasNode(NameConstants.NN_TEMPLATE)) {
                templatePath = componentNode.getNode(NameConstants.NN_TEMPLATE).getPath();
            }
            return templatePath;
        } catch (Exception e) {
            throw new GuideException(e);
        }
    }

    public Iterator getJSONIterator(JSONArray arr){
        return new JSONArrayIterator(arr);
    }

    /**
     *
     * @param object
     */
    public Map getMapFromJSONObject(JSONObject object){
        Map map = new HashMap();
        Object jsonObject = null;
        try {
            Iterator keys = object.keys();
            while (keys.hasNext()) {
                String key = keys.next();
                Object value = object.get(key);

                if (value instanceof JSONObject) {
                    map.put(key, getMapFromJSONObject((JSONObject) value));
                    continue;
                }
                // If value is in the form of array
                if (value instanceof JSONArray) {
                    JSONArray array = ((JSONArray) value);
                    ArrayList list = new ArrayList();
                    for (int i = 0 ; i < array.length() ; i++) {
                        jsonObject = array.get(i);
                        if (jsonObject instanceof JSONObject) {
                            list.add(getMapFromJSONObject((JSONObject) jsonObject));
                        } else {
                            list.add(jsonObject);
                        }
                    }
                    map.put(key, list);
                    continue;
                }
                map.put(key, value);
            }
            return map;
        } catch(JSONException e){
            throw new IllegalStateException(e);
        }
    }

    /**
     * Queries for document fragment layouts present in the system
     * @param searchPaths
     */
    public JSONArray queryDocumentFragmentLayout(String[] searchPaths) {
        // this provides list of all the document fragment layouts
        try {
            JSONArray response = new JSONArray();
            ResourceResolver resolver = resourceResolverHelper.getResourceResolver();

            JSONObject firstObj = new JSONObject();
            firstObj.put("text", "");
            firstObj.put("value", "");
            response.put(firstObj);

            for (int i = 0; i < searchPaths.length; i++) {
                String query = "SELECT *  FROM nt:base  WHERE " + GuideConstants.FD_GUIDE_COMPONENT_TYPE +
                        " = 'fd/adaddon/documentFragmentLayout' AND jcr:path LIKE '" + searchPaths[i] + "%'";
                Iterator layouts = resolver.findResources(query, "sql");
                if (layouts != null) {
                    while (layouts.hasNext()) {
                        Resource r = layouts.next();
                        ValueMap properties = r.getValueMap();
                        String desc = NodeStructureUtils.getLayoutDescription(r);

                        JSONObject respObj = new JSONObject();
                        respObj.put("text", desc == null ? r.getPath() : desc);
                        respObj.put("value", properties.get("layout", ""));
                        response.put(respObj);
                    }
                }
            }
            return response;
        } catch (JSONException e) {
            logger.error(e.getMessage(), e);
            throw new GuideException(e);
        }
    }

    /**
     * Fetches the list of Typekit cloud service configurations for theme configuration page.
     *
     * @param slingRequest slingrequest object to get the locale
     * @return The array of configurations with "text" and "value" keys set to title and path of configuration respectively.
     * @pad.exclude Exclude from list of published API
     */
    public JSONArray fetchTypekitCloudServiceConfiguration(SlingHttpServletRequest slingRequest) {

        JSONArray response = new JSONArray();
        I18n i18n = new I18n(slingRequest.getResourceBundle(slingRequest.getLocale()));
        ResourceResolver resourceResolver = resourceResolverHelper.getResourceResolver();
        if (resourceResolver == null) {
            logger.error("Null value returned by resourcResolverHelper.getResourceResolver. See Documentation " +
                    "of ResourceResolverHelper for debugging.");
            return response;
        }
        Resource theme = resourceResolver.getResource(slingRequest.getParameter("item"));
        try {
            response.put(new JSONObject().put("text", i18n.get("Select")).put("value", ""));
            GuideThemeUtils.addTypekitConfigurations(response, theme, resourceResolver, typekitConfigurationService);
        } catch (JSONException e) {
            logger.error("JsonException while creating JSON from list of Adobe Fonts configurations.", e);
            return response;
        }
        return response;
    }
}