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

com.adobe.aemds.guide.utils.NodeStructureUtils 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.
 **************************************************************************/

/**
 * Created with IntelliJ IDEA.
 * User: rismehta
 * Date: 9/23/13
 * Time: 2:47 PM
 * To change this template use File | Settings | File Templates.
 */

package com.adobe.aemds.guide.utils;

import com.adobe.aemds.guide.common.GuideNode;
import com.adobe.aemds.guide.service.GuideException;
import org.apache.commons.lang.StringUtils;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.*;
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 java.util.*;

/**
 * @pad.exclude Exclude from Published API.
 */
@Component(metatype = false, immediate = true, label = "NodeStructureUtils")
@Service(value = NodeStructureUtils.class)
public class NodeStructureUtils {

    /**
     * The logger.
     */
    private static final Logger log = LoggerFactory.getLogger(NodeStructureUtils.class.getName());

    /**
     * Static name of all guide related nodes
     */
    public final static String GUIDECONTAINER_NODENAME = "guideContainer";
    public final static String GUIDE_FRAG_ROOT_PANEL = "rootPanel";
    public final static String LAYOUT_NODENAME = GuideConstants.LAYOUT_NODENAME;
    public final static String ROOTPANEL_NODENAME = GuideConstants.ROOTPANEL_NODENAME;
    public final static String ROOTPANEL_NODECLASS = "rootPanelNode";
    public final static String PANEL_NODENAME = "panel";
    public final static String GUIDE_ITEMS="items";


    // Default layout names for components(guidecontainer, rootPanel and Panel)
    public final static String DEFAULT_LAYOUT_GUIDECONTAINER = "defaultGuideLayout";
    public final static String DEFAULT_LAYOUT_ROOTPANEL = "panel/tabbedPanelLayout";
    public final static String DEFAULT_LAYOUT_PANEL = "gridFluidLayout2";

    /**
     * The prefix for all guide related resource types.
     */
    public static String RT_GUIDES_PREFIX = "/libs/fd/af/components/";

    /**
     * The prefix for all guide related layouts.
     */
    public static String RT_GUIDES_LAYOUT_PREFIX = "/libs/fd/af/layouts/";

    /**
     * The resource type for a root panel
     */
    public final static String RT_GUIDES_ROOTPANEL = RT_GUIDES_PREFIX + ROOTPANEL_NODENAME;

    /**
     * The default layout for a guide container
     */
    public final static String RT_GUIDES_LAYOUT_GUIDECONTAINER = RT_GUIDES_LAYOUT_PREFIX + DEFAULT_LAYOUT_GUIDECONTAINER;

    /**
     * The default layout for a root panel
     */
    public final static String RT_GUIDES_LAYOUT_ROOTPANEL = RT_GUIDES_LAYOUT_PREFIX + DEFAULT_LAYOUT_ROOTPANEL;

    /**
     * The default layout for a panel
     */
    public final static String RT_GUIDES_LAYOUT_PANEL = RT_GUIDES_LAYOUT_PREFIX + DEFAULT_LAYOUT_PANEL;

    public final static String LAYOUT_PATH_PROPERTY = GuideConstants.SLING_RESOURCE_TYPE;


    // properties for the guide components
    public static final String ELEMENT_PROPERTY_NAME = "name";
    private static final String ELEMENT_PROPERTY_DESCRIPTION = "jcr:description";
    private static final String ELEMENT_PROPERTY_QTIP = "qtip";
    private static final String ELEMENT_PROPERTY_TITLE = "jcr:title";
    private static final String ELEMENT_PROPERTY_IMAGE = "imagePath";

    // properties for guide fragments
    public static final String FRAG_REF = "fragRef";

    // Other properties required for Guides
    @Deprecated
    public static final String RT_PARSYS = "/libs/foundation/components/parsys";

    // Local instance variables
    private static NodeStructureUtils nodeStructureUtils = null;
    private static Map layoutmap;

    /**
     * @brief Constructor
     */
    public NodeStructureUtils() {
        // Create a map to store default layouts of components
        layoutmap = new HashMap();
        layoutmap.put(GUIDECONTAINER_NODENAME, RT_GUIDES_LAYOUT_GUIDECONTAINER);
        layoutmap.put(ROOTPANEL_NODENAME, RT_GUIDES_LAYOUT_ROOTPANEL);
        layoutmap.put(PANEL_NODENAME, RT_GUIDES_LAYOUT_PANEL);
    }


