While working on upgrading a site from Adobe CQ 5.5 to Adobe Experience Manager (AEM) 5.6, we found a new button (Target) that when clicked, seemed to break every component we had. AEM 5.6 introduced the target button to the toolbar of every component. This button is designed to target the content in order to give a user-specific experience that can be tied into Adobe Test&Target. You can then manage your experiences through the Marketing Campaign Manager (MCM) to make your content adaptable and dynamic.
After much investigation, we determined that the bug is encountered when a component is directly included on a page and the target button is clicked. We will go through the problem, cause, and a couple of solutions in the following post.
This is what the target button looks like:
Below is the node structure and properties for bannercarousel (sling:resourceType = brianfanclub/components/bannercarousel.) The component is on our homepage and contains three bannerimages.
<%@include file=”/apps/brianfanclub/global.jsp”%><%
%><section id=”promos”>
<cq:include path=”bannercarousel” resourceType=”brianfanclub/components/bannercarousel” />
<div style=”clear: both;”></div>
</section>
Initially, the bannercarousel is configured to have three bannerimages through a pretty simple dialog. Each bannerimage has its own dialogue that controls the image and associated options.
If we hit the target button, the page refreshes and it gives us a blank component with a blank dialogue.
In the node structure, there is a new node called “default” that inserts itself between the bannercarousel and the bannerimages. The bannercarousel node is now sling:resourceType = cq/personalization/components/target.
This is the default node’s properties. It has the content we expect to be in bannercarousel. Let’s hit the target button again to make sure:
We get another default node nested below bannercarousel.
The problem comes from cq:include:
<%@include file=”/apps/brianfanclub/global.jsp”%><%
%><section id=”promos”>
<cq:include path=”bannercarousel” resourceType=”brianfanclub/components/bannercarousel” />
<div style=”clear: both;”></div>
</section>
path=”bannercarousel” no longer has its resourceType=”brianfanclub/components/bannercarousel”—it has become cq/personalization/components/target. The pressing target wraps the component in a mbox that has its own properties but takes the name of the contained component. It renames the targeted component as “default” inside of the mbox. cq:include cannot find a bannercarousel, so it gives you a blank component as if you meant to add the component to your page but have yet to set it up.
We got around this mix-up by creating a custom tag library with an “axis:include” tag. Check out this link for guidance to create your own tag library: http://docs.oracle.com/cd/E11035_01/wls100/taglib/quickstart.html.
This is our custom include tag code:
package com.axis41.taglibs;
import com.day.cq.wcm.tags.IncludeTag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.servlet.jsp.JspException;
/**
* An extension of the cq:include tag.
*
* This will check the resourceType of the resource and resolve before including it
*/
public class Include extends IncludeTag {
private static final Logger logger = LoggerFactory.getLogger(Include.class);
private String path;
private String resourceType;
@Override
public int doEndTag() throws JspException {
// let base class handle missing attribute(s)
if ((this.path == null) || (this.resourceType == null)) {
return super.doEndTag();
}
Node parentNode = (Node) pageContext.findAttribute("currentNode");
Node resourceNode = null;
try {
resourceNode = parentNode.getNode(this.path);
} catch (RepositoryException e) {
e.printStackTrace();
}
// if resource node does not exist, let the base class create it
if(resourceNode == null) {
return super.doEndTag();
}
// Get the node's resource type and set that as the resourceType member variable.
// The resource type in the include tag may not match the node’s,
// e.g. when resource is targeted, the resourceType will become
// cq/personalization/components/target
try {
if(resourceNode.hasProperty("sling:resourceType")) {
String type = resourceNode.getProperty("sling:resourceType").getString();
setResourceType(type);
}
} catch (RepositoryException e) {
e.printStackTrace();
}
// now that we have resolved the resource type, let the base class do the rest
return super.doEndTag();
}
@Override
public void setPath(String path) {
this.path = path;
super.setPath(path);
}
@Override
public void setResourceType(String resourceType) {
this.resourceType = resourceType;
super.setResourceType(resourceType);
}
}
<%@include file=”/apps/brianfanclub/global.jsp”%><%
%><section id=”promos”>
<axis:include path=”bannercarousel”
resourceType=”brianfanclub/components/bannercarousel” />
<div style=”clear: both;”></div>
</section>
Our tag handles three cases:
<div style=”clear: both;”></div>
</section>
Our tag handles three cases:
- If there is no path or type given, we call the normal cq:include. This will put a blank, fresh component on the page.
- Next, we check the page’s nodes for our component based on the path. If we do not find the component by path on the page, we call cq:include, which will give a blank, fresh component.
- If we do find our node based on the path, we set the sling:resourceType to whatever we find that the node has and then pass it to cq:include.
Using our tag, target behaves as it should:
The node structure is the same, but axis:include handles the case when the bannercarousel is type target. Hitting the Disable Targeting button will revert the mbox and the component go back to normal.
To manually recover a node buried by mboxes, just go down the node structure until you find the default node with the properties that your component expects. Drag the default node out to where the component should be. Delete the old node with your component’s name; it is just a bunch of nested mboxes now. Rename “default” with the correct component name since it now contains the data that was lost.
To manually recover a node buried by mboxes, just go down the node structure until you find the default node with the properties that your component expects. Drag the default node out to where the component should be. Delete the old node with your component’s name; it is just a bunch of nested mboxes now. Rename “default” with the correct component name since it now contains the data that was lost.
Another, the quick fix is to disable the target button on a component. Add a cq:editConfig node to your component if it does not have one already, then add the property cq:disableTargeting=”{Boolean}true”. You would have to go into every component (whether it is custom or from the foundation) to truly disable targeting throughout your site. Changes in libs can be in danger because your modifications will be overwritten with each AEM update. Creating a new tag is a better option.
Keep in mind that this issue is only encountered when the component is included directly on the page. If it is included through a parsys, you get the correct targeting behavior without any modification. This workaround helps you use AEM’s targeting feature as it was intended, providing custom content for your users. Stay informed on AEM workarounds, subscribe to the Adobe Experience Manager Podcast today.
No comments:
Post a Comment
If you have any doubts or questions, please let us know.