
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