    /**
     * Singleton instance of NodeStructureUtils.
     *
     * @return a NodeStructureUtils
     */
    public static NodeStructureUtils getInstance() {
        if (nodeStructureUtils == null) {
            synchronized (NodeStructureUtils.class) {
                if (nodeStructureUtils == null) {
                    nodeStructureUtils = new NodeStructureUtils();
                }
            }
        }
        return nodeStructureUtils;
    }

    /**
     * Check the name of the resource.
     *
     * @return true if the name matches the given name
     */
    private static boolean checkName(final Resource resource, final String name) {
        return resource.getName().equals(name);
    }

    /**
     * Check if the layout node exists. If it doesn't creates one and returns the map of properties present in it
     *
     * @return Map containing the properites
     */
    public static Map getLayoutProperties(final SlingHttpServletRequest request, final Resource elementResource, final String cmpName) throws PersistenceException {

        if (elementResource == null) {
            return null;
        }
        Resource layoutnode = null;
        ResourceResolver resourceResolver = null;
        if (request != null) {
            resourceResolver = request.getResourceResolver();
        }

        Iterator iter = null;
        // search for all the nodes and l
        // get all siblings

        //toremove: workaround for adding components from sidekick into panel ?
        if (cmpName == null) {
            Resource parentRes = elementResource.getParent();
                iter = elementResource.getParent().listChildren();



        } else {
            iter = elementResource.listChildren();
        }


        while (iter.hasNext() && layoutnode == null) {
            final Resource current = iter.next();
            if (checkName(current, LAYOUT_NODENAME)) {
                layoutnode = current;
            }
        }
        // no layout node, create one!
        if (layoutnode == null) {
            if (resourceResolver == null) {
                return null;
            }
            Map map = new HashMap();
            try {
                String resourceType = (String) layoutmap.get(cmpName);
                map.put(LAYOUT_PATH_PROPERTY, resourceType);
                final String nodeName = LAYOUT_NODENAME;
                resourceResolver.create(elementResource, nodeName, map);
            } catch (PersistenceException re) {
                log.error("Unable to create missing layout element for guide node " + elementResource, re);
                throw new GuideException(re);
            } finally {
                if (resourceResolver.hasChanges()) {
                    resourceResolver.refresh();
                }
            }
            return map;
        } else {
            return ResourceUtil.getValueMap(layoutnode);
        }

    }

    public static void setGuideName(final Resource rsrc, String name) throws GuideException{
        Session currentSession = rsrc.getResourceResolver().adaptTo(Session.class);
        try {
            if(currentSession.hasPermission(rsrc.getPath(),"set_property")) {
                ModifiableValueMap modifiableMap = rsrc.adaptTo(ModifiableValueMap.class);
                modifiableMap.put(ELEMENT_PROPERTY_NAME,name);
                try {
                    rsrc.getResourceResolver().commit();
                } catch (PersistenceException e) {
                    log.error("unable to set name property on "+rsrc.getPath(), e);
                    throw new GuideException(e);
                }
            } else {
                //TODO: log this error in edit mode only.
                log.error("user has no write permission on the node " +rsrc.getPath()+". Adaptive Form will not work properly");
            }
        } catch (RepositoryException e) {
            log.error("unable to check the permissions for setting property on " + rsrc.getPath(), e);
            throw new GuideException(e);
        }
    }

    /**
     * Return the parameter name for the field
     *
     * @param rsrc The resource
     * @return The parameter name.
     */
    public static String getGuideName(final Resource rsrc) throws GuideException{
        final ValueMap properties = ResourceUtil.getValueMap(rsrc);
        String name = properties.get(ELEMENT_PROPERTY_NAME, "");
        if(name == null || name.isEmpty()) {
            // Adding TS solves this use casr
//            a.       Drag an element on a panel
//            b.      Now create a child panel
//            c.       Now drag the same element in the child panel
//
//            Now because the element dropped having same name are not
//            sibling different names will not be generated.
// So if I try to submit the same, I will loose the data of the first field.

            String resourceName = GuideUtils.removePrefix(rsrc.getName(),"guide") +
                    Calendar.getInstance().getTimeInMillis();
            setGuideName(rsrc,resourceName);
            name = rsrc.getName();
        }
        return name;
    }

