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

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

/*************************************************************************
 *
 * ADOBE CONFIDENTIAL
 * __________________
 *
 *  Copyright 2014 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.common.GuideContainer;
import com.adobe.aemds.guide.common.GuideNode;
import com.adobe.aemds.guide.common.GuidePanel;
import com.adobe.aemds.guide.fdinternal.utils.JsonResourceWriter;
import com.adobe.aemds.guide.model.TextVariable;
import com.adobe.aemds.guide.service.GuideException;
import com.adobe.aemds.guide.service.GuideLocalizationService;
import com.adobe.aemds.guide.service.GuideModelImporter;
import com.adobe.aemds.guide.service.GuideModelTransformer;
import com.adobe.aemds.guide.service.GuideModuleImporter;
import com.adobe.aemds.guide.service.GuideSchemaType;
import com.adobe.aemds.guide.submitutils.FileRequestParameter;
import com.adobe.aemds.guide.themes.GuideThemeConstants;
import com.adobe.forms.common.service.FileAttachmentWrapper;
import com.adobe.forms.common.service.StaleAssetIndicatorService;
import com.adobe.forms.common.submitutils.CustomParameterRequest;
import com.adobe.forms.common.submitutils.CustomResponse;
import com.adobe.forms.common.submitutils.ParameterMap;
import com.adobe.granite.resourceresolverhelper.ResourceResolverHelper;
import com.adobe.granite.ui.clientlibs.ClientLibrary;
import com.adobe.granite.ui.clientlibs.HtmlLibrary;
import com.adobe.granite.ui.clientlibs.HtmlLibraryManager;
import com.adobe.granite.ui.clientlibs.LibraryType;
import com.adobe.granite.ui.components.ds.ValueMapResource;
import com.adobe.granite.xss.XSSAPI;
import com.day.cq.analytics.testandtarget.util.Constants;
import com.day.cq.commons.inherit.HierarchyNodeInheritanceValueMap;
import com.day.cq.commons.inherit.InheritanceValueMap;
import com.day.cq.commons.jcr.JcrConstants;
import com.day.cq.i18n.I18n;
import com.day.cq.wcm.api.Page;
import com.day.cq.wcm.api.PageManager;
import com.day.cq.wcm.api.Template;
import com.day.cq.wcm.api.TemplatedResource;
import com.day.cq.wcm.api.WCMMode;
import com.day.cq.wcm.api.components.Component;
import com.day.cq.wcm.api.components.ComponentManager;
import com.day.cq.wcm.api.components.EditConfig;
import com.day.cq.wcm.api.components.EditContext;
import com.day.cq.wcm.api.components.Toolbar;
import com.day.cq.wcm.commons.WCMUtils;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.StrLookup;
import org.apache.commons.lang3.text.StrSubstitutor;
import org.apache.http.HttpHeaders;
import org.apache.jackrabbit.api.security.user.Group;
import org.apache.jackrabbit.api.security.user.User;
import org.apache.jackrabbit.api.security.user.UserManager;
import org.apache.jackrabbit.vault.util.Text;
import org.apache.sling.api.SlingException;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.request.RequestParameter;
import org.apache.sling.api.request.RequestParameterMap;
import org.apache.sling.api.request.RequestPathInfo;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.ModifiableValueMap;
import org.apache.sling.api.resource.NonExistingResource;
import org.apache.sling.api.resource.PersistenceException;
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.ResourceUtil;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.api.scripting.SlingBindings;
import org.apache.sling.api.wrappers.SlingHttpServletRequestWrapper;
import org.apache.sling.api.wrappers.ValueMapDecorator;
import org.apache.sling.commons.json.JSONArray;
import org.apache.sling.commons.json.JSONException;
import org.apache.sling.commons.json.JSONObject;
import org.apache.sling.i18n.ResourceBundleProvider;
import org.apache.sling.servlets.post.Modification;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.script.SimpleBindings;
import javax.servlet.RequestDispatcher;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.net.URLDecoder;
import java.security.Principal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Queue;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class GuideUtils {
    private static Logger logger = LoggerFactory.getLogger(GuideUtils.class);
    private static final String MSG_NO_REPEATABLE_ITEM = "Could not compute repeatable item for chart.";
    private final static Map nodeClassToResourceTypeMap = new HashMap<>();
    static {
        nodeClassToResourceTypeMap.put(GuideConstants.ROOTPANEL_NODECLASS, GuideConstants.RT_ROOT_PANEL);
        nodeClassToResourceTypeMap.put(GuideConstants.GUIDECONTAINER_NODECLASS, GuideConstants.RT_GUIDECONTAINER);
        nodeClassToResourceTypeMap.put(GuideConstants.GUIDE_PANEL, GuideConstants.RT_PANEL);
        nodeClassToResourceTypeMap.put(GuideConstants.GUIDE_FIELD_BUTTON, GuideConstants.RT_GUIDEBUTTON);
        nodeClassToResourceTypeMap.put(GuideConstants.GUIDE_FIELD_CHART, GuideConstants.RT_GUIDECHART);
        nodeClassToResourceTypeMap.put(GuideConstants.GUIDE_FIELD_CHECKBOX, GuideConstants.RT_GUIDECHECKBOX);
        nodeClassToResourceTypeMap.put(GuideConstants.GUIDE_FIELD_DATEPICKER, GuideConstants.RT_GUIDEDATEPICKER);
        nodeClassToResourceTypeMap.put(GuideConstants.GUIDE_FIELD_DROPDOWNLIST, GuideConstants.RT_GUIDEDROPDOWNLIST);
        nodeClassToResourceTypeMap.put(GuideConstants.GUIDE_FIELD_EMAIL, GuideConstants.RT_GUIDEEMAIL);
        nodeClassToResourceTypeMap.put(GuideConstants.GUIDE_FIELD_FILEUPLOAD, GuideConstants.RT_GUIDEFILEUPLOAD);
        nodeClassToResourceTypeMap.put(GuideConstants.GUIDE_FIELD_IMAGE, GuideConstants.RT_GUIDEDRAWIMAGE);
        nodeClassToResourceTypeMap.put(GuideConstants.GUIDE_FIELD_IMAGECHOICE, GuideConstants.RT_GUIDEIMAGECHOICE);
        nodeClassToResourceTypeMap.put(GuideConstants.GUIDE_FIELD, GuideConstants.RT_GUIDEFIELD);
        nodeClassToResourceTypeMap.put(GuideConstants.GUIDE_FIELD_NUMERICBOX, GuideConstants.RT_GUIDENUMERICBOX);
        nodeClassToResourceTypeMap.put(GuideConstants.GUIDE_FIELD_PASSWORDBOX, GuideConstants.RT_GUIDEPASSWORDBOX);
        nodeClassToResourceTypeMap.put(GuideConstants.GUIDE_FIELD_RADIOBUTTON, GuideConstants.RT_GUIDERADIOBUTTON);
        nodeClassToResourceTypeMap.put(GuideConstants.GUIDE_FIELD_SCRIBBLE, GuideConstants.RT_GUIDESCRIBBLE);
        nodeClassToResourceTypeMap.put(GuideConstants.GUIDE_FIELD_SWITCH, GuideConstants.RT_GUIDESWITCH);
        nodeClassToResourceTypeMap.put(GuideConstants.GUIDE_FIELD_TEXTBOX, GuideConstants.RT_GUIDETEXTBOX);
        nodeClassToResourceTypeMap.put(GuideConstants.GUIDE_FIELD_TELEPHONE, GuideConstants.RT_GUIDETELEPHONE);
        nodeClassToResourceTypeMap.put(GuideConstants.GUIDE_FIELD_TERMSANDCONDITIONS, GuideConstants.RT_GUIDETERMSANDCONDITIONS);
        nodeClassToResourceTypeMap.put(GuideConstants.GUIDE_FIELD_TEXTDRAW, GuideConstants.RT_GUIDEDRAWTEXT);
        nodeClassToResourceTypeMap.put(GuideConstants.GUIDE_TABLE, GuideConstants.RT_TABLE);
        nodeClassToResourceTypeMap.put(GuideConstants.GUIDE_TABLE_ROW, GuideConstants.RT_TABLE_ROW);
    }

    /**
     * Generic utility to get adobe sign configuration path.
     *
     * @param guideContainerResource - resource representing guide container of the form
     * @return String - adobe sign configured path if present
     * @pad.exclude Exclude from Published API.
     */
    public static String getSignConfigPath(Resource guideContainerResource) {
        if (guideContainerResource == null) {
            return null;
        }

        String signConfigPath = null;
        ValueMap guideContainerProperties = guideContainerResource.getValueMap();
        String useSignedPdf = guideContainerProperties.get("_useSignedPdf", "");
        if (StringUtils.equals("true", useSignedPdf)) {
            Resource signerInfo = guideContainerResource.getChild("signerInfo");
            ValueMap signerInfoProperties = signerInfo.getValueMap();
            signConfigPath = signerInfoProperties.get("signConfigPath", "");
        } else {
            // check if legacy adobe sign is being used
            String query = "/jcr:root" + guideContainerResource.getPath() + "//element(*, nt:unstructured)[@guideNodeClass = 'esign']";
            Iterator iterator = guideContainerResource.getResourceResolver().findResources(query, "xpath");
            while (iterator.hasNext()) {
                Resource esignResource = iterator.next();
                ValueMap esignProperties = esignResource.getValueMap();
                signConfigPath = esignProperties.get("cq:cloudserviceconfigs", "");
                break;
            }
        }

        return signConfigPath;
    }

    /**
     * Generic utility to parse adobe sign fields from HTML
     *
     * @param htmlVal - html string having adobe sign text tags
     * @return JSONArray - array of names of adobe sign fields
     * @pad.exclude Exclude from Published API.
     */
    public static JSONArray getAdobeSignFields(String htmlVal) {
        JSONArray adobeSignFieldsArray = new JSONArray();
        HashSet hs = new HashSet();
        Pattern pattern = Pattern.compile("\\{\\{[*]?([^:]*)_es_:");
        Matcher matcher = pattern.matcher(htmlVal);
        while (matcher.find()) {
            String fieldName = matcher.group(1);
            if (hs.add(fieldName)) {
                adobeSignFieldsArray.put(fieldName);
            }
        }
        return adobeSignFieldsArray;
    }

    /**
     * Generic utility to get Text variable from text resource
     *
     * @param resource - Text resource
     * @return List - list of document fragment variables
     * @pad.exclude Exclude from Published API.
     */
    public static List getTextVariables(Resource resource) {
        List textVariableList = new ArrayList();
        Resource itemsResource = resource.getChild(GuideConstants.ITEMS_NODENAME);
        if (itemsResource != null) {
            Iterable variableIterator = itemsResource.getChildren();
            for (Resource variable : variableIterator) {
                TextVariable textVariable = variable.adaptTo(TextVariable.class);
                textVariableList.add(textVariable);
            }
        }
        return textVariableList;
    }

    /**
     * Generic utility to parse adobe sign date fields from HTML and convert date fields default value in its date format.
     *
     * @param htmlVal - stringified html having adobe sign text tags
     * @return String html, with correct default values for date
     * @pad.exclude Exclude from Published API.
     */
    public static String formatDateDefaultValues(String htmlVal) {
        String result = htmlVal;
        SimpleDateFormat inFormat = new SimpleDateFormat("yyyy-mm-dd");
        Pattern pattern = Pattern.compile("\\{\\{[*]?[^:]*_es_:[^:]*:isdate(\\(format=[\\\"\\']?([^\\\"\\'\\)]*)[\\\"\\']?\\))?(:default\\([\\\"\\']?([^\\\"\\'\\)]*)[\\\"\\']?\\))?[^\\}]*\\}\\}");
        Matcher matcher = pattern.matcher(htmlVal);
        while (matcher.find()) {
            String dateTextTag = matcher.group(0);
            String dateFormat = matcher.group(2);
            String defaultDateStr = matcher.group(4);
            if (StringUtils.isNotEmpty(defaultDateStr)) {
                if (StringUtils.isEmpty(dateFormat)) {
                    dateFormat = "mm/dd/yy";
                }
                try {
                    Date defaultDate = inFormat.parse(defaultDateStr);
                    SimpleDateFormat outFormat = new SimpleDateFormat(dateFormat);
                    String formattedDefaultDateStr = outFormat.format(defaultDate);
                    String correctedDateTextTag = dateTextTag.replace(defaultDateStr, formattedDefaultDateStr);
                    result = result.replace(dateTextTag, correctedDateTextTag);
                } catch (ParseException e) {
                    // invalid date, do nothing
                }
            }
        }
        return result;
    }

    /**
     * Utility to update signer information in Static text/ guide textdraw HTML, while creating guideJson
     *
     * @pad.exclude Exclude from Published API.
     */
    public static String updateSigner(String name, int signerIndex, String htmlVal) {
        Pattern pattern = Pattern.compile("(\\{\\{[*]?" + name + "_es_:[*]?signer(\\d+)[:}])");
        Matcher matcher = pattern.matcher(htmlVal);
        while (matcher.find()) {
            String fieldString = matcher.group(1);
            String fieldSignerIndex = matcher.group(2);
            String newFieldString = fieldString.replace("signer" + fieldSignerIndex, "signer" + signerIndex);
            htmlVal = htmlVal.replace(fieldString, newFieldString);
        }
        return htmlVal;
    }

    /**
     * Generic utility to get service resource resolver
     *
     * @pad.exclude Exclude from Published API.
     */
    public static ResourceResolver getServiceResourceResolver(ResourceResolverFactory resourceResolverFactory) {
        try {
            return resourceResolverFactory.getServiceResourceResolver(null);
        } catch (Exception e) {
            logger.error("Cannot provide  serviceResourceResolver", e);
        }
        return null;
    }

    public static ResourceResolver getCloudServiceUserResourceResolver(ResourceResolverFactory resourceResolverFactory) {
        ResourceResolver resourceResolver = null;
        if (resourceResolverFactory != null) {
            try {
                Map authenticationInfo = new HashMap();
                authenticationInfo.put(ResourceResolverFactory.SUBSERVICE, GuideConstants.CLOUD_SERVICE_USER);
                resourceResolver = resourceResolverFactory.getServiceResourceResolver(authenticationInfo);
            } catch (LoginException e) {
                logger.error("Cannot provide service resource resolver", e);
            }
        }
        return resourceResolver;
    }

    /**
     * Generic utility to convert string into Map
     *
     * @pad.exclude Exclude from Published API.
     */
    public static Map convertStringToMap(String params) {
        Map paramMap = new HashMap();
        if (params != null) {
            String[] strategyParams = params.split(",");
            for (String param : strategyParams) {
                String[] keyValue = param.split("=");
                if (keyValue.length == 2) {
                    paramMap.put(keyValue[0], keyValue[1]);
                } else {
                    // invalid case, adding both param then
                    paramMap.put(param, param);
                }
            }
        }
        return paramMap;
    }

    /**
     * @pad.exclude Exclude from Published API.
     */
    public static boolean hasNestablePanelLayout(GuideNode parentNode, GuideNode node) {
        try {

            if (parentNode == null || !GuideUtils.isLayoutablePanel(parentNode)) {
                return false;
            } else if (node == null || !GuideUtils.isLayoutablePanel(node)) {
                return false;
            } else {
                String parentNodeLayout = parentNode.getLayoutPath();
                if (parentNodeLayout != null && (parentNodeLayout.startsWith("/libs/") || parentNodeLayout.startsWith("/apps/"))) {
                    parentNodeLayout = parentNodeLayout.substring(6);
                }
                String nodeLayout = node.getLayoutPath();
                if (nodeLayout != null && (nodeLayout.startsWith("/libs/") || nodeLayout.startsWith("/apps/"))) {
                    nodeLayout = nodeLayout.substring(6);
                }
                return StringUtils.equals(parentNodeLayout, nodeLayout);
            }
        } catch (Exception e) {
            throw new GuideException(e);
        }
    }

    /**
     * This is a public api used to extract the guideContainer asset from the content.
     * Returns inline guide container if there is no guideRef property present in the guideContainer
     * else returns the guideContainer present inline
     * @param request   sling http servlet request
     * @param resource  sling resource
     * @return path to the form container
     */
    public static String getGuideContainerPath(SlingHttpServletRequest request, Resource resource) {
        String guideContainerPath = null;
        //For authoring if guideContainerNodeName is passed as a parameter it gets highest priority,
        //otherwise check if nodeName available  in ThreadLocal, else keep the default container name
        String guideContainerNodeName = GuideConstants.GUIDECONTAINER_NODENAME;
        String containerParamValue = request.getParameter(GuideConstants.GUIDE_CONTAINER_NODE_PARAMETER);
        if (containerParamValue != null) {
            guideContainerNodeName = containerParamValue;
        } else if (GuideContainerThreadLocal.getGuideContainerName() != null) {
            guideContainerNodeName = GuideContainerThreadLocal.getGuideContainerName();
        } else {
            logger.debug("Guide container name not found in ThreadLocal.");
        }
        try {
            ResourceResolver resolver = request.getResourceResolver();
            Resource currentResource = resource;
            // Search the guide Container by walking up the hierarchy
            while (currentResource != null && !currentResource.getResourceType().equals(GuideConstants.RT_PAGE)) {
                String normalizedNodeType = GuideUtils.getNormalizedNodeType(currentResource.getResourceType(),
                        currentResource.getResourceSuperType());
                if (GuideConstants.CONTAINER_RESOURCES.contains(normalizedNodeType)) {
                    guideContainerPath = currentResource.getPath();
                    break;
                }
                currentResource = currentResource.getParent();
            }
            if (guideContainerPath == null) {
                if (currentResource != null && currentResource.getResourceType().equals(GuideConstants.RT_PAGE)) {
                    guideContainerPath = resource.getPath() + "/" + guideContainerNodeName;
                }
            }
            Resource guideContainerResource = request.getResourceResolver().getResource(guideContainerPath);
            // If this API is used externally, then guideContainerResource would be null
            // As of today, this API is used to check if the request was initiated by a form
            if (guideContainerResource == null) {
                guideContainerPath = null;
            } else {
                final ValueMap properties = ResourceUtil.getValueMap(guideContainerResource);
                String guideRef = properties.get("guideRef", null);
                if (guideRef != null && guideRef.length() != 0) {
                    // before setting the path, check if the path is correct
                    String path = GuideUtils.guideRefToGuidePath(guideRef);
                    Resource guideContainer = request.getResourceResolver().getResource(path);
                    if (guideContainer != null) {
                        guideContainerPath = path;
                    } else {
                        logger.error("No guide found in guide reference present in guide container");
                    }
                }
            }
        } catch (Exception e) {
            throw new GuideException("No guide Container found", e);
        }
        return guideContainerPath;
    }


    /**
     * Get the JSON representation of a Resource
     *
     * @param resource Resource to turn into JSON
     * @return JSON representation of the resource
     * @pad.exclude Exclude from published API
     */
    public static JSONObject resourceToJSON(final Resource resource) throws Exception {
        /* Node properties to exclude from the JSON object. */
        final Set propertiesToIgnore = new HashSet() {{
            add("jcr:created");
            add("jcr:createdBy");
            add("jcr:versionHistory");
            add("jcr:predecessors");
            add("jcr:baseVersion");
            add("jcr:uuid");
            add("jcr:data");
            add("jcr:lastModified");
            add("cq:lastModified");
            add("cq:lastRolledout");
            add("cq:lastRolledoutBy");
            add("cq:lastModifiedBy");
            add("jcr:lastModifiedBy");
        }};

        final StringWriter stringWriter = new StringWriter();
        final JsonResourceWriter jsonWriter = new JsonResourceWriter(propertiesToIgnore);

        JSONObject jsonObject = null;

        try {
            /* Get JSON with no limit to recursion depth. */
            jsonWriter.dump(resource, stringWriter, -1);
            jsonObject = new JSONObject(stringWriter.toString());
        } catch (JSONException e) {
            throw new Exception("Could not create JSON from resource at path " + resource.getPath(), e);
        }

        return jsonObject;
    }

    /**
     * Gets the template JSON of the field from resource type.
     *
     * @param resourceType
     * @param resolver
     * @return
     * @throws Exception
     * @pad.exclude exclude from published api
     */
    public static JSONObject getTemplateJsonForField(String resourceType, ResourceResolver resolver) throws Exception {
        JSONObject result = null;
        if (resolver != null && StringUtils.isNotBlank(resourceType)) {
            ComponentManager componentManager = resolver.adaptTo(ComponentManager.class);
            // get the resource at the given type
            Component component = componentManager.getComponent(resourceType);
            // walk through the super component hierarchy
            while (component != null) {
                // get the template json of the resource
                Resource templateNode = resolver.getResource(component.getTemplatePath());
                if (templateNode != null) {
                    // get the json of the template node
                    result = GuideUtils.resourceToJSON(templateNode);
                    break;
                }
                component = component.getSuperComponent();
            }
        }
        return result;
    }

    /**
     * Adds the template json of the field from resource type.
     *
     * @param currentJsonObject
     * @param resourceType
     * @param resolver
     * @param guideNodeClass
     * @return
     * @throws Exception
     * @pad.exclude exclude from published api
     */
    public static JSONObject addTemplateJsonToField(JSONObject currentJsonObject, String resourceType, ResourceResolver resolver, String guideNodeClass) throws Exception{
        if(StringUtils.isBlank(resourceType) && StringUtils.isNotBlank(guideNodeClass)) {
            resourceType = nodeClassToResourceTypeMap.get(guideNodeClass);
            currentJsonObject.put(JcrResourceConstants.SLING_RESOURCE_TYPE_PROPERTY, resourceType);
        }
        JSONObject templateObj = getTemplateJsonForField(resourceType, resolver);
        if(templateObj != null){
            //Add the template object
            Iterator jsonKeys = templateObj.keys();
            while(jsonKeys.hasNext()){
                String jsonKey = jsonKeys.next();
                //Template shouldn't override json.
                if(!currentJsonObject.has(jsonKey)) {
                    currentJsonObject.put(jsonKey, templateObj.get(jsonKey));
                }
            }
        }
        return currentJsonObject;
    }

    /**
     *
     * @param request
     * @param guideContainerPath
     * @return
     * @pad.exclude Exclude from published API
     */
    public static List getFileAttachmentList(SlingHttpServletRequest request, String guideContainerPath) {
        ResourceResolver resourceResolver = request.getResourceResolver();
        String rootPanelPath = guideContainerPath + "/" + NodeStructureUtils.ROOTPANEL_NODENAME + "/" + GuideConstants.ITEMS_NODENAME;
        Resource guideRootPanel = resourceResolver.getResource(rootPanelPath);
        ArrayList result = new ArrayList();
        GuideUtils.walkThroughContent(result, guideRootPanel, GuideConstants.RT_GUIDEFILEUPLOAD);

        return result;
    }

    /**
     *
     * @param contextPropertyName
     * @param request
     * @return
     * @pad.exclude Exclude from published API
     */
    public static Object consumeContextProperty(String contextPropertyName, SlingHttpServletRequest request) {
        Object contextProp = request.getAttribute(contextPropertyName);
        request.removeAttribute(contextPropertyName);
        return contextProp;
    }

    /**
     * This API is for checking whether the text box field allows filling rich text or not
     *
     * @param jsonObject
     * @return boolean value indicating whether the field allow rich text or not
     * @pad.exclude Exclude from Published API.
     */
    public static boolean isRichTextField(JSONObject jsonObject) throws JSONException {
        if (jsonObject.has(GuideConstants.RICHTEXT_ALLOWED)) {
            return jsonObject.getBoolean(GuideConstants.RICHTEXT_ALLOWED);
        }
        return false;
    }


    public static boolean setToolbarLabel(String name, String title, EditContext editContext, SlingHttpServletRequest request) {
        if (editContext != null) {
            EditConfig editConfig = editContext.getEditConfig();
            if (editConfig != null) {
                Toolbar tb = editConfig.getToolbar();

                if (tb != null) {
                    if (name != null) {
                        tb.add(0, new Toolbar.Label(title + name));
                    }
                    tb.add(1, new Toolbar.Separator());
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Encodes the given string as HTML content
     * @param str       string to encode as HTML content
     * @param xssapi    {@link XSSAPI} instance
     * @return  encoded HTML content
     */
    public static String encodeForHtml(String str, XSSAPI xssapi) {
        String result = xssapi.encodeForHTML(str);
        return result == null ? "" : result;
    }

    /**
     * Encodes the given string as HTML attribute
     * @param str       string to encode as HTML attribute
     * @param xssapi    {@link XSSAPI} instance
     * @return  encoded HTML attribute
     */
    public static String encodeForHtmlAttr(String str, XSSAPI xssapi) {
        String result = xssapi.encodeForHTMLAttr(str);
        return result == null ? "" : result;
    }

    /**
     * Encodes the given string as java script content
     * @param str       string to encode as java script content
     * @param xssapi    {@link XSSAPI} instance
     * @return  encoded JS string
     */
    public static String encodeForJSString(String str, XSSAPI xssapi) {
        String result = xssapi.encodeForJSString(str);
        return result == null ? "" : result;
    }

    public static String filterHtml(String str, XSSAPI xssapi) {
        String result = xssapi.filterHTML(str);
        return result == null ? "" : result;
    }

    public static String getDefaultLocale(Resource resource) {
        String defaultLocale = GuideConstants.DEFAULT_FALLBACK_LOCALE;
        try {
            Node containerNode = resource.adaptTo(Node.class);
            if (containerNode != null) {
                Node parentNode = containerNode.getParent();
                if (parentNode != null) {
                    if (parentNode.hasProperty(JcrConstants.JCR_LANGUAGE)) {
                        defaultLocale = parentNode.getProperty(JcrConstants.JCR_LANGUAGE).getString();
                    }
                }
            }
        } catch (Exception e) {
            logger.error("Unable to access Page Locale", e);
        }

        return defaultLocale;
    }

    public static String getThemeClientLibName(Resource resource) {
        try {
            Node containerNode = resource.adaptTo(Node.class);
            if (containerNode != null && containerNode.hasProperty(GuideConstants.THEME_CLIENTLIB)) {
                String theme = containerNode.getProperty(GuideConstants.THEME_CLIENTLIB).getString();
                if (StringUtils.isNotBlank(theme)) {
                    Resource themeResource = resource.getResourceResolver().getResource(theme + "/jcr:content/metadata");
                    if (themeResource != null) {
                        Node themeNode = themeResource.adaptTo(Node.class);
                        if (themeNode != null && themeNode.hasProperty("clientlibCategory")) {
                            return themeNode.getProperty("clientlibCategory").getString();
                        }
                    } else {
                        logger.warn("Unable to retrieve the theme resource: " + theme);
                    }
                }
            }
        } catch (RepositoryException e) {
            logger.error("Unable to get Client lib name: " + e.getMessage(), e);
        }
        return "";
    }

    /**
     * @param request
     * @param guideContainerResource
     * @return the first locale in the accept Lang header, which has a correspoiding guide-i18n clientlib
     */
    public static String getLocale(SlingHttpServletRequest request, Resource guideContainerResource) {
        try {

            String defaultLocale = getDefaultLocale(guideContainerResource);
            String localeCode = null,
                    clientLibPrefix = "guides.I18N";
            Locale locale = null;
            //  If fate yours ever
            //  Encounters the spell scribed below
            // Thou need to turn your wand to ElGuideUtils.getI18n
            // To take equal effect
            // As the ingredients used, charm it casts are same
            SlingBindings bindings = (SlingBindings) request.getAttribute(SlingBindings.class.getName());
            HtmlLibraryManager clientlibManager = bindings.getSling().getService(HtmlLibraryManager.class);
            boolean clientLibForDefaultLocaleExists = clientlibManager.getLibraries(new String[]{clientLibPrefix + "." + defaultLocale}, null, true, false).toArray().length > 0;
            String acceptLang = request.getParameter(GuideConstants.AF_LANGUAGE_PARAMETER);
            if (acceptLang == null || acceptLang.trim().isEmpty()) {
                acceptLang = request.getHeader(HttpHeaders.ACCEPT_LANGUAGE);
            }
            if (acceptLang == null || acceptLang.trim().isEmpty()) {
                acceptLang = defaultLocale;
            }
            String[] locales = StringUtils.split(acceptLang, ",");
            for (int i = 0; i < locales.length; i++) {

                //strip the priority, if present
                localeCode = StringUtils.substringBefore(locales[i], ";");

                // locales have format language-country and since we do not store the - in the clientlib name,
                // remove that
                String[] splitLocale = StringUtils.split(localeCode, "-");
                localeCode = splitLocale[0].toLowerCase();
                if (splitLocale.length > 1) {
                    localeCode += splitLocale[1].toUpperCase();  // if locale has country include that
                }

                String[] categories = new String[]{clientLibPrefix + "." + localeCode};
                Collection libraries = clientlibManager.getLibraries(categories, null, true, false);
                boolean clientLibForLocaleExists = libraries.toArray().length > 0;
                if (clientLibForLocaleExists) {
                    return localeCode;
                } else if (splitLocale.length > 1) {
                    // Fall back
                    // if the localeCOUNTRY  called lib is not present
                    // try with locale only
                    categories = new String[]{clientLibPrefix + "." + splitLocale[0].toLowerCase()};
                    libraries = clientlibManager.getLibraries(categories, null, true, false);
                    clientLibForLocaleExists = libraries.toArray().length > 0;
                    if (clientLibForLocaleExists) {
                        return splitLocale[0].toLowerCase();
                    }
                }
            }
            if (clientLibForDefaultLocaleExists)
                return defaultLocale;
            return GuideConstants.DEFAULT_FALLBACK_LOCALE;
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new GuideException(e);
        }
    }

    /**
     * @param request
     * @param guideFieldResource
     * @return the i18n object for the guide level dictionary
     */
    public static I18n getI18n(SlingHttpServletRequest request, Resource guideFieldResource) {
        I18n i18n = null;
        ResourceResolver resourceResolver = request.getResourceResolver();
        try {
            String acceptLang = GuideUtils.getAcceptLang(request);
            // If given resource is inside a template, then create the i18n object from dictionary corresponding
            // to the form in which that template is used.
            if (isEditableTemplateResource(guideFieldResource)) {
                String formAssetPath = resourceResolver.resolve(request,request.getRequestURI()).getPath();         // TODO: Use some other method to get the guideContainer referring this template resource.
                formAssetPath = formAssetPath.substring(request.getContextPath().length());                         // Using hackish method, by resolving the requestURI to resource path and manually
                String formPath = formAssetPath.replace(GuideConstants.FM_DAM_ROOT, GuideConstants.FM_AF_ROOT);     // handling the context path. Some use cases might break.
                guideFieldResource = resourceResolver.getResource(formPath);
            }
            String containerPath = getGuideContainerPath(request, guideFieldResource);
            // if containerPath is null, traverse from cq:page node to search for container path
            if (containerPath == null || containerPath.isEmpty()) {
                containerPath = GuideUtils.getGuideContainerPathFromResource(request, guideFieldResource);
            }
            String baseName = "";
            if (StringUtils.isNotBlank(containerPath)) {
                baseName = containerPath + "/assets/dictionary";
                Locale locale = new Locale(acceptLang);
                i18n = new I18n(request.getResourceBundle(baseName, locale));
            } else if (request != null) {
                i18n = new I18n(request.getResourceBundle(request.getLocale()));
            } else {
                i18n = new I18n(request.getResourceBundle(null));
            }
        } catch (Exception e) {
            // Do nothing
            logger.error("There was certainly some problem in geti18n api", e);
            // Not wanting to throw any exceptions here so just logging it
        }
        return i18n;
    }

    /**
     * Checks if given resource lies within any editable template or not
     * This API is using the logic of TemplateUtils.isAuthoredTemplate() in CQ/wcm
     * to check for editable template node.
     * @param resource
     * @return
     * @pad.exclude
     */
    public static Boolean isEditableTemplateResource (Resource resource) {
        Boolean result = false;
        Resource pageResource = getPageResource(resource);
        if (pageResource != null ) {
            Resource pageParent = pageResource.getParent();
            // If the page's parent is a cq:Template node and it has all the 3 nodes (structure, policies and initial), then it is an editable template.
            if (GuideConstants.CQ_TEMPLATE.equals(pageParent.getResourceType())) {
                result = pageParent.getChild(GuideConstants.TEMPLATE_INITIAL_NODE) != null &&
                         pageParent.getChild(GuideConstants.TEMPLATE_POLICIES_NODE) != null &&
                         pageParent.getChild(GuideConstants.TEMPLATE_STRUCTURE_NODE) != null;
            }
        }
        return result;
    }

    /**
     *  Returns the cq:Page resource by walking up in the resource hierarchy
     *  @pad.exclude Exclude from Published API.
     */
    public static Resource getPageResource (Resource resource) {
        while(resource != null && !GuideConstants.RT_PAGE.equals(resource.getResourceType())) {
            resource = resource.getParent();
        }
        return resource;
    }


    /**
     * @param resourceBundleProvider
     * @param containerResource
     * @param locale
     * @return the i18n object for the guide level dictionary
     */
    public static I18n getI18n(ResourceBundleProvider resourceBundleProvider, Resource containerResource, Locale locale) {
        if (resourceBundleProvider != null && containerResource != null && locale != null) {
            return new I18n(resourceBundleProvider.getResourceBundle(containerResource.getPath(), locale));
        }
        return null;
    }

    /**
     * Gives the mime type of data which as per the data model
     * configured with the form
     *
     * @param formResource resource of adaptive form container
     * @return string representing the mime type
     */
    public static String getDataMimeType(Resource formResource) {
        String mimeType = GuideConstants.CONTENT_TYPE_APPLICATION_XML;
        if (formResource != null) {
            GuideContainer guideContainerBean = GuideContainer.from(formResource);
            GuideSchemaType schemaType = guideContainerBean.getSchema();
            if (schemaType != null) {
                // For formdatamodel and jsonschema, the prefillDataType is json but for other form models, it is xml.
                if (GuideSchemaType.JSON.equals(schemaType) || GuideSchemaType.FDM.equals(schemaType)) {
                    mimeType = GuideConstants.CONTENT_TYPE_APPLICATION_JSON;
                }
            }
        }
        return mimeType;
    }

    /**
     * Gives the schema type of data which is configured with the form
     *
     * @param formContainerJson json of adaptive form container
     * @return String representing schemaType of form it could have any of the following value
     * - none, formtemplates, xmlschema, jsonschema, formdatamodel
     * @deprecated Use {@link com.adobe.aemds.guide.common.GuideContainer#getSchemaType()} instead
     */
    public static String getSchemaType(JSONObject formContainerJson) {
        String type = GuideSchemaType.BASIC.getValue();
        String schemaType = formContainerJson.optString(GuideConstants.SCHEMA_TYPE, "");
        if (StringUtils.isNotEmpty(schemaType) && GuideSchemaType.isGuideSchemaType(schemaType)) {
            type = schemaType;
        } else if (StringUtils.isNotEmpty(formContainerJson.optString(GuideConstants.XSD_REF))) {
            type = GuideSchemaType.XSD.getValue();
        } else if (StringUtils.isNotEmpty(formContainerJson.optString(GuideConstants.XDP_REF))) {
            type = GuideSchemaType.XDP.getValue();
        }
        return type;
    }

    /**
     * Gives the afDataSom for an adaptive form field
     *
     * @param schemaType of adaptive form
     * @param name of adaptive form field
     * @param bindRef of adaptive form field
     * @return afDataSom of adaptive form field
     */
    public static String getAfDataSom(String schemaType, String name, String bindRef) {
        String identifier = name;
        String fieldType = GuideConstants.BASIC;
        // set type if the associated field has bindref, else type will be none (unbound field)
        if (StringUtils.isNotEmpty(bindRef)) {
            fieldType = schemaType;
            identifier = bindRef;
        }
        return fieldType + ":" + identifier;
    }

    /**
     * Gives JSONObject of Adaptive Form field/panel corresponding to given SOM Expression
     *
     * @param formContainerJson adaptive form container JSON
     * @param somExpression som expression of adaptive form panel/field
     * @return JSONObject of Adaptive Form field/panel corresponding to given SOM Expression
     */
    public static JSONObject somToAdapativeFormItem(JSONObject formContainerJson, String somExpression) {
        JSONObject afItemJson = null;
        if (StringUtils.isEmpty(somExpression) || formContainerJson == null) {
            return afItemJson;
        }
        if ("guide[0].guide1[0]".equals(somExpression)) {
            return  formContainerJson;
        }
        try {
            JSONObject rootPanelJSon = formContainerJson.getJSONObject(GuideConstants.ROOTPANEL_NODENAME);
            if ("guide[0].guide1[0].guideRootPanel[0]".equals(somExpression)) {
                return  rootPanelJSon;
            }
            String afItemSom = somExpression.replaceFirst("guide\\[0\\]\\.guide1\\[0\\]\\.guideRootPanel\\[0\\]\\.", "");
            afItemJson = searchAFItemForSom(rootPanelJSon, afItemSom);
        } catch (JSONException jsonException) {
            logger.error("Error creating signer info " + jsonException.getMessage(), jsonException);
        }
        return afItemJson;
    }

    /**
     * Recursively traverse json for finding the field/panel, for given SOM
     * @param afItemJson
     * @param parentSom
     * @return
     */
    private static JSONObject searchAFItemForSom(JSONObject afItemJson, String parentSom) {
        JSONObject parentJson = afItemJson;
        String afItemSom = parentSom;
        if (parentJson != null) {
            String fieldName = afItemSom.substring(0, afItemSom.indexOf("["));
            parentJson = parentJson.optJSONObject(GuideConstants.ITEMS_NODENAME);
            // if its not a panel then its a field, and field does have child, hence invalid SOM
            if (parentJson == null && StringUtils.isEmpty(fieldName)) {
                return null;
            }
            Iterator keys = parentJson.keys();
            // check with the name of all its children
            while (keys.hasNext()) {
                String key = keys.next();
                JSONObject childJson = parentJson.optJSONObject(key);
                if (childJson != null && fieldName.equals(childJson.optString(GuideConstants.NAME))) {
                    parentJson = childJson;
                    break;
                }
            }
            int nextIndex = afItemSom.indexOf(".");
            // check if we fully traversed som Expression, or there is more to traverse
            if (nextIndex != -1) {
                // shorten the som for the traversed part
                afItemSom = afItemSom.substring(nextIndex + 1);
                // traverse for remainder som
                return searchAFItemForSom(parentJson, afItemSom);
            } else {
                return  parentJson;
            }
        }
        return null;
    }

    /**
     * Utility to convert a RequestParameter to String
     *
     * @param param RequestParameter
     * @pad.exclude Exclude from Published API
     */
    public static String paramToString(RequestParameter param) {
        if (param != null) {
            return param.toString();
        }
        return null;
    }

    /**
     * Utility to convert an AF(Adaptive Form) meta data path to the actual adaptive forms path
     *
     * @param guideRefPath path of the adaptive forms metadata node
     * @return the path of the adaptive form
     */
    //TODO This api assumes the guideContainer name to be guideContainer but in case of ABTesting it can be guideContainer2.
    public static String guideRefToGuidePath(String guideRefPath) {
        // @todo: have to introduce notion of channels here in future
        if (StringUtils.startsWith(guideRefPath, GuideConstants.FM_DAM_ROOT)) {
            return GuideConstants.FM_AF_ROOT + StringUtils.substringAfter(guideRefPath, GuideConstants.FM_DAM_ROOT) + GuideConstants.FM_AF_GUIDECONTAINER;
        } else {
            return guideRefPath;
        }
    }

    /**
     * Utility to convert an MC DOCUMENT meta data path to the actual mc document path
     *
     * @param guideRefPath path of the mc document metadata node
     * @return the path of the mc document path
     */
    public static String guideRefToDocPath(String guideRefPath) {
        if (StringUtils.startsWith(guideRefPath, GuideConstants.FM_DAM_ROOT)) {
            return GuideUtils.convertADAssetPathToWebChannelPagePath(guideRefPath) + GuideConstants.FM_AF_GUIDECONTAINER;
        } else {
            return guideRefPath;
        }
    }

    /**
     * This API is for internal purpose.
     * This will manipulate the bindRef of the element if there is a bindRef prefix present. This means that the binded node is a fragment in adaptive forms
     * so we will need to prefix the schema hierarchy of XFA/XSD
     * if the bindRef passed was xfa[0].form[0].form1[0].Subform1[0].Subform2[0].n[0].NumericField1[0]
     * and  fragment Root is n[0]
     * then the new bindref may be like prefix + Fragment root + rest of bindRef after fragment root
     * xfa[0].form[0].form1[0].AnotherSubFormWhereFragReferenced[0].n[0].NumericField1[0]
     *
     * @param bindRefPrefixForFragment this the prefix after which the fragment root would be appended
     * @param bindRef                  Reference to the
     * @param fragmentRoot             Root of fragment
     * @return the manipulated bindref incorporating the fragment related changes
     * @pad.exclude Exclude from Published API.
     */
    public static String manipulateBindRefForFragments(String bindRefPrefixForFragment, String bindRef, String fragmentRoot) {
        String manipulatedBindRef = bindRef;
        // Case when the its a xfa binding
        if (StringUtils.isNotBlank(bindRefPrefixForFragment) && StringUtils.isNotBlank(fragmentRoot)) {
            // This is the case when someone drops fragmentModelRoot in adaptive forms fragment
            // and in adaptive forms authoring we know that the panel having fragment binding
            // would also lead to same bindRef being created
            // e.g if fragment subform  is xfa[0].form[0].form1[0].n[0]
            // and we map a xfa[0].form[0].form1[0].Subform[0].n[0] fragment to adaptive forms fragment
            // then the child of this adaptive forms fragment panel would be
            // have xfa[0].form[0].form1[0].n[0]  (if it were fragment root) and resolveNode with relative
            // som as "" would break the code
            if (fragmentRoot.equals(bindRef)) {
                return null;
            }
            // this check is added to handle panels that are converted to fragments and have the fragment model root set to default value of
            // "/" for xsd based and "xfa[0].form[0]" for xdp based. This will ensure that the bindref for the child elements are not manipulated.
            if (fragmentRoot.equals("/") || fragmentRoot.equals(GuideConstants.XFA_BINDREF_PREFIX)) {
                return bindRef;
            } else if (StringUtils.startsWith(bindRef, GuideConstants.XFA_BINDREF_PREFIX) && !StringUtils.startsWith(bindRef, fragmentRoot + '.')) {
                // this check is to support cross fragment bindRef manipulation for xfa based form.
                // if bindRef doesn't start with fragmentRoot then bindRef is not manipulated.
                // Example bindRef was xfa[0].form[0].shiporder[0].#subform[0].shipto[0].address[0]
                // fragmentRoot was xfa[0].form[0].shiporder[0].#subform[0].item[0]
                // as bindRef doesn't start with fragmentRoot, the bindRef is not manipulated.
                return bindRef;
            } else if (StringUtils.startsWith(bindRef, "/") && !StringUtils.startsWith(bindRef, fragmentRoot + '/')) {
                // this check is to support cross fragment bindRef manipulation for xsd based form.
                return bindRef;
            } else if (StringUtils.startsWith(bindRef, GuideConstants.XFA_BINDREF_PREFIX)) {
                // TODO to find out where the actual fragment is located in   the XFA
                // Example fragment subforms was xfa[0].form[0].form1[0].n[0]  with child NumericField1[0]
                // Now to use it in the hierarchy xfa[0].form[0].form1[0].Subform1[0].Subform2[0].n[0]
                //  I would need to replace xfa[0].form[0].form1[0].n[0]  with xfa[0].form[0].form1[0].Subform1[0].Subform2[0].n[0]


                manipulatedBindRef = bindRefPrefixForFragment + StringUtils.substringAfter(bindRef, fragmentRoot);

            } else if (StringUtils.startsWith(bindRef, "/")) {
                // its XML Schema binding
                manipulatedBindRef = bindRefPrefixForFragment + StringUtils.substringAfter(bindRef, fragmentRoot);

            }
        }
        return manipulatedBindRef;
    }

    /**
     * @param key
     * @pad.exclude Exclude from Published API.
     */
    public static String getNamespacedKeys(String key) {
        return GuideConstants.GUIDE_PREFIX_TO_KEYS + key;
    }

    /**
     * @param original
     * @param i18n
     * @pad.exclude Exclude from Published API.
     */
    public static String translateOrReturnOriginal(String original, I18n i18n) {
        String localizedkey, nameSpacedkey;
        if (i18n != null) {
            nameSpacedkey = getNamespacedKeys(original);
            localizedkey = i18n.getVar(nameSpacedkey);
            if (!nameSpacedkey.equals(localizedkey)) {
                return localizedkey;
            }
        }
        return original;
    }

    /**
     * @param unescaped
     * @pad.exclude Exclude from Published API.
     */
    public static String escapeXml(String unescaped) {
        return StringEscapeUtils.escapeXml(unescaped);
    }

    /**
     * @param guideNodeClass
     * @pad.exclude Exclude from Published API.
     */
    public static boolean isGuideFileUploadModel(String guideNodeClass) {
        return GuideConstants.GUIDE_FIELD_FILEUPLOAD.equals(guideNodeClass);
    }

    /**
     * @param guideNodeClass
     * @pad.exclude Exclude from Published API.
     */
    public static boolean isGuideButtonModel(String guideNodeClass) {
        return GuideConstants.GUIDE_FIELD_BUTTON.equals(guideNodeClass);
    }

    /**
     * Checks if given node class belongs to guide field
     *
     * @param guideNodeClass
     * @pad.exclude Exclude from Published API.
     */
    public static boolean isGuideFieldModel(String guideNodeClass) {
        return GuideConstants.GUIDE_FIELDS_CLASS_NAMES.indexOf(guideNodeClass) >= 0;
    }


    /**
     * Checks if given node class belongs to guide panel
     *
     * @param guideNodeClass
     * @pad.exclude Exclude from Published API.
     */
    public static boolean isGuidePanelModel(String guideNodeClass) {
        return GuideConstants.GUIDE_PANELS_CLASS_NAMES.indexOf(guideNodeClass) >= 0;
    }

    /**
     * @param guideNodeClass
     * @return
     * @pad.exclude Exclude from Published API.
     */
    public static boolean isGuideCompositeField(String guideNodeClass) {
        return GuideConstants.GUIDE_COMPOSITE_FIELD_CLASS_NAMES.indexOf(guideNodeClass) >= 0;
    }

    /**
     * @param guideNodeClass
     * @return
     * @pad.exclude Exclude from Published API.
     */
    public static boolean isGuideTnCModel(String guideNodeClass) {
        return GuideConstants.GUIDE_FIELD_TERMSANDCONDITIONS.equals(guideNodeClass);
    }

    /**
     * This API is for internal purpose only.
     * Returns guideContainer node.
     * Input form node or guideContainer node.
     *
     * @param guideJson
     * @return guideContainer JSON object
     * @pad.exclude Exclude from Published API.
     */
    public static JSONObject getGuideContainer(JSONObject guideJson)
            throws GuideException {
        try {
            return guideJson.has(GuideConstants.ROOTPANEL_NODENAME) ?
                    guideJson :
                    guideJson.getJSONObject("jcr:content").getJSONObject("guideContainer");
        } catch (Exception e) {
            logger.error("Could not get guideContainer within GuideJson : " + guideJson.toString(), e);
            throw new GuideException(e);
        }
    }

    /**
     * This API is for internal purpose.
     * Resource here is an ancestor of guideContainer. It returns the value map of inline guideContainer present in the current page and not the reference guide container.
     *
     * @param request
     * @param elementResource elementResource here should be an ancestor if guideContainer, if it a descendent use the generic api getGuideContainerPath present inside GuideELUtils
     * @throws javax.jcr.RepositoryException
     * @pad.exclude Exclude from Published API.
     */
    public static ValueMap getGuideContainer(final SlingHttpServletRequest request, final Resource elementResource) {

        if (elementResource == null)
            return null;
        // search for all the nodes and get the guideContainer
        final Iterator iter = elementResource.listChildren();
        Resource guideContainer = null;

        while (iter.hasNext() && guideContainer == null) {
            final Resource current = iter.next();
            if (GuideConstants.CONTAINER_RESOURCES.contains(current.getResourceType())) {
                guideContainer = current;
            }
        }
        if (guideContainer != null)
            return ResourceUtil.getValueMap(guideContainer);
        else {
            logger.error("No guideContainer found");
            throw new GuideException("No guideContainer found");
        }
    }

    /**
     * @param request
     * @param resource
     * @return
     * @throws javax.jcr.RepositoryException
     * @pad.exclude Exclude from Published API.
     */
    public static Boolean isValidGuide(final SlingHttpServletRequest request, final Resource resource) throws RepositoryException {
        ValueMap properties = null;
        if (GuideConstants.CONTAINER_RESOURCES.contains(resource.getResourceType())) {
            properties = ResourceUtil.getValueMap(resource);
        } else {
            properties = GuideUtils.getGuideContainer(request, resource);
        }
        //get the guideRef Attribute
        String guideRef = properties.get("guideRef", null);
        if (guideRef != null && guideRef.length() != 0) {
            // before setting the path, check if the path is correct
            String guideContainerPath = guideRefToGuidePath(guideRef); //guideRef.concat(GuideConstants.GUIDECONTAINER_SUFFIX);
            Resource guideContainer = request.getResourceResolver().getResource(guideContainerPath);
            if (guideContainer == null) {
                logger.error("No guide found in guide reference present in guide container");
                return false;
            }
        }
        return true;
    }

    /**
     * @param resourceType
     * @param resourceSuperType
     * @pad.exclude Exclude from Published API.
     */
    public static String getNormalizedNodeType(String resourceType, String resourceSuperType) {
        String normalizedType = null;
        if (resourceType == null && resourceSuperType == null) {
            return null;
        }
        if (StringUtils.startsWith(resourceType, "fd/")) {
            normalizedType = resourceType;
        } else if (StringUtils.startsWith(resourceType, "/libs/fd/") || StringUtils.startsWith(resourceType, "/apps/fd/")) {
            normalizedType = resourceType.substring(6);
        } else if (StringUtils.startsWith(resourceSuperType, "fd/")) {
            normalizedType = resourceSuperType;
        } else if (StringUtils.startsWith(resourceSuperType, "/libs/fd/") || StringUtils.startsWith(resourceSuperType, "/apps/fd/")) {
            normalizedType = resourceSuperType.substring(6);
        }
        return normalizedType;
    }

    /**
     * @param list
     * @param elementResource
     * @param resourceToBeFound
     * @pad.exclude Exclude from Published API.
     */
    public static void walkThroughContent(List list, Resource elementResource, final String resourceToBeFound) {
        if (elementResource != null) {
            Iterator iter = elementResource.listChildren();
            while (iter.hasNext()) {
                final Resource current = iter.next();
                String normalizedNodeType = GuideUtils.getNormalizedNodeType(current.getResourceType(),
                        current.getResourceSuperType());
                if (resourceToBeFound.equals(normalizedNodeType)) {
                    list.add(current.getPath());
                }
                walkThroughContent(list, current, resourceToBeFound);
            }
        }
    }

    /**
     * @param node
     * @pad.exclude Exclude from Published API.
     */
    public static boolean isLayoutablePanel(GuideNode node) {
        return isPanel(node.getResource());
    }

    /**
     * This API converts DAM asset path to adaptive forms page's container path e.g. if the path was /content/dam/formsanddocument/bla/jcr:content
     * the return vaule will be /content/forms/af/bla/jcr:content/guideContainer
     *
     * @param fragRef DAM asset path of Fragment
     * @return adaptive forms container path of fragment
     * @pad.exclude Exclude from Published API.
     */
    public static String convertFMAssetPathToContainerPath(String fragRef) {
        // @todo: Have to include channel type here in future, once we support fragments in channels
        // Convert asset to adaptive forms page

        fragRef = org.apache.commons.lang.StringUtils.replace(fragRef, GuideConstants.FM_DAM_ROOT, GuideConstants.FM_AF_ROOT, 1);
        // Now add  JCR content and container path
        fragRef = fragRef + GuideConstants.JCR_CONTENT + "/" + GuideConstants.GUIDECONTAINER_NODENAME;
        return fragRef;
    }

    private static String removeChannelNameFromEndIfExist(final String pagePath){
        String retVal = pagePath;
        retVal = StringUtils.substringBefore(retVal, "/" + GuideConstants.NN_CHANNELS + "/");
        // remove jcr:content from end if exists
        retVal = StringUtils.substringBeforeLast(retVal, GuideConstants.JCR_CONTENT);
        return retVal;
    }

    /**
     * Get Form Asset path from the given form container path
     * @param guideContainerPath path to the form container
     * @return corresponding path of Form Manager Form Asset
     */
    public static String convertGuideContainerPathToFMAssetPath(String guideContainerPath) {
        String fmPath =  StringUtils.substringBefore(guideContainerPath, GuideConstants.FM_AF_GUIDECONTAINER);
        String retVal =  removeChannelNameFromEndIfExist(fmPath);
        retVal = StringUtils.replace(retVal, GuideConstants.FM_AF_ROOT, GuideConstants.FM_DAM_ROOT, 1);
        return retVal;
    }

    /**
     * Get Form Asset Meatadata path in form manager for given form container path
     * @param guideContainerPath path to the adaptive form container
     * @return corresponding path of Form Manager Form Asset Metadata node
     */
    public static String convertGuideContainerPathToFMAssetMetadataPath(String guideContainerPath) {
        String fmAssetMetadataPath = convertGuideContainerPathToFMAssetPath(guideContainerPath);
        fmAssetMetadataPath = fmAssetMetadataPath + GuideConstants.FM_DAM_METADATA;
        return fmAssetMetadataPath;
    }

    /**
     * Returns path of XDP used for DoR Template (auto generated or associated)
     * dorType = none, dorTemplateRef = 
     * dorType = generate, dorTemplateRef = /renditions/dorTemplate/runtimeLocale
     * dorType = select, dorTemplateRef
     *
     * @param guideContainerPath
     * @param runtimeLocale      locale
     * @param resourceResolver
     * @return path of XDP used for DoR Template
     */
    public static String getDoRTemplateRef(String guideContainerPath,
                                           String runtimeLocale, ResourceResolver resourceResolver) {
        // if dorType is generate then dorTemplate/ node must exist;
        // if not, then create it
        String formDAMAssetMetadataPath = GuideUtils.convertGuideContainerPathToFMAssetMetadataPath(guideContainerPath);
        Resource containerResource = resourceResolver.getResource(guideContainerPath);
        Resource formMetadataResource = resourceResolver.getResource(formDAMAssetMetadataPath);
        String dorTemplateRef = "";
        if (formMetadataResource != null) {
            ValueMap metadata = formMetadataResource.getValueMap();
            dorTemplateRef = metadata.get(GuideConstants.XDP_REF, "");

            if (StringUtils.isEmpty(dorTemplateRef)) {
                dorTemplateRef = (String) metadata.get(GuideConstants.DOR_TEMPLATE_REF);
                String dorType = metadata.get(GuideConstants.DOR_TYPE, "");
                if ("generate".equals(dorType)) {
                    dorTemplateRef = GuideUtils.convertGuideContainerPathToFMAssetPath(guideContainerPath) + "/jcr:content/renditions/dorTemplate/" + runtimeLocale;
                } else if ("none".equals(dorType)) {
                    dorTemplateRef = "";
                }
            }
        } else {
            // only if this is not an editable template resource, log an error
            // since editable template guide container resource, don't have dam asset meta data node
            if (containerResource != null && !isEditableTemplateResource(containerResource)) {
                logger.error("DAM asset metadata node not available for guide container path: " + guideContainerPath);
            }
        }
        return dorTemplateRef;
    }

    /**
     * @param resourceResolverHelper
     * @pad.exclude Exclude from Published API.
     */
    public static ResourceResolver getResolverFromResourceResolverHelper(ResourceResolverHelper resourceResolverHelper) {
        if (resourceResolverHelper != null) {
            return resourceResolverHelper.getResourceResolver();
        }
        return null;
    }

    /**
     * Returns resource resolver from GuideContainer
     *
     * @param guideContainer
     * @pad.exclude Exclude from Published API.
     */
    public static ResourceResolver getResolverFromResource(GuideContainer guideContainer) {
        Resource resource = null;
        if (guideContainer != null && (resource = guideContainer.getResource()) != null) {
            return resource.getResourceResolver();
        }
        return null;
    }

    /**
     * @param valueMap
     * @pad.exclude Exclude from Published API.
     */
    public static StrSubstitutor getStringSubstitutor(JSONObject valueMap) {
        return new StrSubstitutor(new DataLookUp(valueMap));
    }

    /**
     * Returns wether given resource is of type panel or not.
     * @param resource
     * @return Boolean true if given resource is of type panel
     * @pad.exclude Exclude from Published API.
     */
    public static Boolean isPanel(Resource resource) {
        Boolean isPanel = Boolean.FALSE;
        if(resource != null){
            isPanel = resource.isResourceType(GuideConstants.RT_PANEL) ||
                    resource.isResourceType(GuideConstants.RT_ROOT_PANEL);
        }
        return isPanel;
    }

    /**
     * Checks if session has property modification permission on resource.
     * @param resource
     * @return
     * @pad.exclude Exclude from Published API.
     */
    public static Boolean hasModifyPermission(Resource resource) {
        Session currentSession = resource.getResourceResolver().adaptTo(Session.class);
        try {
            if (currentSession.hasPermission(resource.getPath(), "set_property")) {
                return Boolean.TRUE;
            }
        } catch (RepositoryException e) {
            logger.error("unable to check the permissions for setting property on " + resource.getPath(), e);
        }
        return Boolean.FALSE;
    }

    /* * Converts Form Fragment having old responsive layout to new responsive layout.
     * a) Delete previous /items if present.
     * b) Copy fragment resource to current panel.
     * c) delete the fragRef flag from current panel and save changes.
     * @param fragmentPanelResource
     * @param fragmentPath
     * @param processConversion
     * @return
     */
    public static Boolean embedFragmentResources(Resource fragmentPanelResource, String fragmentPath, Boolean processConversion){
        ResourceResolver resourceResolver = fragmentPanelResource.getResourceResolver();
        try {
            Resource prevItemsResources = resourceResolver.getResource(fragmentPanelResource.getPath()+"/items");
            if(prevItemsResources!=null){
                resourceResolver.delete(prevItemsResources);
            }
            resourceResolver.copy(fragmentPath, fragmentPanelResource.getPath());
            if(processConversion){
                convertPanelToNewResponsiveLayout(fragmentPanelResource);
            }
            ModifiableValueMap modifiableMap = fragmentPanelResource.adaptTo(ModifiableValueMap.class);
            modifiableMap.remove(GuideConstants.FRAG_REF);
            fragmentPanelResource.getResourceResolver().commit();
        } catch (Exception e) {
            logger.error("error in embedding adaptive form fragment",e);
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }
    /**
     * Converts Form / IC web channel / Fragment having old responsive layout to new responsive layout.
     * @param guideContainerResource
     * @return Boolean.TRUE if conversion succeeds otherwise returns Boolean.FALSE
     * @pad.exclude Exclude from Published API.
     */
    public static Boolean convertToNewResponsiveLayout(Resource guideContainerResource) {
        Resource rootPanelResource = guideContainerResource.getChild(GuideConstants.ROOTPANEL_NODENAME);
        // check for permission
        if (!hasModifyPermission(rootPanelResource)) {
            logger.error("Has insufficient permissions to convert to new responsive layout " + rootPanelResource.getPath());
            return Boolean.FALSE;
        }
        convertPanelToNewResponsiveLayout(rootPanelResource);
        // save changes in the end
        try {
            rootPanelResource.getResourceResolver().commit();
        } catch (PersistenceException e) {
            logger.error("Unable to save changes of converted layout " + rootPanelResource.getPath(), e);
        }
        return Boolean.TRUE;
    }

    /**
     * Converts Panel and its decedents having old responsive layout to new responsive layout.
     * @param panelResource
     * @pad.exclude Exclude from Published API.
     */
    public static void convertPanelToNewResponsiveLayout(Resource panelResource) {
        Resource layoutResource = panelResource.getChild(GuideConstants.LAYOUT_NODENAME);
        String layoutResourceType = layoutResource.getResourceType();
        //if of type old responsive layout
        if (GuideConstants.LAYOUT_GRIDFLUIDLAYOUT.equals(layoutResourceType)) {
            // modify & remove old responsive layout related properties from layout node
            ModifiableValueMap modifiableMap = layoutResource.adaptTo(ModifiableValueMap.class);
            modifiableMap.put(JcrResourceConstants.SLING_RESOURCE_TYPE_PROPERTY, GuideConstants.LAYOUT_GRIDFLUIDLAYOUT2);
            modifiableMap.remove("columns");
        }

        Resource itemsResource = panelResource.getChild(GuideConstants.ITEMS_NODENAME);
        String itemsResourceType = itemsResource.getResourceType();
        if (GuideConstants.LAYOUT_GRIDFLUIDLAYOUT.equals(itemsResourceType)) {
            // remove layout related properties from items node
            ModifiableValueMap modifiableMap = itemsResource.adaptTo(ModifiableValueMap.class);
            modifiableMap.remove(JcrResourceConstants.SLING_RESOURCE_TYPE_PROPERTY);
        }

        for (Resource item : itemsResource.getChildren()) {
            ModifiableValueMap modifiableMap = item.adaptTo(ModifiableValueMap.class);
            modifiableMap.remove(GuideConstants.LAYOUT_COLSPAN);
            if (item.isResourceType(GuideConstants.RT_PANEL)) {
                convertPanelToNewResponsiveLayout(item);
            }
        }
    }

    /**
     *
     * @param panelResource
     * @return Boolean.TRUE on first occurrence of old responsive layout
     *         Boolean.FALSE on first occurrence of new responsive layout
     *         otherwise null
     * @pad.exclude Exclude from Published API.
     */
    public static Boolean isOldResponsiveLayout(Resource panelResource) {
        //only panel resources have layout
        if (!isPanel(panelResource)) {
            return null;
        }
        Resource layoutResource = panelResource.getChild(GuideConstants.LAYOUT_NODENAME);
        if (layoutResource == null) {
            return null;
        }
        String layoutResourceType = layoutResource.getResourceType();
        //return true if old responsive layout
        if (GuideConstants.LAYOUT_GRIDFLUIDLAYOUT.equals(layoutResourceType)) {
            return Boolean.TRUE;
        } else if (GuideConstants.LAYOUT_GRIDFLUIDLAYOUT2.equals(layoutResourceType)) {
            return Boolean.FALSE;
        }
        //we don't consider fragments used by reference here
        Resource itemsResource = panelResource.getChild(GuideConstants.ITEMS_NODENAME);
        if (itemsResource != null && itemsResource.hasChildren()) {
            for (Resource itemResource : itemsResource.getChildren()) {
                //return true on first occurrence of old responsive layout, no need to iterate full hierarchy
                // or return false on first occurrence of new responsive layout
                Boolean isOldResponsiveLayout = isOldResponsiveLayout(itemResource);
                if (isOldResponsiveLayout != null) {
                    return isOldResponsiveLayout;
                }
            }
        }
        return null;
    }

    /**
     * Returns weather the panel resource or any of its hierarchy has old responsive layout in it.
     * @param panelResource
     * @return Boolean true if panel resource or any of its hierarchy has old responsive layout in it
     * @pad.exclude Exclude from Published API.
     */
    public static Boolean hasOldResponsiveLayout(Resource panelResource) {
        Boolean isOldResponsiveLayout = isOldResponsiveLayout(panelResource);
        if (isOldResponsiveLayout != null) {
            return isOldResponsiveLayout;
        }
        return Boolean.FALSE;
    }

    /**
     * @param resource
     * @param slingRequest
     * @pad.exclude Exclude from Published API.
     */
    public static GuidePanel getRootPanel(Resource resource, SlingHttpServletRequest slingRequest) {
        Iterator iter = resource.listChildren();
        GuidePanel rootPanel = null;
        ResourceResolver resourceResolver = resource.getResourceResolver();

        while (iter.hasNext() && rootPanel == null) {
            final Resource current = iter.next();
            String name = current.getName();
            if (GuideConstants.ROOTPANEL_NODENAME.equals(name)) {
                rootPanel = new GuidePanel();
                SimpleBindings bindings = new SimpleBindings();
                bindings.put("resource", current);
                bindings.put("request", slingRequest);
                rootPanel.init(bindings);
            }
        }
        return rootPanel;
    }

    /**
     * @param slingRequest
     * @pad.exclude Exclude from Published API.
     */
    public static GuideModuleImporter getGuideModuleImporter(SlingHttpServletRequest slingRequest) {
        SlingBindings bindings = null;
        GuideModuleImporter guideModuleImporter = null;
        if (slingRequest != null) {
            bindings = (SlingBindings) slingRequest.getAttribute(SlingBindings.class.getName());
            // binding would be null, if this function is invoked from Servlet or there is no request
            if (bindings != null) {
                guideModuleImporter = bindings.getSling().getService(GuideModuleImporter.class);
            }
        }
        return guideModuleImporter;
    }

    /**
     * @param resource
     * @pad.exclude Exclude from Published API.
     */
    public static Resource getRootPanel(Resource resource) {

        try {
            if (resource != null) {
                Iterator itemKeys = resource.getChildren().iterator();
                while (itemKeys.hasNext()) {
                    Resource item = itemKeys.next();
                    ValueMap valueMap = item.adaptTo(ValueMap.class);
                    if (valueMap.get(GuideConstants.SLING_RESOURCE_TYPE) != null) {
                        String valueOfResourceType = (String) valueMap.get(GuideConstants.SLING_RESOURCE_TYPE);
                        if (GuideConstants.RT_ROOT_PANEL.equals(valueOfResourceType)) {
                            return item;
                        }
                    }
                }
            }

        } catch (Exception e) {
            logger.error("error in getting root panel via guide json", e);
        }

        return null;
    }

    /**
     * @param str
     * @param prefix
     * @pad.exclude Exclude from Published API.
     */
    public static String removePrefix(String str, String prefix) {
        if (str == null || prefix == null) {
            return str;
        }
        if (str.startsWith(prefix)) {
            return str.substring(prefix.length());
        }
        return str;
    }

    /**
     * This method calls staleAssetIndicatorService and gets LMT of the form and related assets - Used to validate Caches - both JSON & HTML
     *
     * @param guideContainer
     * @param staleAssetIndicatorService
     * @return lastmodified time from StaleAssetIndicatorService or from jcr:content (parent of guideContainer)
     * @pad.exclude Exclude from Published API.
     */
    public static Calendar getLastModifiedTimeFromStaleAssetIndicatorService(GuideContainer guideContainer, StaleAssetIndicatorService staleAssetIndicatorService) {
        Resource assetNode = null;
        Calendar lastModifiedTime = null;
        try {
            ResourceResolver guideResourceResolver = guideContainer.getResource().getResourceResolver();
            if (guideResourceResolver != null) {
                String assetPath = convertGuideContainerPathToFMAssetPath(guideContainer.getPath());
                assetNode = guideResourceResolver.getResource(assetPath);
                // FM DAM root check to help  dev test cases not hit cache miss exception
                // due to null pointer exception as FM expects this assetNode to be FM asset
                if (assetNode != null && StringUtils.startsWith(assetPath, GuideConstants.FM_DAM_ROOT)) {

                    lastModifiedTime = GuideUtils.getCurrentLMT(assetNode, staleAssetIndicatorService);
                } else {
                    // For Inline guides created using adaptive forms page template, that is, these don't have an asset associated
                    lastModifiedTime = guideContainer.getLastModifiedTime();
                }
            }

        } catch (Exception e) {
            logger.error("Error while calling getLastModifiedTimeFromStaleAssetIndicatorService", e);
        }
        return lastModifiedTime;
    }

    /**
     * Returns if it's a json based schema type.
     *
     * @param schemaType
     * @return if it's a json based schema type.
     * @pad.exclude
     */
    public static boolean isJsonBasedSchema(String schemaType) {
        return GuideSchemaType.JSON.equals(GuideSchemaType.getGuideSchemaType(schemaType)) || GuideSchemaType.FDM.equals(GuideSchemaType.getGuideSchemaType(schemaType));
    }

    /**
     * Checks if the give adaptive forms(using guidecontainer resource) has a valid XDP Reference to it
     *
     * @param guideContainer resource of the adaptive form container
     * @return false if the resource is null or xdpRef property is null/empty string,
     *         true otherwise
     */
    public static boolean isXDPValid(Resource guideContainer) {
        if (guideContainer == null) {
            return false;
        }
        GuideContainer guideContainerBean = GuideContainer.from(guideContainer);
        if(!GuideSchemaType.XDP.equals(guideContainerBean.getSchema())) {
            return false;
        }
        String xdpRef = guideContainerBean.getSchemaRef();
        if (StringUtils.isBlank(xdpRef)) {
            return false;
        }
        return guideContainerBean.getResource().getResourceResolver().getResource(xdpRef) != null;
    }

    /**
     * Checks if path pointed by metaTemplateRef is valid resource.
     *
     * @param resourceResolver ResourceResolver to resolve path
     * @return false if the resource is null or metaTemplateRef property is null/empty string,
     *         true otherwise
     */
    public static boolean isMetaTemplateValid(ResourceResolver resourceResolver, String metaTemplateRef) {
        if (resourceResolver == null) {
            return false;
        }

        if (StringUtils.isBlank(metaTemplateRef)) {
            return false;
        }

        return resourceResolver.getResource(metaTemplateRef) != null;
    }

    /**
     * Get meta-template path associated with guide container.
     *
     * @param guideContainerResource guide container resource object
     * @return returns path to meta-template if exists otherwise empty string
     */
    public static String getMetaTemplateRef(Resource guideContainerResource) {
        if (guideContainerResource != null && !isGuideContainerResource(guideContainerResource)) {
            return "";
        }

        ValueMap guideContainerProperties = guideContainerResource.getValueMap();
        String metaTemplateRef = guideContainerProperties.get(GuideConstants.META_TEMPLATE_REF, "");

        Resource print = guideContainerResource.getChild(GuideConstants.PRINT_NODE_RELATIVE_PATH);
        if (print != null) {
            ValueMap map = print.getValueMap();
            if (map.containsKey(GuideConstants.META_TEMPLATE_REF)) {
                metaTemplateRef = map.get(GuideConstants.META_TEMPLATE_REF, "");
            }
        }

        return metaTemplateRef;
    }

    /**
     * This is a public api used to identify if a resource is a Guide Container or Wrapper resource
     * Returns true if resource is a GuideContainer or Wrapper
     * else returns false
     *
     * @param resource
     * @pad.exclude Exclude from Published API.
     */
    public static boolean isGuideContainerResource(Resource resource) {
        return GuideConstants.CONTAINER_RESOURCES.contains(resource.getResourceType());
    }

    /**
     * This API checks weather the resource's HTML should ever be cached
     *
     * @param resource container resource
     * @return boolean stating if resource can be cached
     * @pad.exclude Exclude from Published API.
     */
    public static boolean isCacheableContainerResource(Resource resource) {
        return GuideConstants.CACHEABLE_CONTAINER_RESOURCES.contains(resource.getResourceType());
    }

    /**
     * API to identify if targeting is enabled for the Adaptive form requested by
     * the specified guide container
     *
     * @param guideContainerResource resource to the adaptive form container
     * @return true if targeting enabled false otherwise
     */
    public static boolean isTargetEnabled(Resource guideContainerResource) {
        ValueMap props = guideContainerResource.getParent().adaptTo(ValueMap.class);
        return props.get(GuideConstants.TARGET_ENABLED, false);
    }

    /**
     * API to identify if adobe target is configured for the Adaptive form.
     *
     * @param resource resource to the adaptive form container
     * @return true if adobe target is configured otherwise false
     */
    public static boolean isAdobeTargetConfigured(Resource resource) {
        InheritanceValueMap valueMap = new HierarchyNodeInheritanceValueMap(resource);
        Object cloudserviceConfigs = valueMap.getInherited(Constants.PN_CQ_CLOUD_SERVICE_CONFIGS, String[].class);
        if(cloudserviceConfigs != null) {
            for (String cloudserviceConfig : (String [])cloudserviceConfigs) {
                if (cloudserviceConfig.startsWith("/etc/cloudservices/testandtarget")) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * The api returns the alternate (Target B scenario for ABTesting)
     * Returns path to the alternate Guide Container
     *
     * @param requestPathInfo RequestPathInfo object from the request
     * @return alternateContainerPath
     * @pad.exclude Exclude from Published API.
     */
    public static String getAlternateContainerPathFromCurrentContainer(RequestPathInfo requestPathInfo) {
        String currentContainerPath = requestPathInfo.getResourcePath();
        String extension = null;
        if (currentContainerPath == null) {
            return null;
        }
        extension = requestPathInfo.getExtension();
        if (extension == null) {
            return currentContainerPath.substring(0, currentContainerPath.lastIndexOf("/")) + "/" + GuideConstants.GUIDE_TARGET_ALTERNATE_CONTAINER_NAME;
        } else {
            return currentContainerPath.substring(0, currentContainerPath.lastIndexOf("/")) + "/" + GuideConstants.GUIDE_TARGET_ALTERNATE_CONTAINER_NAME + "." + extension;
        }
    }

    /**
     * validates Adaptive Form (having Signature Step) authored state
     *
     * @param guideContainer,
     * @return list of errors if any
     * @pad.exclude Exclude from Published API.
     */
    public static List validateFormAuthoredState(GuideContainer guideContainer, String afInitializationState, I18n localI18n) {
        List errorList = new ArrayList();

        if (!guideContainer.isSignatureConfiguredCorrectly()) {
            String errorMsg = localI18n.getVar(AuthoringErrorMessages.AUTHORING_ERROR_MAP.get(AuthoringErrorMessages.MSG_E_SIGN_0007));
            errorList.add(new AuthoringError(errorMsg, AuthoringErrorMessages.MSG_E_SIGN_0007));
        }
        String xdpRef = guideContainer.getXdpRef();
        String dorTemplateRef = guideContainer.getDorTemplateRef();

        //check for Signature Step component - "guideNodeClass\":\"esign\"
        int esignNodeClassIndex = afInitializationState.indexOf("\"guideNodeClass\\\":\\\"esign\\\"");
        if (esignNodeClassIndex > 0) {
            //Extracting json string of SignatureStep
            int beginIndex = afInitializationState.substring(0, esignNodeClassIndex).lastIndexOf("{");
            String signatureStepJsonStr = afInitializationState.substring(beginIndex, afInitializationState.indexOf("}", beginIndex) + 1);
            Boolean isEchoSignService = signatureStepJsonStr.contains("\"signingService\\\":\\\"echosign\\\"");
            //checking for target version of Signature Step
            if (signatureStepJsonStr.contains("\"fd:targetVersion\\\":\\\"1.1\\\"") && isEchoSignService) {
                //Adobe Sign should be enabled on guideContainer
                if (!guideContainer.isAdobeSignEnabled()) {
                    String errorMsg = localI18n.getVar(AuthoringErrorMessages.AUTHORING_ERROR_MAP.get(AuthoringErrorMessages.MSG_E_SIGN_0001));
                    errorList.add(new AuthoringError(errorMsg, AuthoringErrorMessages.MSG_E_SIGN_0001));
                } else if (!guideContainer.isFormFillerFirstSigner()) {
                    //Form filler should be the first signer for form having signature step
                    String errorMsg = localI18n.getVar(AuthoringErrorMessages.AUTHORING_ERROR_MAP.get(AuthoringErrorMessages.MSG_E_SIGN_0002));
                    errorList.add(new AuthoringError(errorMsg, AuthoringErrorMessages.MSG_E_SIGN_0002));
                }
            } else if (guideContainer.isAdobeSignEnabled() && isEchoSignService) {
                //For legacy component Adobe Sign should not be enabled on guideContainer
                String errorMsg = localI18n.getVar(AuthoringErrorMessages.AUTHORING_ERROR_MAP.get(AuthoringErrorMessages.MSG_E_SIGN_0003));
                errorList.add(new AuthoringError(errorMsg, AuthoringErrorMessages.MSG_E_SIGN_0003));
            }
        }

        Boolean isXDPBased = StringUtils.isNotEmpty(xdpRef);
        Boolean isDorConfigured = StringUtils.isNotEmpty(dorTemplateRef);
        Boolean canAfProduceDor = isXDPBased || isDorConfigured;
        Boolean isDorAutogenerate = "generate".equals(guideContainer.getDoRType());

        // Electronic signature to be configured, AF either has to be XDP based or should have associated DOR
        if (guideContainer.isAdobeSignEnabled() && !canAfProduceDor) {
            String errorMsg = localI18n.getVar(AuthoringErrorMessages.AUTHORING_ERROR_MAP.get(AuthoringErrorMessages.MSG_E_SIGN_0004));
            errorList.add(new AuthoringError(errorMsg, AuthoringErrorMessages.MSG_E_SIGN_0004));
        }

        // Esign component can not exist without DOR or XDP based
        if (esignNodeClassIndex > 0 && !canAfProduceDor) {
            String errorMsg = localI18n.getVar(AuthoringErrorMessages.AUTHORING_ERROR_MAP.get(AuthoringErrorMessages.MSG_E_SIGN_0005));
            errorList.add(new AuthoringError(errorMsg, AuthoringErrorMessages.MSG_E_SIGN_0005));
        }

        //check for Verify Step component - "guideNodeClass\":\"verify\"
        Boolean hasVerifyComponent = afInitializationState.contains("\"guideNodeClass\\\":\\\"verify\\\"");
        // Verify component can not exist without DOR or XDP based
        if (hasVerifyComponent && !canAfProduceDor) {
            String errorMsg = localI18n.getVar(AuthoringErrorMessages.AUTHORING_ERROR_MAP.get(AuthoringErrorMessages.MSG_VERIFY_STEP_0002));
            errorList.add(new AuthoringError(errorMsg, AuthoringErrorMessages.MSG_VERIFY_STEP_0002));
        }

        //check for adobe sign block component - "guideNodeClass\":\"guideAdobeSignBlock\"
        Boolean hasAdobeSignBlockComponent = afInitializationState.contains("\"guideNodeClass\\\":\\\"guideAdobeSignBlock\\\"");
        // adobe sign blocks don't have any affect in XDP based AF or Form template associated with DOR
        if (hasAdobeSignBlockComponent && !isDorAutogenerate) {
            String errorMsg = localI18n.getVar(AuthoringErrorMessages.AUTHORING_ERROR_MAP.get(AuthoringErrorMessages.MSG_E_SIGN_0006));
            errorList.add(new AuthoringError(errorMsg, AuthoringErrorMessages.MSG_E_SIGN_0006));
        }

        //Check for minimum count to be 0.
        Boolean isMinCountZero = afInitializationState.contains("\\\"minOccur\\\":\\\"0\\\"");
        if(isMinCountZero && hasVerifyComponent) {
            String errorMsg = localI18n.getVar(AuthoringErrorMessages.AUTHORING_ERROR_MAP.get(AuthoringErrorMessages.MSG_VERIFY_STEP_0001));
            errorList.add(new AuthoringError(errorMsg, AuthoringErrorMessages.MSG_VERIFY_STEP_0001));
        }

        Boolean hasDefaultCaptcha = afInitializationState.contains("\"captchaService\\\":\\\"afcaptcha\\\"");
        if (hasDefaultCaptcha) {
            String errorMsg = localI18n.getVar(AuthoringErrorMessages.AUTHORING_ERROR_MAP.get(AuthoringErrorMessages.MSG_DEFAULT_CAPTCHA_0001));
            errorList.add(new AuthoringError(errorMsg, AuthoringErrorMessages.MSG_DEFAULT_CAPTCHA_0001));
        }

        String aemWorkflowStr = "\"aemWorkflowId\\\":";
        int aemWorkflowIndex = afInitializationState.indexOf(aemWorkflowStr);
        if (aemWorkflowIndex > -1) {
            String errorMsg = localI18n.getVar(AuthoringErrorMessages.AUTHORING_ERROR_MAP.get(AuthoringErrorMessages.MSG_FLAMINGO_0001));
            int aemWorkflowEndIndex = afInitializationState.indexOf('}', aemWorkflowIndex) + 1;
            String workflowIdsStr = afInitializationState.substring(aemWorkflowIndex+2+aemWorkflowStr.length(), aemWorkflowEndIndex);
            try {
                Gson gson = new Gson();
                Map workflowIdMap = gson.fromJson(StringEscapeUtils.unescapeJava(workflowIdsStr).replace("\\\"", "'"), Map.class);
                Iterator> entryIterator = workflowIdMap.entrySet().iterator();
                while(entryIterator.hasNext()) {
                    Map.Entry entry = entryIterator.next();
                    String workFlowId = entry.getValue();
                    errorMsg = StringUtils.join(errorMsg, "
"+entry.getKey()+""); } } catch (Exception e) { logger.error("Error while fetching workflow id for smart forms", e); } errorList.add(new AuthoringError(errorMsg, AuthoringErrorMessages.MSG_FLAMINGO_0001, AuthoringError.ErrorType.Info)); } return errorList; } /** * returns whether the dorTemplateRef present in the resource is valid or not * todo: Have to check if the schema of XSD and dorTemplateRef match? * * @param guideContainer, * @return false if the resource is null, dorTemplateRef property is null or empty string, * true otherwise * @pad.exclude Exclude from Published API. */ public static boolean isDorTemplateRefValid(Resource guideContainer) { if (guideContainer == null) { return false; } GuideContainer guideContainerBean = GuideContainer.from(guideContainer); ValueMap guideContainerProps = guideContainerBean.getResource().adaptTo(ValueMap.class); String xsdRef = GuideSchemaType.XSD.equals(guideContainerBean.getSchema()) ? guideContainerBean.getSchemaRef() : null, dorTemplateRef = null; if (xsdRef == null || xsdRef.length() == 0) { return false; } else { // here it is expected that it is the absolute path of XDP present // todo: please check for relative path, will depend on how forms manager store XDP in guide container dialog settings dorTemplateRef = guideContainerProps.get(GuideConstants.DOR_TEMPLATE_REF, null); } if (dorTemplateRef != null) { boolean isValid = true; try { isValid = (guideContainer.getResourceResolver().getResource(dorTemplateRef) != null); } catch (SlingException ex) { isValid = false; logger.error("DOR Template present in XDP is either set to null or is relative" + ex.getMessage(), ex); } return isValid; } else { return false; } } /** * Returns the xfa compliant locale. If a XFA compliant version is not found then it returns the locale as is * Eg. For en it returns en_US. * * @param locale AEM supported locale * @return Returns the corresponding XFA compliant locale * */ public static String getXFALocale(String locale) { String xfaLocale = locale; for (int i = 0; i < GuideConstants.AEM_SUPPORTED_LOCALES.length; i++) { if (GuideConstants.AEM_SUPPORTED_LOCALES[i].equals(locale)) { xfaLocale = GuideConstants.AEM_XFA_SUPPORTED_LOCALES[i]; break; } } return xfaLocale; } /** * Returns the runtime locale in format like en-us * * @param resource resource to the adaptive form container * @param slingRequest client request object * @return Returns the runtime locale if localeLib is present otherwise falls back to * authoring locale or "en" as last resort */ public static String getGuideRuntimeLocale(SlingHttpServletRequest slingRequest, Resource resource) { int index = 0; // if no resource or guideContainer resource the "en" is fallback String[] GUIDES_SUPPORTED_CLIENTLIBS = GuideConstants.GUIDES_SUPPORTED_CLIENTLIBS; String[] supportedLocales = GuideConstants.AEM_SUPPORTED_LOCALES; //changed operator from or to and so that bindings are initialized only if slingRequest is not null. if (resource != null && slingRequest != null) { SlingBindings bindings = (SlingBindings) slingRequest.getAttribute(SlingBindings.class.getName()); if (bindings != null) { GuideLocalizationService guideLocalizationService = bindings.getSling().getService(GuideLocalizationService.class); supportedLocales = guideLocalizationService.getSupportedLocales(); GUIDES_SUPPORTED_CLIENTLIBS = GuideUtils.sanitizeLocaleList(supportedLocales); } String locale = getLocale(slingRequest, resource); index = Arrays.asList(GUIDES_SUPPORTED_CLIENTLIBS).indexOf(locale); // Try to fallback to the author locale if (index == -1) { Locale authoringLocale = slingRequest.getLocale(); if (authoringLocale != null) { index = Arrays.asList(GUIDES_SUPPORTED_CLIENTLIBS).indexOf(authoringLocale.toString()); } } // fallback to english if (index == -1) { index = 0; } } return supportedLocales[index]; } /** * @param containerResource * @param staleAssetIndicatorService * @return Calendar LMT of the child asset or LONG_MAX value if service missing * This method will call form managers' API to check that all * related child asset's TS and return eldest modified time * @pad.exclude Exclude from Published API. */ public static Calendar getCurrentLMT(Resource containerResource, StaleAssetIndicatorService staleAssetIndicatorService) { Calendar calendar = Calendar.getInstance(); if (staleAssetIndicatorService != null) { long currentLMT = staleAssetIndicatorService.getAssetTreeLMT(containerResource); calendar.setTimeInMillis(currentLMT); } else { // Cache miss logger.info("staleAssetIndicatorService not available : Guide Json cache miss"); calendar.setTimeInMillis(Long.MAX_VALUE); } return calendar; } /** * @param request * @return string consisting of appropriate locale code * This method will iterate through list of language in "accept-language" * request header and provide with the appropriate locale code * after finding the clientlibs available for localization * Guide Container Locale is the fallback. * @pad.exclude Exclude from Published API. */ public static String getAcceptLang(SlingHttpServletRequest request) { // request would be null at server side validation if (request == null) { return GuideConstants.DEFAULT_FALLBACK_LOCALE; } String language = null; SlingBindings bindings = (SlingBindings) request.getAttribute(SlingBindings.class.getName()); //check the parameter afAcceptLang for accepted language String afLang = request.getParameter(GuideConstants.AF_LANGUAGE_PARAMETER); if (afLang != null && !afLang.trim().isEmpty()) { language = getAcceptLang(afLang, bindings); } //if the accepted language is not provided, or invalid, look for the request header //accept-language if (language == null) { String acceptLang = request.getHeader(HttpHeaders.ACCEPT_LANGUAGE); if (acceptLang != null && !acceptLang.isEmpty()) { language = getAcceptLang(acceptLang, bindings); } } if (language != null) { return language; } return getDefaultLocale(request); } /** * @param request * @return string consisting of default locale * This method will extract the authoring locale from * Guide Container Path after resolving resources * "en" is the last fallback. * @pad.exclude Exclude from Published API. */ private static String getDefaultLocale(SlingHttpServletRequest request) { try { Resource resource = request.getResource(); if (resource != null) { String path = getGuideContainerPath(request, resource); if (path != null && !path.isEmpty()) { ResourceResolver guideResourceResolver = request.getResourceResolver(); if (guideResourceResolver != null) { Resource guideContainerResource = guideResourceResolver.getResource(path); if (guideContainerResource != null) { return getDefaultLocale(guideContainerResource); } } } } } catch (Exception e) { logger.error("Guide Resource not found", e); } return GuideConstants.DEFAULT_FALLBACK_LOCALE; } /** * @param locale * @param bindings * @return string consisting of appropriate locale code for a locale * @pad.exclude Exclude from Published API. */ private static String getAcceptLang(String locale, SlingBindings bindings) { String acceptLang = locale; // Q. When can sling bindings be null ? // Ans. When called from GuideInternalSubmitServlet if (bindings != null) { GuideLocalizationService guideLocalizationService = bindings.getSling().getService(GuideLocalizationService.class); String[] supportedLocales = guideLocalizationService.getSupportedLocales(); String[] supportedLocalesSanitized = GuideUtils.sanitizeLocaleList(supportedLocales); String localeCode; // If fate yours ever // Encounters the spell scribed below // Thou need to turn your wand to ElGuideUtils.getLocale // To take equal effect // As the ingredients used, charm it casts are same String[] locales = StringUtils.split(acceptLang, ","); for (int i = 0; i < locales.length; i++) { //strip the priority, if present localeCode = StringUtils.substringBefore(locales[i], ";"); // locales have format language-country and since we do not store the - in the clientlib name, // remove that String[] splitLocale = StringUtils.split(localeCode, "-"); localeCode = splitLocale[0].toLowerCase(); if (splitLocale.length > 1) { localeCode += splitLocale[1].toUpperCase(); // if locale has country include that } int index = Arrays.asList(supportedLocalesSanitized).indexOf(localeCode); if (index != -1) { acceptLang = supportedLocales[index]; return acceptLang; } else if (splitLocale.length > 1) { // Fall back // if the localeCOUNTRY called lib is not present // try with locale only index = Arrays.asList(supportedLocalesSanitized).indexOf(splitLocale[0].toLowerCase()); if (index != -1) { acceptLang = supportedLocales[index]; return acceptLang; } } } return null; } else { return locale; } } /** * @param guideTypeJsonObject * @param toCheck * @param keyToPut * @param valueToPut * @param collection * @pad.exclude Exclude from Published API. */ public static void visitToCollectProperties(JSONObject guideTypeJsonObject, String toCheck, String keyToPut, String valueToPut, JSONArray collection) { try { if (guideTypeJsonObject.has(toCheck)) { JSONObject jsonObject = new JSONObject(); jsonObject.put((String) guideTypeJsonObject.get(keyToPut), guideTypeJsonObject.get(valueToPut)); collection.put(jsonObject); } if (guideTypeJsonObject.has(GuideConstants.ITEMS_NODENAME)) { JSONObject items = (JSONObject) guideTypeJsonObject.get(GuideConstants.ITEMS_NODENAME); Iterator childKeys = items.keys(); while (childKeys.hasNext()) { visitToCollectProperties((JSONObject) items.get(childKeys.next()), toCheck, keyToPut, valueToPut, collection); } } } catch (Exception e) { logger.error("Error in visting XML Schema", e); } } /** * returns the i18 object for the desired locale * * @return returns the i18 object for the desired locale * @pad.exclude Exclude from Published API. */ public static I18n getI18nForDesiredLocale(SlingHttpServletRequest request, Resource guideContainerResource, Locale desiredLocale) { String baseName = null; if (desiredLocale == null) { desiredLocale = request.getLocale(); } if (guideContainerResource != null && request != null) { baseName = getGuideContainerPath(request, guideContainerResource) + "/assets/dictionary"; if (baseName != null) { ResourceBundle resourceBundle = request.getResourceBundle(baseName, desiredLocale); if (resourceBundle != null) { return new I18n(resourceBundle); } } } return null; } /** * returns the index of the locale from the GuideConstants.AEM_SUPPORTED_LOCALES * if locale is not suppoted then index of "en" is returned * * @param locale * @return returns the index of the locale from the GuideConstants.AEM_SUPPORTED_LOCALES * @pad.exclude Exclude from Published API. */ public static int getLocaleIndexFromLocale(String locale, String[] AEM_SUPPORTED_LOCALES) { String[] GUIDES_SUPPORTED_CLIENTLIBS = GuideUtils.sanitizeLocaleList(AEM_SUPPORTED_LOCALES); int index = Arrays.asList(GUIDES_SUPPORTED_CLIENTLIBS).indexOf(locale); if (index == -1) { index = 0; } return index; } /** * This is to sanitize the localisation service locales * They are in pt-br format, we will try to sanitize them to ptBR format * * @param locales * @return returns a new list of sanitized locales * @pad.exclude Exclude from Published API. */ public static String[] sanitizeLocaleList(String[] locales) { String localeCode; String[] sanitizedLocales = new String[locales.length]; for (int i = 0; i < locales.length; i++) { localeCode = locales[i]; String[] splitLocale = StringUtils.split(localeCode, "-"); localeCode = splitLocale[0].toLowerCase(); if (splitLocale.length > 1) { localeCode += splitLocale[1].toUpperCase(); // if locale has country include that } sanitizedLocales[i] = localeCode; } return sanitizedLocales; } /** * Returns the inherited property value of the property from the page containing the resource mentioned. * @param resource resource * @param propertyName name of the property * @return string representing the inherited property value * @pad.exclude */ public static String getInheritedProperty(Resource resource, String propertyName) { String retVal = null; if (resource != null) { final ResourceResolver resourceResolver = resource.getResourceResolver(); if (resourceResolver != null) { final PageManager pageManager = resourceResolver.adaptTo(PageManager.class); if (pageManager != null) { final Page page = pageManager.getContainingPage(resource); retVal = WCMUtils.getInheritedProperty(page, resourceResolver, propertyName); } } } return retVal; } /** * To get user session from sling request * * @pad.exclude Exclude from Published API. */ public static Session getUserSessionFromRequest(SlingHttpServletRequest slingHttpServletRequest) { return slingHttpServletRequest.getResourceResolver() .adaptTo(Session.class); } /** * Forwards the given request using HTTP Post to the given post url * * @param request sling request * @param response sling response * @param postUrl URL to forward the current request * @return sling http servlet response */ public static SlingHttpServletResponse processInternalPostOnRestEndPoint(SlingHttpServletRequest request, SlingHttpServletResponse response, String postUrl) { SlingHttpServletRequest wrappedRequest = null; SlingHttpServletResponse wrappedResponse = null; try { ParameterMap wrappedParameterMap = GuideUtils.prepareWrappedRequestWithDataXMLAndAttachments(request); wrappedRequest = new CustomParameterRequest(request, wrappedParameterMap, "POST"); wrappedResponse = new CustomResponse(response); request.getRequestDispatcher(postUrl).forward(wrappedRequest, wrappedResponse); } catch (Exception e) { logger.error("Some Problem While Posting to rest End Point", e); } return wrappedResponse; } /** * @param requestParameterMap * @param key * @param requestParameter * @deprecated * @pad.exclude Exclude from Published API. */ public static void addToRequestMap(ParameterMap requestParameterMap, String key, RequestParameter[] requestParameter) { requestParameterMap.put(key, requestParameter); } /** * @param request * @pad.exclude Exclude from Published API. */ public static ParameterMap prepareWrappedRequestWithDataXMLAndAttachments(SlingHttpServletRequest request) { ParameterMap wrappedParameterMap = new ParameterMap(); RequestParameterMap originalParams = request.getRequestParameterMap(); List fileAttachments = new ArrayList(); try { // Add file attachments for (Map.Entry param : originalParams.entrySet()) { RequestParameter[] rpm = param.getValue(); if (rpm != null && rpm.length > 0 && !rpm[0].isFormField()) { FileAttachmentWrapper fileAttachment = new FileAttachmentWrapper(rpm[0].getFileName(), rpm[0].getContentType(), rpm[0].get()); fileAttachments.add(fileAttachment); } } // copy file "attachments" if present if (fileAttachments.size() > 0) { RequestParameter[] attachments = new RequestParameter[fileAttachments.size()]; int index = 0; for (final FileAttachmentWrapper fileAttachment : fileAttachments) { attachments[index++] = new FileRequestParameter(fileAttachment.getFileName(), IOUtils.toByteArray(fileAttachment.getInputStream()), fileAttachment.getContentType()); } GuideUtils.addToRequestMap(wrappedParameterMap, GuideConstants.ATTACHMENTS, attachments); } // Put dataXml too GuideUtils.addToRequestMap(wrappedParameterMap, GuideConstants.DATA_XML, originalParams.getValues(GuideConstants.JCR_DATA)); } catch (Exception e) { logger.error("Not Able to make internal post req parameter", e); } return wrappedParameterMap; } /** * Returns the field layout related configuration of the field. * If one wishes to write own custom layout, the framework is similar to how * we write custom panel layout * * @pad.exclude Exclude from Published API. */ public static String getFieldLayout(Resource guideContainerResource, String propertyName, String defaultValue) { String fieldLayout = defaultValue; if(guideContainerResource != null) { // get the properties map for guide container resource ValueMap properties = guideContainerResource.adaptTo(ValueMap.class); // Return the field layout present fieldLayout = properties.get(propertyName, defaultValue); } return fieldLayout; } /** * Sets the embed fragment button in the edit bar for the panel based on the title, handler and index. * * @param title * @param editContext * @param request * @param fragRef * @param currentPanelPath * @param bindRef * @pad.exclude Exclude from Published API. */ public static boolean setEmbedFragButton(String title, EditContext editContext, SlingHttpServletRequest request, String fragRef, String currentPanelPath, String bindRef) { if (editContext != null) { EditConfig editConfig = editContext.getEditConfig(); XSSAPI xssapi = request.adaptTo(XSSAPI.class); if (editConfig != null) { Toolbar tb = editConfig.getToolbar(); if (tb != null) { if (title != null) { tb.add(new Toolbar.Separator()); // fragRef stored was FM asset path , converting it to AF page path fragRef = StringUtils.replace(fragRef, GuideConstants.FM_DAM_ROOT, GuideConstants.FM_AF_ROOT, 1); String handler = "function(){guidelib.author.editConfigListeners.embedFragment(\"" + fragRef + "\", \"" + currentPanelPath + "\" ,\"" + bindRef + "\");}"; //NOCHECKMARX - handler must be handled where it's used. //In this case, panel.jsp has these values from property and resource preventive Reflective XSS All Client. tb.add(new Toolbar.Button(title, handler)); } return true; } } } return false; } /** * Sets the button in the edit bar for the panel based on the title and handler. * * @param title * @param editContext * @param request * @param currentPanelPath * @param bindRef * @param handler * @return * @pad.exclude Exclude from Published API. */ public static boolean setButtonForPanel(String title, EditContext editContext, SlingHttpServletRequest request, String currentPanelPath, String bindRef, String handler) { return setButtonForPanel(title, editContext, request, currentPanelPath, bindRef, handler, null); } /** * Sets the button in the edit bar for the panel based on the title, handler and index. * * @param title * @param editContext * @param request * @param currentPanelPath * @param bindRef * @param handler * @param index * @pad.exclude Exclude from Published API. */ public static boolean setButtonForPanel(String title, EditContext editContext, SlingHttpServletRequest request, String currentPanelPath, String bindRef, String handler, Integer index) { if (editContext != null) { EditConfig editConfig = editContext.getEditConfig(); if (editConfig != null) { Toolbar tb = editConfig.getToolbar(); if (tb != null) { if (index == null) { index = tb.size(); } if (title != null) { tb.add(index, new Toolbar.Separator()); tb.add(index + 1, new Toolbar.Button(title, handler)); } return true; } } } return false; } /** * @param responseString * @param redirectParameters * @pad.exclude Exclude from Published API. */ public static void putQueryParamsToRedirectRequest(String responseString, Map redirectParameters) { try { String[] pairs = responseString.split("&"); for (int i = 0; i < pairs.length; i++) { String[] fields = pairs[i].split("="); if (fields.length == 2) { String name = URLDecoder.decode(fields[0].trim(), "UTF-8"); String value = URLDecoder.decode(fields[1].trim(), "UTF-8"); redirectParameters.put(name, value); } } } catch (Exception e) { logger.error("Error while putting params to redirect Request", e); } } /** * @param size * @pad.exclude Exclude from Published API. */ public static double parseSize(String size) { if (size.indexOf("in") >= 0 || size.indexOf("mm") >= 0 || size.indexOf("cm") >= 0 || size.indexOf("pt") >= 0 || size.indexOf("px") >= 0) { return Double.parseDouble(size.substring(0, size.length() - 2)); } return 1.00; } /** * @param guideJsonString AF Json * @pad.exclude Exclude from Published API. * This is a wrapper API - would cut down all the lazy children from the JSON */ public static JSONObject trimLazyChildren(String guideJsonString) { JSONObject trimmedGuideJson = null; try { trimmedGuideJson = new JSONObject(guideJsonString); trimJsonOfLazyInstances(trimmedGuideJson); } catch (Exception e) { logger.error("Could not trim children", e); } return trimmedGuideJson; } /** * @param jsonObject * @pad.exclude Exclude from Published API. */ private static void trimJsonOfLazyInstances(JSONObject jsonObject) { try { if (jsonObject.has(GuideConstants.OPTIMIZE_RENDER_PERFORMANCE)) { if (jsonObject.has(GuideConstants.ITEMS_NODENAME)) { jsonObject.remove(GuideConstants.ITEMS_NODENAME); } } else if (jsonObject.has(GuideConstants.ROOTPANEL_NODENAME)) { trimJsonOfLazyInstances(jsonObject.getJSONObject(GuideConstants.ROOTPANEL_NODENAME)); } else if (jsonObject.has(GuideConstants.ITEMS_NODENAME)) { JSONObject itemsNode = jsonObject.getJSONObject(GuideConstants.ITEMS_NODENAME); Iterator keys = itemsNode.keys(); while (keys.hasNext()) { String key = (String) keys.next(); JSONObject childItem = itemsNode.getJSONObject(key); trimJsonOfLazyInstances(childItem); } } } catch (Exception e) { logger.error("Some exception while removing on demand children", e); } } /** * Finds a fragment/form with the specified property and having the given path (required for getting in context(manipulated bindrefs/ HTML IDs) fragment/form ) * * @param jsonObject * @param propertyName * @param propertyValue * @param path * @param ignorePath if true then the resource should have same jcr:path path is ignored * @return string having JSON of the fragment/ form * @pad.exclude Exclude from Published API. */ public static String findJsonObjectWithProperty(JSONObject jsonObject, String propertyName, String propertyValue, String path, boolean ignorePath) { String stringifiedResult = ""; try { if (jsonObject.has(propertyName) && propertyValue.equals(jsonObject.getString(propertyName)) && (ignorePath || path.equals(jsonObject.get(JcrConstants.JCR_PATH)))) { // clean nested lazy children JSONObject itemsNode = jsonObject.getJSONObject(GuideConstants.ITEMS_NODENAME); Iterator keys = itemsNode.keys(); while (keys.hasNext()) { String key = (String) keys.next(); JSONObject childItem = itemsNode.getJSONObject(key); trimJsonOfLazyInstances(childItem); } // yay now we are good to go return jsonObject.toString(); } else if (jsonObject.has(GuideConstants.ROOTPANEL_NODENAME)) { return findJsonObjectWithProperty(jsonObject.getJSONObject(GuideConstants.ROOTPANEL_NODENAME), propertyName, propertyValue, path, ignorePath); } else if (jsonObject.has(GuideConstants.ITEMS_NODENAME)) { JSONObject itemsNode = jsonObject.getJSONObject(GuideConstants.ITEMS_NODENAME); Iterator keys = itemsNode.keys(); while (keys.hasNext()) { String key = (String) keys.next(); JSONObject childItem = itemsNode.getJSONObject(key); stringifiedResult += findJsonObjectWithProperty(childItem, propertyName, propertyValue, path, ignorePath); } } else { // its a leaf return ""; } } catch (Exception e) { logger.error("Error while fetching json for " + propertyValue, e); } return stringifiedResult; } /** * Finds a fragment/form with the specified property and having the given path (required for getting in context(manipulated bindrefs/ HTML IDs) fragment/form ) * * @param jsonObject * @param propertyName * @param propertyValue * @param path * @return string having JSON of the fragment/ form * @pad.exclude Exclude from Published API. */ public static String findJsonObjectWithProperty(JSONObject jsonObject, String propertyName, String propertyValue, String path) { return findJsonObjectWithProperty(jsonObject, propertyName, propertyValue, path, Boolean.FALSE); } /** * @param templateId * @param html * @return HTML {@link String} of the templateId provided * @pad.exclude Exclude from Published API. * Returns the HTML of lazy asset from the whole AF HTML */ public static String getAssetHTMLFromFullHTML(String templateId, String html) { return GuideHTMLParser.getAssetHTMLFromFullHTML(html, templateId); } /** * @param json * @param html * @return String form of JSON object having asset JSON & asset HTML * @pad.exclude Exclude from Published API. *

* Forms a json object of the json and html strings provided */ public static String formJsonHtmlString(String json, String html, String multipleJson) { StringWriter stringWriter = new StringWriter(); CustomJSONWriter jsonWriter = new CustomJSONWriter(stringWriter); jsonWriter.object(); jsonWriter.key("json").value(json); jsonWriter.key("html").value(html); jsonWriter.key("multipleJson").value(multipleJson); jsonWriter.endObject(); return stringWriter.toString(); } /** * @param guideContainer * @return fetchs layout script from {@link com.adobe.aemds.guide.common.GuideContainer} * @pad.exclude Exclude from Published API. *

* Fetchs layout script from {@link com.adobe.aemds.guide.common.GuideContainer} */ public static String getLayoutScriptFromContainer(GuideContainer guideContainer) { // get layout script from guideContainer // getResource will take care of /libs and /apps String script = guideContainer.getLayout(); Resource scriptResource = GuideUtils.getResolverFromResource(guideContainer).getResource(script); String scriptPath; if (scriptResource != null) { scriptPath = scriptResource.getPath(); } else { // where resolver cant resolve the resource (Test cases) //fall back to hard-coded libs scriptPath = "/libs" + "/" + script; } script = scriptPath + "/" + StringUtils.substringAfterLast(script, "/") + ".jsp"; return script; } /** * @param guideContainer * @param slingHttpServletResponse * @return HTML of Adaptive Form (stringfied) * @pad.exclude Exclude from Published API. *

* Produce HTML of the children of {@link com.adobe.aemds.guide.common.GuideContainer} */ public static String produceHTML(GuideContainer guideContainer, SlingHttpServletResponse slingHttpServletResponse) { String script = GuideUtils.getLayoutScriptFromContainer(guideContainer); SlingHttpServletRequest slingRequest = guideContainer.getSlingRequest(); try { String guideContainerPath = guideContainer.getPath(); String urlToRootPanel = guideContainerPath + "/" + GuideConstants.ROOTPANEL_NODENAME + "." + GuideConstants.HTML; RequestDispatcher dispatcher = slingRequest.getRequestDispatcher(urlToRootPanel); WCMMode.DISABLED.toRequest(slingRequest); GuideStringWriterResponse responseWrapper = new GuideStringWriterResponse(slingHttpServletResponse); dispatcher.include(new SlingHttpServletRequestWrapper(slingRequest), responseWrapper); return responseWrapper.getString(); } catch (Exception e) { logger.error("Error while executing script " + script, e); } return null; } /** * @param guideJsonObject * @param jsonArray * @pad.exclude Exclude from Published API. * Gets stringfied jsonModel object of the given templateId list */ public static String getJsonObjectWithGivenTemplateIds(JSONObject guideJsonObject, JSONArray jsonArray) { try { JSONObject jsonObject = new JSONObject(); for (int index = 0; index < jsonArray.length(); index++) { JSONObject newGuideJsonObject = new JSONObject(guideJsonObject.toString()); String templateId = jsonArray.getString(index); jsonObject.put(templateId, findJsonObjectWithProperty(newGuideJsonObject, GuideConstants.TEMPLATEID, templateId, "", Boolean.TRUE)); } return jsonObject.toString(); } catch (Exception e) { logger.error("Could not for json object with the given template ids", e); } return null; } /** * Gets the JavaScript as String from the client lib path specified. * * @param htmlLibraryManager HtmlLibraryManager service. * @param clientLibPath String specifying the path of client lib. * @return String representing the java script code of the client lib(not css). */ public static String getScriptAsStringFromClientLib(HtmlLibraryManager htmlLibraryManager, String clientLibPath, boolean minify) { StringWriter writer = new StringWriter(); InputStream inputStream = null; try { HtmlLibrary htmlLibrary = htmlLibraryManager.getLibrary(LibraryType.JS, clientLibPath); if (htmlLibrary != null) { inputStream = htmlLibrary.getInputStream(minify); IOUtils.copy(inputStream, writer); } } catch (IOException ex) { logger.error(ex.getMessage(), ex); } finally { IOUtils.closeQuietly(inputStream); } return writer.toString(); } /** * Gets the JavaScript as a string array from clientlib categories. * * @param htmlLibraryManager HtmlLibraryManager service. * @param clientLibCategories Array of all clientlib categories fow which script is required. * @return List of Strings for every clientlib specified. */ public static ArrayList getScriptFromClientLibList(HtmlLibraryManager htmlLibraryManager, String[] clientLibCategories, boolean minify) { // Construct all the client libs here // Order is important // Get all the JS Files used by Adaptive form using the HTML library manager Collection clientLibrary = htmlLibraryManager.getLibraries(clientLibCategories, null, true, false); ArrayList script = new ArrayList(); // Walk through all the client libs and put it in rhino context Iterator iter = clientLibrary.iterator(); while (iter.hasNext()) { String path = ((ClientLibrary) iter.next()).getPath(); script.add(getScriptAsStringFromClientLib(htmlLibraryManager, path, minify)); } return script; } /** * Need an object like * { * "languages":[ * {"lang":"ja","dicts":["/libs/wcm/core/i18n/ja.json","path":"/libs/wcm/core/i18n/ja2.json"]}, * {"lang":"fr","dicts":["/libs/wcm/core/i18n/fr.json","/libs/wcm/core/i18n/fr2.json"]}, * {"lang":"de","dicts":[ /libs/wcm/core/i18n/de.json","path":"/libs/wcm/core/i18n/de2.json"]} * ] * } * * @param assetList * @param locales * @pad.exclude Exclude from published API * Returns */ public static JSONObject getDictionaryInfoFromAssetPaths(JSONArray assetList, List locales) { JSONObject dictionaryInfo = new JSONObject(); JSONArray dictInfoArray = new JSONArray(); try { for (String locale : locales) { JSONObject localeObject = new JSONObject(); localeObject.put(GuideConstants.LANG, locale); JSONArray pathArray = new JSONArray(); for (int index = 0; index < assetList.length(); index++) { String path = assetList.getString(index); String convertedPath = convertContainerPathToDictionaryPath(path, locale); if (StringUtils.isNotEmpty(convertedPath)) { pathArray.put(convertedPath); } } localeObject.put(GuideConstants.DICTS, pathArray); dictInfoArray.put(localeObject); } dictionaryInfo.put(GuideConstants.LANGUAGES, dictInfoArray); } catch (Exception e) { logger.error("Could not create dictionary paths", e); } return dictionaryInfo; } private static String convertContainerPathToDictionaryPath(String containerPath, String locale) { if (StringUtils.isNotEmpty(containerPath) && StringUtils.isNotEmpty(locale)) { return containerPath + "/" + GuideConstants.ASSETS_NODE + "/" + GuideConstants.DICTIONARY_NODENAME + "/" + locale; } return null; } /** * * @param componentType * @return * @pad.exclude Exclude from Published API. */ public static String getDefaultPattern(String componentType) { if (componentType.equals(GuideConstants.GUIDE_FIELD_EMAIL)) return GuideConstants.GUIDE_FIELD_EMAIL_PATTERN; return ""; } /** * * @param jsonData * @return * @throws JSONException * @pad.exclude */ public static JSONObject getCCRData(String jsonData) throws JSONException{ if(jsonData != null && jsonData.length() > 0){ JSONObject jsonDataObj = new JSONObject(jsonData); if(jsonDataObj.has(GuideConstants.WRAPPED_SUBMIT_JSON_ROOT)){ JSONObject jsonRoot = jsonDataObj.optJSONObject(GuideConstants.WRAPPED_SUBMIT_JSON_ROOT); if(jsonRoot.has(GuideConstants.WRAPPED_SUBMIT_SUBMISSION_INFO_ROOT)){ JSONObject submissionInfoRoot = jsonRoot.optJSONObject(GuideConstants.WRAPPED_SUBMIT_SUBMISSION_INFO_ROOT); if(submissionInfoRoot.has(GuideConstants.WEB_CHANNE_CCR_DATA_ROOT)){ JSONObject iccData = submissionInfoRoot.optJSONObject(GuideConstants.WEB_CHANNE_CCR_DATA_ROOT); return iccData; } } } } return null; } /** * Checks case-insensitively if a string is present in a list of strings * @param list * @param searchString * @param ignoreCase * @pad.exclude Exclude from Published API. */ public static Boolean listContains(List list, String searchString, Boolean ignoreCase) { if (searchString != null && list != null) { if (!ignoreCase) { return list.contains(searchString); } for (String str : list) { if (searchString.equalsIgnoreCase(str)) { return Boolean.TRUE; } } } return Boolean.FALSE; } /** * Returns {@link org.apache.sling.api.scripting.SlingBindings} from given {@link org.apache.sling.api.SlingHttpServletRequest} * * @param slingRequest * @return {@link org.apache.sling.api.scripting.SlingBindings} * @pad.exclude Exclude from Published API. */ public static SlingBindings getSlingBinding(SlingHttpServletRequest slingRequest) { SlingBindings bindings = null; if (slingRequest != null) { bindings = (SlingBindings) slingRequest.getAttribute(SlingBindings.class.getName()); } return bindings; } /** * This API check if the given resource is a form * by looking for resource type property of jcr:content/guideContainer * * @param resource resource pointing to the page * @return boolean Indicating if the given page is a form or not */ public static boolean checkIfForms(Resource resource) { Resource pageContent = resource.getChild(GuideConstants.JCR_CONTENT_NODENAME); if (pageContent != null) { // check if there is a child node of name "guideContainer" Resource guideContainer = pageContent.getChild(GuideConstants.GUIDECONTAINER_NODENAME); if (guideContainer != null) { // there can be use-case where guideContainer can be locked, in case of editable templates // generally this is not a correct use-case, hence logging the same and ensuring form editor loads Resource guideContainerMergedResource = guideContainer.adaptTo(TemplatedResource.class); if (guideContainerMergedResource == null) { // guide container is present inside initial content of editable template or inside static template guideContainerMergedResource = guideContainer; } else { logger.warn("GuideContainer resource has been locked in the editable template for the form - " + resource.getPath()); } // check if the given resource is a container resource of forms return GuideConstants.CONTAINER_RESOURCES.contains(guideContainerMergedResource.getResourceType()); } else { // if guide container is not present as a child, it may be part of structure aspect of a template // in that case we need to move from page to template and from template to structure Page currentPage = resource.adaptTo(Page.class); if (currentPage != null) { Template template = currentPage.getTemplate(); if (template == null) { return false; } if (template.hasStructureSupport()) { Resource res = pageContent.adaptTo(TemplatedResource.class); if (res == null) { // if there is no templated resource, return false return false; } Iterator it = res.listChildren(); while (it.hasNext()) { Resource child = it.next(); // check if the resource "r" is a guide container String name = child.getName(); if (GuideConstants.GUIDE_CONTAINER_NODE_NAME.equals(name)) { logger.warn("GuideContainer resource has been locked in the editable template for the form - " + resource.getPath()); // check if the guide container type has the container resource type return GuideConstants.CONTAINER_RESOURCES.contains(child.getResourceType()); } } } } } } return false; } /** * @pad.exclude Exclude from Published API. */ public static Resource getMergedFormResource (ResourceResolver resolver, String formPath) { Resource formResource = resolver.resolve(formPath); if (ResourceUtil.isNonExistingResource(formResource)) { return null; } if (!checkIfForms(formResource)) { return null; } Page currentPage = formResource.adaptTo(Page.class); if (currentPage == null) { return formResource; } Template template = currentPage.getTemplate(); if (template != null && template.hasStructureSupport()) { formResource = formResource.adaptTo(TemplatedResource.class); } return formResource; } /** * This API check if the given resource(template) is a forms template * by looking for guideComponentType property in jcr:content node of template * * @param resource * @return true if the resource is an AEM Forms template, false otherwise */ public static boolean checkIfFormsTemplate(Resource resource) { Resource templateContent = resource.getChild("jcr:content"); if (templateContent != null) { // check if the property guideComponentType exist ValueMap templateContentValueMap = templateContent.getValueMap(); String guideComponentType = templateContentValueMap.get(GuideConstants.FD_GUIDE_COMPONENT_TYPE, ""); if (guideComponentType != null && guideComponentType.length() > 0) { return true; } } return false; } /** * @pad.exclude Exclude from published API */ public static boolean isRepeatable(JSONObject obj) { // assume can be repeatable if it has max or min occurs prop int maxOccur = obj.optInt(GuideConstants.MAX_OCCUR, 1), minOccur = obj.optInt(GuideConstants.MIN_OCCUR, 1); return minOccur != 1 || maxOccur != 1; } /** * @pad.exclude Exclude from published API */ public static boolean isCompositeField(JSONObject obj) { String objType = obj.optString(GuideConstants.GUIDE_NODE_CLASS, ""); return GuideConstants.GUIDE_COMPOSITE_FIELD_TYPES.contains(objType); } /** * @pad.exclude Exclude from published API */ public static boolean isSubmitableField(JSONObject obj) { String objType = obj.optString(GuideConstants.GUIDE_NODE_CLASS, ""); return GuideConstants.GUIDE_SUBMITABLE_FIELD_TYPES.contains(objType); } /** * Returns true if type equals guideTextDraw or guideAdobeSignBlock * @pad.exclude Exclude from published API */ public static boolean isStaticTextType(String type) { return GuideConstants.GUIDE_FIELD_TEXTDRAW.equals(type) || GuideConstants.GUIDE_FIELD_ADOBE_SIGN_BLOCK.equals(type); } /** * Returns true if type equals guideTextDraw or guideAdobeSignBlock and have document fragment * variables inside it * @pad.exclude Exclude from published API */ public static boolean hasFloatingField(JSONObject obj) { Boolean isFloatingField = Boolean.FALSE; String objType = obj.optString(GuideConstants.GUIDE_NODE_CLASS, ""); if (GuideUtils.isStaticTextType(objType)) { JSONArray documentFragmentVariables = obj.optJSONArray(GuideConstants.DOCUMENT_FRAGMENT_VARIABLES); if (documentFragmentVariables != null && documentFragmentVariables.length() > 0) { isFloatingField = Boolean.TRUE; } } return isFloatingField; } /** * Checks if the given container resource is a deprecated AD or not * @pad.exclude Exclude from published API */ public static Boolean isDeprecatedAD(Resource containerResource){ Boolean isDeprecatedAD = false; if(containerResource != null) { if(containerResource.isResourceType(GuideConstants.RT_GUIDE_DOCUMENT_CONTAINER)){ isDeprecatedAD = true; } else if(containerResource.isResourceType(GuideConstants.RT_WEB_DOCUMENT_CONTAINER)){ isDeprecatedAD = false; } } return isDeprecatedAD; } /** * @pad.exclude Exclude from published API */ public static FileAttachmentWrapper findFileAttachment(List attachmentWrappers, String fileName) { if (attachmentWrappers != null) { for (FileAttachmentWrapper fileAttachmentWrapper : attachmentWrappers) { if (StringUtils.equals(fileAttachmentWrapper.getFileName(), fileName)) { return fileAttachmentWrapper; } } } return null; } /** * @pad.exclude Exclude from published API */ public static Resource getFormResource(Resource otherResource, String fragRef) { if (StringUtils.isNotBlank(fragRef) && otherResource != null) { String path = GuideUtils.convertFMAssetPathToContainerPath(fragRef); ResourceResolver resolver = otherResource.getResourceResolver(); return resolver.getResource(path); } return null; } /** * @pad.exclude Exclude from published API */ public static Resource getFormBreakpointResource(ResourceResolver resolver, String formPath) { Resource formResource = getMergedFormResource(resolver, formPath); if (formResource == null) { return null; } if(formResource.getChild(GuideConstants.JCR_CONTENT_NODENAME) == null) { return null; } Resource jcrResource = formResource.getChild(GuideConstants.JCR_CONTENT_NODENAME); if (jcrResource == null) { return null; } Resource responsiveResource = jcrResource.getChild("cq:responsive"); if (responsiveResource == null) { return null; } return responsiveResource.getChild("breakpoints"); } /** * @pad.exclude Exclude from published API */ public static JSONObject getFormJson(Resource formRes, GuideModelTransformer guideModelTransformer) { JSONObject guideJson = null; try { //TODO: Should not be forced to fallback to default locale Locale defaultFallBackLocaleObject = new Locale(GuideConstants.DEFAULT_FALLBACK_LOCALE); guideJson = guideModelTransformer.exportGuideJsonObject(formRes, null, defaultFallBackLocaleObject); } catch (GuideException e) { logger.error("Failed to Fetch Form Json", e); } return guideJson; } /** * @pad.exclude Exclude from published API */ private static Resource findGuideContainerFromPage(Resource resource) { if (resource == null) { return null; } if (GuideUtils.isGuideContainerResource(resource)) { return resource; } Resource r = null; Iterator t = resource.listChildren(); while (t.hasNext() && r == null) { Resource child = t.next(); if (child != null && GuideUtils.isGuideContainerResource(resource)) { return child; } else { r = findGuideContainerFromPage(child); } } return r; } /** * This function returns the Guide Container path from any resource in the adaptive form. * Eg - if the resource is a header/footer that is outside the guideContainer, then it traverses up to the cq:page and then * searches down from cq:page for guideContainer * @pad.exclude Exclude from published API */ public static String getGuideContainerPathFromResource(SlingHttpServletRequest request, Resource resource) { String path = null; Resource pageResource = getPageResource(resource); Resource guideContainer = findGuideContainerFromPage(pageResource); if (guideContainer != null) { path = guideContainer.getPath(); } return path; } /** * @param obj : guide json obj * @return * @pad.exclude Exclude from Published API. */ public static boolean isUnboundObj(JSONObject obj) { String bindRef = obj.optString(GuideConstants.BIND_REF); return StringUtils.isBlank(bindRef); } /** * Returns the theme content path from Guide container resource * * @param guideContainerResource resource to the form container * @return Path to theme content resource of theme associated with guide container. */ public static String getThemeContentRef(Resource guideContainerResource) { ValueMap vmap = guideContainerResource.getValueMap(); String themeRef = vmap.get(GuideConstants.THEME_CLIENTLIB, String.class); if (StringUtils.isNotEmpty(themeRef)) { themeRef = themeRef + GuideThemeConstants.RELATIVE_PATH_JCR_CONTENT; } return themeRef; } /** * Transform JSON into Resource. * * @param resourceResolver ResourceResolver to be set for resource being returned * @param path path on which resource has to be mounted * @param json input JSON which is to be transformed into resource * @return Resource * @pad.exclude Excluede from published API */ public static Resource getResource(ResourceResolver resourceResolver, String path, JSONObject json) { return getResource(resourceResolver, path, json, null); } /** * Transform JSON into Resource. * * @param resourceResolver ResourceResolver to be set for resource being returned * @param path path on which resource has to be mounted * @param json input JSON which is to be transformed into resource * @param parentResource parent resource of synthetic resource that is being generated. * @return Resource * @pad.exclude Excluede from published API */ public static Resource getResource(ResourceResolver resourceResolver, String path, JSONObject json, Resource parentResource) { return getResource(resourceResolver, path, json, parentResource, false); } /** * Transform JSON into Resource. * * @param resourceResolver ResourceResolver to be set for resource being returned * @param path path on which resource has to be mounted * @param json input JSON which is to be transformed into resource * @param parentResource parent resource of synthetic resource that is being generated. * @param useNameAsResourceIdentifier use name of resource as the key during resoruce creation from json * @return Resource * @pad.exclude Excluede from published API */ public static Resource getResource(ResourceResolver resourceResolver, String path, JSONObject json, Resource parentResource, boolean useNameAsResourceIdentifier) { if (json == null) { return new GuideValueMapResource(resourceResolver, path, null, new ValueMapDecorator(new HashMap()), parentResource); } String resourceType = JcrConstants.NT_UNSTRUCTURED; if (json.has(JcrResourceConstants.SLING_RESOURCE_TYPE_PROPERTY)) { resourceType = json.optString(JcrResourceConstants.SLING_RESOURCE_TYPE_PROPERTY); } else if (json.has(JcrResourceConstants.SLING_RESOURCE_SUPER_TYPE_PROPERTY)) { resourceType = json.optString(JcrResourceConstants.SLING_RESOURCE_SUPER_TYPE_PROPERTY); } Map map = new HashMap(); ValueMap valueMap = new ValueMapDecorator(map); List children = new ArrayList(); Resource valueMapResource = new GuideValueMapResource(resourceResolver, path, resourceType, valueMap, children, parentResource); try { Iterator keys = json.keys(); while (keys.hasNext()) { String key = keys.next(); Object value = json.get(key); if (value instanceof JSONObject) { String name = null; if (useNameAsResourceIdentifier) { // using name of json object to create a resource // sometimes key can be random(for example, in multi channel document use-case) name = ((JSONObject) value).optString(GuideConstants.NAME); if (StringUtils.isBlank(name)) { name = key; } } else { // use key present in json as the name of the resource name = key; } // populate children Resource child = getResource(resourceResolver, path + "/" + name, (JSONObject) value, valueMapResource, useNameAsResourceIdentifier); children.add(child); } else if (value instanceof JSONArray) { JSONArray array = ((JSONArray) value); ArrayList list = new ArrayList(); for (int i = 0; i < array.length(); i++) { list.add(array.get(i)); } map.put(key, list.toArray()); } else { map.put(key, value); } } } catch (JSONException e) { logger.error("Exception while creating resource from JSON", e); } return valueMapResource; } /** * Fill queue with resources (outside guide container) which can be mapped to static text and image in document of record * * @param queue queue to be filled * @param resource parent of guide container * @param type either StaticText or Image * @pad.exclude Exclude from public API */ public static void fillQueue(Queue queue, Resource resource, String type) { if (resource == null) { return; } Page page = resource.getParent().adaptTo(Page.class); // if form references template, get branding components from template (structure layer) if (page != null) { Template template = page.getTemplate(); if (template != null && template.hasStructureSupport()) { // Resource structure = TemplateUtils.getStructureResource(resource); // Resource structureContent = structure.getChild(NameConstants.NN_CONTENT); // TemplateUtils is not exposed by wcm-core so manually get structure content ResourceResolver resourceResolver = resource.getResourceResolver(); Resource structureContent = resourceResolver.getResource(template.getPath() + "/structure/jcr:content"); if (structureContent != null) { Iterator iterator = structureContent.listChildren(); while (iterator.hasNext()) { Resource child = iterator.next(); if (child.isResourceType("fd/af/components/guideContainer")) { continue; } else { fillQueue(queue, child, type); } } } } } Iterator iterator = resource.listChildren(); while (iterator.hasNext()) { Resource child = iterator.next(); if (child.isResourceType("fd/af/components/guideheader") || child.isResourceType("fd/af/components/guidefooter") || child.isResourceType("wcm/foundation/components/responsivegrid")) { fillQueue(queue, child, type); } else if (child.isResourceType("fd/af/components/guideimage")) { if (StringUtils.equals(type, "Image")) { queue.add(child); } } else if (child.isResourceType("fd/af/components/afFormTitle") || child.isResourceType("fd/af/components/guidetextdraw")) { if (StringUtils.equals(type, "StaticText")) { queue.add(child); } } } } /** * Get components outside guide container which could be mapped to branding components in document of record. * Only static text and image components can be mapped. * * @param resourceResolver * @param guideContainerPath * @param type either StaticText or Image * @param value component which should be selected * @return {Resource} Applicable components as children of items resource * @pad.exclude Exclude from public API. */ public static Resource getAdaptiveFormBrandingComponents(ResourceResolver resourceResolver, String guideContainerPath, String type, String value, I18n i18n) { if (StringUtils.isEmpty(guideContainerPath) || resourceResolver.getResource(guideContainerPath) == null) { return null; } Resource jcrContentResource = resourceResolver.getResource(guideContainerPath).getParent(); Map map = new HashMap(); ValueMap vm = new ValueMapDecorator(map); List children = new ArrayList(); Resource itemsResource = new ValueMapResource(resourceResolver, jcrContentResource.getPath(), JcrConstants.NT_UNSTRUCTURED, vm, children); Queue queue = new ArrayDeque<>(); fillQueue(queue, jcrContentResource, type); Iterator iterator = queue.iterator(); while (iterator.hasNext()) { Resource child = iterator.next(); ValueMap properties = child.getValueMap(); ValueMap valueMap = new ValueMapDecorator(new HashMap()); ValueMapResource option = new ValueMapResource(resourceResolver, child.getPath(), JcrConstants.NT_UNSTRUCTURED, valueMap); String name = properties.get("name", String.class); String humanizedName = NameUtils.humanize(name); String text = ""; if (i18n != null) { if (StringUtils.startsWith(humanizedName, "Form ")) { String sentence = "Use Adaptive " + humanizedName; String localizedSentence = i18n.getVar(sentence); text = StringUtils.equals(sentence, localizedSentence) ? i18n.get("Use Adaptive {0}", "0 replaced with type, example Logo Image", i18n.getVar(humanizedName)) : localizedSentence; } else { String sentence = "Use Adaptive Form " + humanizedName; String localizedSentence = i18n.getVar(sentence); text = StringUtils.equals(sentence, localizedSentence) ? i18n.get("Use Adaptive Form {0}", "0 replaced with type, example Logo Image", i18n.getVar(humanizedName)) : localizedSentence; } } else { text = StringUtils.startsWith(humanizedName, "Form ") ? "Use Adaptive " + humanizedName : "Use Adaptive Form " + humanizedName; } valueMap.put("value", name); valueMap.put("text", text); if (StringUtils.equals(value, name)) { valueMap.put("selected", true); } children.add(option); } ValueMap valueMap = new ValueMapDecorator(new HashMap()); ValueMapResource option = new ValueMapResource(resourceResolver, "", JcrConstants.NT_UNSTRUCTURED, valueMap); valueMap.put("value", " "); valueMap.put("text", i18n != null ? i18n.get("Enter Custom") : "Enter Custom"); children.add(option); return itemsResource; } /** * Gets the theme resource for a given Adaptive Form * * @param formResource Form resource. * @return Theme Resource for the Form Resource */ public static Resource getThemeResource(Resource formResource) { String themeRef = getThemeContentRef(formResource); if (StringUtils.isNotBlank(themeRef)) { ResourceResolver resolver = formResource.getResourceResolver(); return resolver.getResource(themeRef); } return null; } /** * Given user principal returns true if * principal is allowed to write scripts * * @param userPrincipal {@link Principal} user principal * @param um reference to UserManager service * @return true if script authoring is allowed * @pad.exclude Exclude from public API. */ public static boolean isScriptAuthoringAllowed(Principal userPrincipal, UserManager um) throws RepositoryException { boolean isAllowed = false; User user = (User) um.getAuthorizable(userPrincipal); if (user.isAdmin()) { isAllowed = true; } else { Group scriptWritersGroup = (Group) um.getAuthorizable(GuideConstants.SCRIPT_WRITERS_GROUP); Group templateScriptWritersGroup = (Group) um.getAuthorizable(GuideConstants.TEMPLATE_SCRIPT_WRITERS_GROUP); if (scriptWritersGroup != null) { isAllowed = scriptWritersGroup.isMember(user); } else if (templateScriptWritersGroup != null) { isAllowed = templateScriptWritersGroup.isMember(user); } } return isAllowed; } /** * Given user principal and list of groups returns true if * principal is to be shown rule editor * * @param userPrincipal {@link Principal} user principal * @param um reference to UserManager * @param customGroups list of group names to show rule editor * @return true if script authoring is allowed * @pad.exclude Exclude from public API. */ public static boolean showRuleEditor(Principal userPrincipal, UserManager um, String[] customGroups) throws RepositoryException { if (customGroups == null || customGroups.length == 0) { return true; } User user = (User) um.getAuthorizable(userPrincipal); if (user.isAdmin()) { return true; } for (String groupName : customGroups) { Group group = (Group) um.getAuthorizable(groupName); if (group != null && group.isMember(user)) { return true; } } return false; } /** * Gets the naked bound data from the given data * @param data * @return * @pad.exclude Exclude from published API */ public static Object getNakedBoundJsonData(Object data){ Object boundData = null; if (data instanceof JSONObject && ((JSONObject)data).optJSONObject("afData") != null) { //if the data starts with afData boundData = ((JSONObject)data).optJSONObject("afData").optJSONObject("afBoundData").optJSONObject("data"); } else { boundData = data; } return boundData; } /** * This method will process the given resource and return a boolean value signifying whether DOR * is present for the AF or not. * @param formResource The guideContainer Resource or Form Asset Resource or CQ Page resource. * @return true if present, false otherwise. */ public static boolean isDORConfigured(Resource formResource) { // populating with sample "en" for locale as aim is not to get correct value but to check status. String xdpRef = getDoRTemplateRef(convertGuideContainerPathToFMAssetPath(formResource.getPath()), "en", formResource.getResourceResolver()); return StringUtils.isNotEmpty(xdpRef); } /** * Recursively generates the SOM Expression of an element in guide container. * * @param element - Resource, whose SOM Expression needs to be calculated. * @return String - SOM Expression */ public static String generateSOM(Resource element) { String som = ""; ValueMap valueMap = ResourceUtil.getValueMap(element); Resource parentElement = element.getParent(); String guideNodeClass = (String) valueMap.get("guideNodeClass"); if (guideNodeClass != null) { som = valueMap.get("name") + "[0]"; if ("guideContainerNode".equals(guideNodeClass)) { return "guide[0]." + som; } } else { return generateSOM(parentElement) + som; } return generateSOM(parentElement) + "." + som; } /** * Marshalls the naked data with AF wrapper tags. * Any custom prefill service could make use of this utility for marshalling the data * @param data form data * @return marshalled form data with AF wrapper tags */ public static Object marshallDataWithAFWrapper(Object data) throws Exception { if(data instanceof String){ // convert json string to map ObjectMapper mapper = new ObjectMapper(); Map map = new HashMap(); map = mapper.readValue(data.toString(), new TypeReference>(){}); data = map; } // add wrapper tags if (data instanceof Map) { Map jsonData = (Map) (data); // check if the json data is wrapped with afData tag // marshall the json data with afData root object if (jsonData.get(GuideConstants.WRAPPED_SUBMIT_XML_ROOT) == null) { // add the bound data Map afBoundData = new HashMap(); Map afUnboundData = new HashMap(); Map boundDataObject = new HashMap(); Map unboundDataObject = new HashMap(); // walk through all the root entities Iterator entries = jsonData.entrySet().iterator(); // get the entity from top while(entries.hasNext()) { Map.Entry thisEntry = (Map.Entry) entries.next(); String entityName = (String)thisEntry.getKey(); // done to make root element as object if (jsonData.get(entityName) instanceof List) { List jsonList = (List) jsonData.get(entityName); // todo: fix this in dermis later Map arrayData = (Map) jsonList.get(0); // add this to the root entity jsonData = new HashMap(); jsonData.put(entityName, arrayData); } } boundDataObject.put(GuideConstants.UNWRAPPED_SUBMIT_ROOT, jsonData); unboundDataObject.put(GuideConstants.UNWRAPPED_SUBMIT_ROOT, new HashMap()); // let's add the wrapper tag to the data Map afData = new HashMap(); Map afDataChildren = new HashMap(); afDataChildren.put(GuideConstants.WRAPPED_SUBMIT_BOUND_ROOT, boundDataObject); afDataChildren.put(GuideConstants.WRAPPED_SUBMIT_UNBOUND_ROOT, unboundDataObject); afData.put(GuideConstants.WRAPPED_SUBMIT_XML_ROOT, afDataChildren); // convert map to json data = new ObjectMapper().writeValueAsString(afData); } } return data; } /** * @pad.exclude Exclude from Published API. */ public static String getRedirectUrl(String redirectUrl, String formPath) { if (StringUtils.isNotBlank(redirectUrl) && redirectUrl.matches(GuideConstants.URL_PROTOCOL_REGEX)) { //redirect url starting with http/https. returning as it is. return redirectUrl; } else if (StringUtils.isNotBlank(redirectUrl)) { final int lastSlash = redirectUrl.lastIndexOf('/'); if (redirectUrl.indexOf('.', lastSlash) == -1) { //redirect url is an aem page. Append .html in the end redirectUrl = redirectUrl + ".html"; } } else if (StringUtils.isNotBlank(formPath)) { //redirect url is empty. provide default Thank you page url, if formPath is not blank. redirectUrl = formPath + "." + GuideConstants.THANKYOU_PAGE; } return redirectUrl; } /* * @pad.exclude Exclude from Published API. */ //TODO this API again assumes the location of adative form guide container needs to be more generic. public static List getGuideContainerFromFormPage(Resource formPageResource) { List guideContainerResourceList = new ArrayList(); if (formPageResource != null && !(formPageResource instanceof NonExistingResource)) { Resource jcrContentNode = formPageResource.getChild(JcrConstants.JCR_CONTENT); if (jcrContentNode != null) { Iterator formPageResourceIterator = jcrContentNode.listChildren(); while (formPageResourceIterator.hasNext()) { Resource currentResource = formPageResourceIterator.next(); if (GuideUtils.isGuideContainerResource(currentResource)) { guideContainerResourceList.add(currentResource); } } } } return guideContainerResourceList; } /* * @pad.exclude Exclude from Published API. */ public static String convertFMAssetPathToFormPagePath (String FMAssetPath) { return StringUtils.replace(FMAssetPath, GuideConstants.FM_DAM_ROOT, GuideConstants.FM_AF_ROOT, 1); } /* * @pad.exclude Exclude from Published API. */ public static String convertFormPagePathToFMAssetPath(String formPagePath) { formPagePath = removeChannelNameFromEndIfExist(formPagePath); return StringUtils.replace(formPagePath, GuideConstants.FM_AF_ROOT, GuideConstants.FM_DAM_ROOT, 1); } /* * @pad.exclude Exclude from Published API. */ public static String convertADAssetPathToWebChannelPagePath (String FMDocAssetPath) { return convertFMAssetPathToFormPagePath(FMDocAssetPath) + GuideConstants.DOC_WEB_CHANNEL; } private static List convertIteratorToList(Iterator i) { List list = new ArrayList(); while (i.hasNext()) { list.add(i.next()); } return list; } public static JSONArray getMergedJSONArray(JSONArray array1, JSONArray array2) throws JSONException { JSONArray array = new JSONArray(); JSONArray[] arrays = {array1, array2}; for (JSONArray currentArray : arrays) { if (currentArray != null) { for (int i = 0; i < currentArray.length(); i++) { array.put(currentArray.get(i)); } } } return array; } public static class AuthoringError { private String errorMessage; private String errorCode; private ErrorType errorType; private enum ErrorType { Error("error"), Info("info"), Warn("warn"); private String errorType; ErrorType(String errorType) { this.errorType = errorType; } @Override public String toString() { return errorType; } } public AuthoringError(String errorMessage) { this.errorMessage = errorMessage; this.errorType = ErrorType.Error; } public AuthoringError(String errorMessage, String errorCode) { this(errorMessage); this.errorCode = errorCode; } public AuthoringError(String errorMessage, String errorCode, ErrorType errorType) { this(errorMessage, errorCode); this.errorType = errorType; } public String getErrorMessage() { return errorMessage; } public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; } public String getErrorCode() { return errorCode; } public void setErrorCode(String errorCode) { this.errorCode = errorCode; } public String getErrorType() { return errorType.toString(); } public void setErrorType(ErrorType errorType) { this.errorType = errorType; } } /** * Checks whether resource is a web channel resource. * It checks fd:channelType property on jcr:content node. * * @param resource * * @return boolean * */ public static boolean isWebChannel(Resource resource) { if (resource == null) { return false; } final PageManager pageManager = resource.getResourceResolver().adaptTo(PageManager.class); if (pageManager != null) { final Page page = pageManager.getContainingPage(resource); if(page != null) { String channel = (String) page.getContentResource().getValueMap().get(GuideConstants.PROP_FD_CHANNEL_TYPE); if (StringUtils.equalsIgnoreCase(GuideConstants.CHANNEL_NAME_WEB, channel)) { return true; } } } return false; } /* * This method finds the parent resource of the given parentResourceType, up in the hierarchy. * If not found, it returns null. * * @param resource, whose parent resource needs to be calculated. * @param parentResourceType * * @return parent resource if found, otherwise null. * * */ public static Resource getParentResource(Resource resource, String parentResourceType) { if (resource == null || StringUtils.isBlank(parentResourceType)) { return null; } Resource parentResource = null; while (resource != null) { if (resource.isResourceType(parentResourceType)) { parentResource = resource; break; } resource = resource.getParent(); } return parentResource; } /* * Computes common repeatable bindRef. * This method iterates over the children of table row resource, computes all the children bindRefs and then * calculates common repeatable bindRef. * * if all bindrefs belong to collection level zero then there will be no common repeatable bindref. * * @param guideModelImporter * @param tableRowResource * @param guideContainerResource * * @return Map map * boolean hasRepeatableBindRef = map.get(GuideConstants.HAS_REPEATABLE_BIND_REFS) * String commonRepeatableBindRef = map.get(GuideConstants.COMMON_REPEATABLE_BIND_REF) * * @pad.exclude Exclude from Published API. * */ public static Map getCommonRepeatableBindRefForTableRow(GuideModelImporter guideModelImporter, Resource tableRowResource, Resource guideContainerResource) { if (guideModelImporter == null || tableRowResource == null || guideContainerResource == null) { return null; } ResourceResolver resourceResolver = tableRowResource.getResourceResolver(); if (resourceResolver == null) { return null; } Map map = new HashMap<>(2); map.put(GuideConstants.HAS_REPEATABLE_BIND_REFS, false); map.put(GuideConstants.COMMON_REPEATABLE_BIND_REF, null); //Find all children bind refs of table row resource Set childrenBindRefs = new HashSet<>(); computeRowChildrenBindRefs(tableRowResource, childrenBindRefs); try { JSONObject jsonObject = getFDMJson(guideModelImporter, guideContainerResource, GuideConstants.DEFAULT_MAX_COLLECTION_LEVEL); Map> bindRefToCollectionInfoMap = new HashMap<>(); createBindRefToCollectionInfoMap(jsonObject, bindRefToCollectionInfoMap); //Find common repeatable bindRef Set commonRepeatableBindRefs = new HashSet<>(); boolean allCollectionLevelZero = true; boolean isAnyCollectionLevelGreaterThanOne = false; for (String childrenBindRef : childrenBindRefs) { if (bindRefToCollectionInfoMap.containsKey(childrenBindRef)) { Map innerMap = bindRefToCollectionInfoMap.get(childrenBindRef); String parentCollectionPath = (String) innerMap.get(GuideConstants.PARENT_COLLECTION_PATH); Integer collectionLevel = (Integer) innerMap.get(GuideConstants.COLLECTION_LEVEL); if (StringUtils.isNotBlank(parentCollectionPath)) { commonRepeatableBindRefs.add(parentCollectionPath); } if (collectionLevel > 0) { allCollectionLevelZero = false; } if (collectionLevel > 1) { isAnyCollectionLevelGreaterThanOne = true; } } } /* * if all bindRefs belongs to collection level 0, then there is no need for further calculation because there * will be no repeatable bindRef. * */ if (allCollectionLevelZero) { return map; } /* * In 6.4 release, we are considering only 1st level of collection. * Remove this condition to allow using collection level greater than 1. * */ if (isAnyCollectionLevelGreaterThanOne) { map.put(GuideConstants.HAS_REPEATABLE_BIND_REFS, true); map.put(GuideConstants.COMMON_REPEATABLE_BIND_REF, null); return map; } String commonRepeatableBindRef = null; if (commonRepeatableBindRefs.size() == 1) { for (String str : commonRepeatableBindRefs) { commonRepeatableBindRef = str; } } map.put(GuideConstants.HAS_REPEATABLE_BIND_REFS, true); map.put(GuideConstants.COMMON_REPEATABLE_BIND_REF, commonRepeatableBindRef); } catch (JSONException e) { logger.error("Can not parse json string.", e); } return map; } /* * Computes bind references of children of tableRowResource and writes in bindRefs. * * @param tableRowResource * @param bindRefs * * */ private static void computeRowChildrenBindRefs(Resource tableRowResource, Set bindRefs) { if (tableRowResource == null) { return; } Iterator children = tableRowResource.listChildren(); while (children.hasNext()) { Resource child = children.next(); if (child.hasChildren()) { computeRowChildrenBindRefs(child, bindRefs); } else { ValueMap vm = child.getValueMap(); String bindRef = vm.get(GuideConstants.BIND_REF, String.class); if (StringUtils.isNotBlank(bindRef)) { bindRefs.add(bindRef); } } } } /* * Return FDM json using guideContainerResource * * @param guideModelImporter * @param guideContainerResource * * @return FDM json * */ private static JSONObject getFDMJson(GuideModelImporter guideModelImporter, Resource guideContainerResource, int maxCollectionLevel) throws JSONException { if (guideModelImporter == null || guideContainerResource == null) { return null; } String schemaRef = GuideContainer.from(guideContainerResource).getSchemaRef(); SchemaImportOptions schemaImportOptions = new SchemaImportOptions(); schemaImportOptions.setGuideContainer(guideContainerResource); schemaImportOptions.setSchemaPath(schemaRef); schemaImportOptions.setSchemaType(GuideSchemaType.FDM); schemaImportOptions.setMaxCollectionLevel(Integer.valueOf(maxCollectionLevel)); String jsonStr = guideModelImporter.createFormJsonFromSchema(schemaImportOptions); JSONObject jsonObject = new JSONObject(jsonStr); return jsonObject; } /* * Computes common repeatable item. * This method calculates repeatable item from xExp and yExp from chart resource and set the property on the same resource. * * xExp and yExp should belong to collection level greater than 0. * * @param guideModelImporter * @param chartResource * @param guideContainerResource * * @return Map map * boolean hasRepeatableBindRef = map.get(GuideConstants.HAS_REPEATABLE_BIND_REFS) * String commonRepeatableBindRef = map.get(GuideConstants.COMMON_REPEATABLE_BIND_REF) * * @pad.exclude Exclude from Published API. * */ public static Map getCommonRepeatableItemForChart(GuideModelImporter guideModelImporter, Resource chartResource, Resource guideContainerResource) { if (guideModelImporter == null || chartResource == null || guideContainerResource == null) { return null; } ResourceResolver resourceResolver = chartResource.getResourceResolver(); if (resourceResolver == null) { return null; } Map map = new HashMap<>(2); map.put(GuideConstants.HAS_REPEATABLE_BIND_REFS, false); map.put(GuideConstants.COMMON_REPEATABLE_BIND_REF, null); //Find xExp and yExp value ValueMap vm = chartResource.getValueMap(); String xExp = vm.get(GuideConstants.CHART_X_EXP, String.class), yExp = vm.get(GuideConstants.CHART_Y_EXP, String.class); if (StringUtils.isBlank(xExp) || StringUtils.isBlank(yExp)) { return null; } Boolean multiSeries = vm.get(GuideConstants.CHART_MULTI_SERIES, Boolean.class); String seriesNameExp = vm.get(GuideConstants.CHART_SERIES_NAME, String.class); int expectedSeriesNameCollectionLevel = 0; int expectedXYCollectionLevel = 1; if (Boolean.TRUE.equals(multiSeries)) { if (StringUtils.isBlank(seriesNameExp)) { map.put(GuideConstants.HAS_REPEATABLE_BIND_REFS, true); map.put(GuideConstants.COMMON_REPEATABLE_BIND_REF, null); return map; } expectedSeriesNameCollectionLevel++; expectedXYCollectionLevel++; } try { //get FDM json JSONObject jsonObject = getFDMJson(guideModelImporter, guideContainerResource, expectedXYCollectionLevel); Map> bindRefToCollectionInfoMap = new HashMap<>(); createBindRefToCollectionInfoMap(jsonObject, bindRefToCollectionInfoMap); int seriesNameCollectionLevel = 0; String seriesParentCollectionPath = null; if (Boolean.TRUE.equals(multiSeries) && bindRefToCollectionInfoMap.containsKey(seriesNameExp)) { Map innerMap = bindRefToCollectionInfoMap.get(seriesNameExp); seriesParentCollectionPath = (String) innerMap.get(GuideConstants.PARENT_COLLECTION_PATH); seriesNameCollectionLevel = (int) innerMap.get(GuideConstants.COLLECTION_LEVEL); } String xParentCollectionPath = null, yParentCollectionPath = null; int xCollectionLevel = 0, yCollectionLevel = 0; if (bindRefToCollectionInfoMap.containsKey(xExp)) { Map innerMap = bindRefToCollectionInfoMap.get(xExp); xParentCollectionPath = (String) innerMap.get(GuideConstants.PARENT_COLLECTION_PATH); xCollectionLevel = (int) innerMap.get(GuideConstants.COLLECTION_LEVEL); } if (bindRefToCollectionInfoMap.containsKey(yExp)) { Map innerMap = bindRefToCollectionInfoMap.get(yExp); yParentCollectionPath = (String) innerMap.get(GuideConstants.PARENT_COLLECTION_PATH); yCollectionLevel = (int) innerMap.get(GuideConstants.COLLECTION_LEVEL); } if (seriesNameCollectionLevel > 0 && (StringUtils.isBlank(seriesParentCollectionPath) || !StringUtils .equals(seriesParentCollectionPath, Text.getRelativeParent(xParentCollectionPath, 1)))) { map.put(GuideConstants.HAS_REPEATABLE_BIND_REFS, true); map.put(GuideConstants.COMMON_REPEATABLE_BIND_REF, null); } else if (seriesNameCollectionLevel == expectedSeriesNameCollectionLevel && StringUtils.isNotBlank(xParentCollectionPath) && StringUtils.isNotBlank(yParentCollectionPath) && StringUtils.equals(xParentCollectionPath, yParentCollectionPath) && xCollectionLevel == expectedXYCollectionLevel && yCollectionLevel == expectedXYCollectionLevel) { map.put(GuideConstants.HAS_REPEATABLE_BIND_REFS, true); map.put(GuideConstants.COMMON_REPEATABLE_BIND_REF, xParentCollectionPath); map.put(GuideConstants.CHART_SERIES_REPEATABLE_BIND_REF, seriesParentCollectionPath); } else { map.put(GuideConstants.HAS_REPEATABLE_BIND_REFS, true); map.put(GuideConstants.COMMON_REPEATABLE_BIND_REF, null); logger.error("Not able to compute repeatable item for chart component. xExp=" + xExp + ", yExp=" + yExp + ", seriesNameExp=" + seriesNameExp + ", xCollectionLevel=" + xCollectionLevel + ", yCollectionLevel=" + yCollectionLevel + ", seriesNameCollectionLevel=" + seriesNameCollectionLevel + ", expectedXYCollectionLevel=" + expectedXYCollectionLevel + ", expectedSeriesNameCollectionLevel=" + expectedSeriesNameCollectionLevel); } } catch (JSONException e) { logger.error("Can not parse json string.", e); } return map; } /** * * This method is used to get the List of Attachments passed with the form. * * @param parameterMap Map of request parameters * @return List of Attachments sent along with form * * @pad.exclude Exclude from Published API. */ public static List getFileAttachmentWrapperList(final ParameterMap parameterMap) { List fileAttachments = new ArrayList<>(); for (Map.Entry param : parameterMap.entrySet()) { RequestParameter[] rpm = param.getValue(); if (rpm != null && rpm.length > 0 && !rpm[0].isFormField()) { String newFileName = rpm[0].getFileName(); if(StringUtils.contains(newFileName, "/")) { newFileName = newFileName.substring(newFileName.indexOf("/") + 1); newFileName = newFileName.replaceAll("[^a-zA-Z0-9.]", ""); } FileAttachmentWrapper fileAttachment = new FileAttachmentWrapper(newFileName, rpm[0].getContentType(), rpm[0].get()); fileAttachments.add(fileAttachment); } } return fileAttachments; } /* * Creates bindRef to collectionInfo map. collectionInfo map has ParentCollectionInfo and CollectionLevel information. * * @param jsonObject FDM Json Object. * @param Map> * * */ private static void createBindRefToCollectionInfoMap(JSONObject fdmJsonObject, Map> map) throws JSONException { if (fdmJsonObject == null) { return; } Iterator keys = fdmJsonObject.keys(); while (keys.hasNext()) { String key = keys.next(); Object obj = fdmJsonObject.opt(key); if (obj instanceof JSONObject) { createBindRefToCollectionInfoMap(fdmJsonObject.optJSONObject(key), map); } else if (GuideConstants.BIND_REF.equals(key) && fdmJsonObject.has(GuideConstants.PARENT_COLLECTION_PATH) && fdmJsonObject.has(GuideConstants.COLLECTION_LEVEL)) { String bindRef = fdmJsonObject.getString(GuideConstants.BIND_REF); String parentCollectionPath = fdmJsonObject.getString(GuideConstants.PARENT_COLLECTION_PATH); int collectionLevel = fdmJsonObject.getInt(GuideConstants.COLLECTION_LEVEL); if (!map.containsKey(bindRef)) { Map innerMap = new HashMap<>(); innerMap.put(GuideConstants.PARENT_COLLECTION_PATH, parentCollectionPath); innerMap.put(GuideConstants.COLLECTION_LEVEL, collectionLevel); map.put(bindRef, innerMap); } } } } public static void setMasterAuthoringConfig(Map authoringConfig, ValueMap resourceProps) { if (resourceProps != null) { String masterPath = resourceProps.get("cq:master",""); if (StringUtils.isNotBlank(masterPath)) { authoringConfig.put("master", masterPath); } } } /** * Check if formPath points to valid resource of type formType * * @param resourceResolver * @param formPath * @param formType - formType is "guide" for AdaptiveForm and "mcdocument" for IC * @return boolean value */ public static boolean isValidFormResource(ResourceResolver resourceResolver, String formPath, String formType){ if("".equals(formPath)) { return false; } if (resourceResolver == null){ return false; } Resource formResource = resourceResolver.getResource(formPath); if (formResource == null) { return false; } Resource jcrContent = formResource.getChild("jcr:content"); if (jcrContent == null) { return false; } ValueMap props = jcrContent.getValueMap(); if (props.get(formType, 0) != 1) { return false; } return true; } public static boolean processChartAddition(GuideModelImporter guideModelImporter, Resource chartResource, Resource guideContainerResource) throws Exception { Map map = GuideUtils.getCommonRepeatableItemForChart(guideModelImporter, chartResource, guideContainerResource); if (map == null) { return false; } Boolean hasBindRef = (Boolean) map.get(GuideConstants.HAS_REPEATABLE_BIND_REFS); String repeatableItem = (String) map.get(GuideConstants.COMMON_REPEATABLE_BIND_REF); String seriesRepeatableItem = (String) map.get(GuideConstants.CHART_SERIES_REPEATABLE_BIND_REF); if (hasBindRef == true) { //if common repeatable item not found, then throw error. if (StringUtils.isBlank(repeatableItem)) { throw new Exception(MSG_NO_REPEATABLE_ITEM); } //save the repeatableItem on chart node. ValueMap valueMap = chartResource.adaptTo(ModifiableValueMap.class); if (valueMap == null) { valueMap = chartResource.getValueMap(); } valueMap.put(GuideConstants.CHART_REPEATABLE_ITEM, repeatableItem); if (StringUtils.isNotBlank(seriesRepeatableItem)) { valueMap.put(GuideConstants.CHART_SERIES_REPEATABLE_ITEM, seriesRepeatableItem); } } return hasBindRef; } } /** * DataLookUp class required by {@link org.apache.commons.lang3.text.StrSubstitutor} * * @author harsing * @pad.exclude Exclude from Published API. */ class DataLookUp extends StrLookup { private JSONObject guideValueMap; private Logger logger = LoggerFactory.getLogger(DataLookUp.class); public DataLookUp(JSONObject guideValueMap) { this.guideValueMap = guideValueMap; } @Override public String lookup(final String key) { String value = ""; try { if (guideValueMap.has(key)) { value = guideValueMap.getString(key); } } catch (Exception e) { logger.error("Unable to look up : " + e.toString(), e); } return value; } }