    /**
     * Returns the description of the layout resource
     *
     * @param rsrc The resource
     * @return The parameter name.
     */
    public static String getLayoutDescription(final Resource rsrc) {
        final ValueMap properties = ResourceUtil.getValueMap(rsrc);
        String desc = properties.get(ELEMENT_PROPERTY_DESCRIPTION, "");
        if (desc.length() == 0) {
            // fallback to title property of layout node if description not found
            desc = properties.get(ELEMENT_PROPERTY_TITLE, "");
        }
        return desc;
    }

    /**
     * Returns qtip of the layout resource
     *
     * @param rsrc
     * @return layout qtip
     */
    public static String getLayoutQtip(final Resource rsrc) {
        final ValueMap properties = ResourceUtil.getValueMap(rsrc);
        String qtip = properties.get(ELEMENT_PROPERTY_QTIP, "");
        return qtip;
    }

    /**
     * Returns guideNavigatorTab property of the layout resource
     * @param rsrc - the layout resource
     * @return  guideNavigatorTab property
     */
    public static String getGuideNavigatorTabProperty(final Resource rsrc) {
        final ValueMap properties = ResourceUtil.getValueMap(rsrc);
        String guideNavigatorTab = properties.get(GuideConstants.LAYOUT_TAB_TYPE_PROPERTY,"");
        return guideNavigatorTab;
    }

    /**
     * This API generates ID prefix for fragments.
     * Suppose we have a structure   GC-> rootPanel -> PanelA
     *                                               -> PanelB
     *                                                        -> FragmentA
     *                                                                   -> ChildItem1
     *                                                                   -> ChildItem2
     *                                                                   -> FragmentB (Instance1)
     *                                                      -> FragmentB (Instance2)
     *  Id of frag a should be gc-rootPanel-PanelB-FragmentA so prefix ID for FragmentA should be   gC-rootPanel-PanelB
     *  and prefix for FragmentB (Instance1) should be gc-rootPanel-PanelB-FragmentA and prefix for Instance2 would be
     *   gc-rootPanel-PanelB.
     * This logic is implemented by generating ID by path and removing name to get prefix
     *
     *
     * @param fragRefNode
     */
    public  static String getFragPrefixString(Resource fragRefNode, String previousPrefixId) {
        String fragPrefix = null;
        try {
            // user information from previously create prefix id
            // so that we can get the full hierarchy in case of nested fragments
            String pathAfterRootPanel = StringUtils.substringAfter(fragRefNode.getPath(), "/" + GuideConstants.ROOTPANEL_NODENAME +
            GuideConstants.ITEMS_NODENAME_PATH_SUBSTRING);
            if(StringUtils.isNotEmpty(previousPrefixId)) {
                fragPrefix = previousPrefixId  + pathAfterRootPanel +"/";
            } else {
                fragPrefix = generateIdForGuideNodes(fragRefNode.getPath(),fragRefNode) ;
                fragPrefix = fragPrefix + GuideConstants.ITEMS_NODENAME_PATH_SUBSTRING;
            }
        } catch (Exception e) {
            log.error("Error while getting fragRef prefix string", e);
        }
        return fragPrefix;
    }

    /**
     * NOTE: If the ID generation logic is changed here, it has to be changed in AuthorUtils as well
     */

    public static String getGuideNodeHtmlId(Resource elementResource) {
        return getGuideNodeHtmlId(elementResource,null);
    }

    public static String getGuideNodeHtmlId(Resource elementResource, String fragPrefixId) {
        String path = elementResource.getPath();
        if (!path.contains("/" + GUIDECONTAINER_NODENAME)) {
            // we still have componetns that are out of guideCotainer
            // like header is containing guideImage etc
            // that should be whitelisted but not doing it for now.
            log.debug("getGuideNodeHtmlId-path does not have guideContainer:" + path);
            return path; //That should never happen though
        }
        String nodeHtmlId = null,
               fragPrefixidString=null;
        if(fragPrefixId == null) {
            GuideFragmentHolder guideFragmentHolder = null;
            try {
                //TODO: Usually thread local is used to keep things that
                // that is independent of previous state
                // to find a way to just use this logic instead of manipulating
                // the information of thred local
                // this can potentially be a problem if some how someone is not able to reset the set
                // value if thread local
                //Id generation logic has become too fragile because of depending on state of thread local.
                // Somehow you while rendering , something goes bad and you are not able to reset the threadLocal,
                // same value would be used in JsonObjectCreatorImpl
                guideFragmentHolder = GuideContainerThreadLocal.getGuideFragmentHolder();
                if (guideFragmentHolder != null) {
                    fragPrefixidString = guideFragmentHolder.getFragPrefixID();
                }
            } catch (Exception e) {
                log.error("Error while getting thread local from NodeStructureUtils", e);
            }
        } else {
            fragPrefixidString = fragPrefixId;
        }
        if (fragPrefixidString != null && fragPrefixidString.length() > 0) {
            nodeHtmlId = generateIdForFragmentNodes(path, fragPrefixidString);
        } else {
            nodeHtmlId = generateIdForGuideNodes(path, elementResource);
        }
        return manipulateNodeHtmlId(nodeHtmlId);
    }

    private static String getContainerNodeName(Resource elementResource) {

        String containerName = GuideContainerThreadLocal.getGuideContainerName();
        if(containerName!=null) {
            return containerName;
        }
        else {
            log.debug("Guide container name not found in ThreadLocal.");
        }
        if(GuideUtils.isGuideContainerResource(elementResource)) {
            log.debug("Container name retrieved from node structure: " + elementResource.getName());
            return elementResource.getName();
        }
        Resource parent = elementResource.getParent();
        while (parent != null && !(GuideUtils.isGuideContainerResource(parent))) {
            parent = parent.getParent();
        }
        if(parent!=null) {
            String parentName = parent.getName();
            log.debug("Container name retrieved from node structure: " + parentName);
            return parentName;
        }
        return "";
    }


    private static String generateIdForGuideNodes(String path, Resource elementResource) {

        String nodeHtmlId = null;
        String containerName = GUIDECONTAINER_NODENAME;
        //element resource is not passed as Fragment will always be on guideContainer (only main Guide has a copy
        // of the guideContainer)
        if(elementResource != null) {
            containerName = getContainerNodeName(elementResource);
        }
        //if the node is a container node/wrapper the name is sufficient for the HTMLId, otherwise prefix the path to the node from the
        //container node
       if (elementResource != null && GuideUtils.isGuideContainerResource(elementResource)) {
            nodeHtmlId = containerName;
       } else {
            nodeHtmlId = containerName + "-" + StringUtils.substringAfter(path, "/" + containerName + "/");
       }

        return nodeHtmlId;
    }

    private static String generateIdForFragmentNodes(String path, String fragPrefix) {
        return fragPrefix + StringUtils.substringAfter(path, "/" + GUIDE_FRAG_ROOT_PANEL + "/" + GUIDE_ITEMS + "/");

    }

    private static String manipulateNodeHtmlId(String nodeHtmlId) {
        //TODO: [PERFORMANCE] Extensive usage - Replace with compiled regexp (auth and first render)
        return nodeHtmlId.replaceAll(GuideConstants.ITEMS_NODENAME_PATH_SUBSTRING, "-")
                .replaceAll("/", "-")
                .replaceAll("\\.", "-") + GuideConstants.GUIDE_NODE_ID_SUFFIX;
    }

    /**
     * This function assumes that the elementResource contains layout node under it. Will return the map containing properties
     *
     * @return Map containing the properites
     */
    public static Map getLayoutProperties(final Resource elementResource, SlingHttpServletRequest request) throws PersistenceException {
        return getLayoutProperties(request, elementResource, null);
    }
    public static Map getLayoutProperties(final Resource elementResource) throws PersistenceException {
        return getLayoutProperties(null, elementResource, null);
    }

    /**
     * Check if the root panel node exists. If it doesn't creates one and returns the map of properties present in it
     *
     * @return Map containing the properites
     * @todo: Remove CSS property from the node. Also, add resourceResolver
     */
    public static Map getRootPanel(final SlingHttpServletRequest request, final Resource elementResource) throws RepositoryException {

        if (elementResource == null) {
            return null;
        }
        // search for all the nodes and l
        // get all siblings
        final Iterator iter = elementResource.listChildren();
        Resource rootpanel = null;
        ResourceResolver resourceResolver = request.getResourceResolver();

        while (iter.hasNext() && rootpanel == null) {
            final Resource current = iter.next();
            if (checkName(current, ROOTPANEL_NODENAME)) {
                rootpanel = current;
            }
        }

        // no layout node, create one!
        if (rootpanel == null) {

            Map map = new HashMap();
            // todo: Have to remove css from node ?
            String css = ROOTPANEL_NODENAME;
            try {
                String resourceType = RT_GUIDES_ROOTPANEL;
                // Not putting all the properties to map as of now?
                map.put(GuideConstants.SLING_RESOURCE_TYPE, resourceType);
                map.put(GuideConstants.ELEMENT_PROPERTY_NODECLASS, ROOTPANEL_NODECLASS);
                map.put(ELEMENT_PROPERTY_NAME, ROOTPANEL_NODENAME);
                final String nodeName = ROOTPANEL_NODENAME;
                resourceResolver.create(elementResource, nodeName, map);
            } catch (PersistenceException re) {
                log.error("Unable to create missing root panel element for guide node " + elementResource, re);
                throw new GuideException(re);
            } finally {
                if (resourceResolver.hasChanges()) {
                    resourceResolver.refresh();
                }
            }
            return map;
        } else {
            return ResourceUtil.getValueMap(rootpanel);
        }
    }

    /**
     * Check if the item node exists. If it doesn't creates one and returns the list of elements present inside the resource
     *
     * @return list containing all the elements present inside a resource
     * @todo: Check how to work with Exception handling ?
     */
    public static List getItems(final SlingHttpServletRequest request, final Resource elementResource) throws RepositoryException {

        // search for all the nodes and l
        // get all siblings
        Iterator iter = elementResource.listChildren();
        ResourceResolver resourceResolver = request.getResourceResolver();
        Resource items = elementResource;
        ArrayList result = new ArrayList();
        while (iter.hasNext() && items == null) {
            final Resource current = iter.next();
            if (checkName(current, GuideConstants.ITEMS_NODENAME)) {
                items = current;
            }
        }

        // no layout node, create one!
        if (items == null) {
            if (elementResource != null) {
                try {
                    final String nodeName = GuideConstants.ITEMS_NODENAME;
                    resourceResolver.create(elementResource, nodeName, null);
                } catch (PersistenceException re) {
                    log.error("Unable to create missing layout element for guide node " + elementResource, re);
                    throw new GuideException(re);
                } finally {
                    if (resourceResolver.hasChanges()) {
                        resourceResolver.refresh();
                    }
                }
            }
        } else {
            iter = items.listChildren();

            try {
                while (iter.hasNext()) {
                    final Resource current = iter.next();
                    GuideNode newNode = new GuideNode();
                    SimpleBindings bindings = new SimpleBindings();
                    bindings.put("resource",current);
                    bindings.put("request",request);
                    newNode.init(bindings);
                    result.add(newNode);
                }
            } catch (Exception ex) {
                log.error("unable to iterate items for the resource "+elementResource.getPath(), ex);
                throw new GuideException(ex);
            }
        }
        return result;
    }

    /**
     * If override is set, it always set the property
     *
     * @param elementResource
     * @param propName
     * @param propValue
     * @param override
     */
    public static void createProperty(Resource elementResource, String propName, String propValue, Boolean override) {

        // get the node
        final Node node = elementResource.adaptTo(Node.class);
        if (node != null) {
            try {
                // If override is set, please always set the property
                if (override || (!node.hasProperty(propName))) {
                    node.setProperty(propName, propValue);
                }
                node.getSession().save();
            } catch (RepositoryException re) {
                log.error("Unable to set property for the guide node " + elementResource + " propName :" + propName, re);
                throw new GuideException(re);
            } finally {
                try {
                    if (node.getSession().hasPendingChanges()) {
                        node.getSession().refresh(false);
                    }
                } catch (RepositoryException re) {
                    log.trace("Error in create property: ignoring it."+ re.getMessage(), re);
                    // we ignore this
                }
            }
        }
    }


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy