Thursday 12 October 2017

AEM Page Redirect - Sling Model


We usually come across page redirection in AEM , its a common practice to include the redirection logic in the base page component of a site. Providing the code sample required for redirection.

Sling Model for handling page redirection in AEM :

import java.io.IOException;

import javax.annotation.PostConstruct;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.models.annotations.Default;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.ScriptVariable;
import org.apache.sling.models.annotations.injectorspecific.SlingObject;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.adobe.acs.commons.util.ModeUtil;
import com.day.cq.wcm.api.Page;
import com.google.common.net.HttpHeaders;

@Model(adaptables = SlingHttpServletRequest.class)
public class RedirectPageModel {

private static final Logger LOG = LoggerFactory.getLogger(RedirectPageModel.class);

@SlingObject
private SlingHttpServletResponse response;
@SlingObject
private ResourceResolver resourceResolver;
@ScriptVariable
private Page currentPage;
@ValueMapValue(name = "redirectStatus")
@Default(longValues = HttpStatus.SC_MOVED_PERMANENTLY)
private Long slingRedirectStatus;
@ValueMapValue
private String redirectTarget;

@PostConstruct
private void activate() throws IOException {
if (!ModeUtil.isPublish()) {
return;
}
String redirectUrl = getRedirectTarget();
if (StringUtils.isNotBlank(redirectUrl)) {
final int redirectStatus = slingRedirectStatus.intValue();
LOG.trace("Redirecting ({}) to {}", redirectStatus, redirectUrl);
if(redirectStatus==HttpStatus.SC_MOVED_TEMPORARILY){
response.sendRedirect(redirectUrl);
}else {
response.setHeader(HttpHeaders.LOCATION, redirectUrl);
response.setStatus(redirectStatus);
}
}

}

public boolean isRedirectConfigured(){
return StringUtils.isBlank(getRedirectTarget());
}

/**
* Get the "redirectTarget" property from the page content. Check to see its
* not pointing to same page. If its external, return it as is. If its
* internal, remove the html extension and use the resource resolver to map
* it.
*
* @return target path/url
*/
public final String getRedirectTarget() {
if (StringUtils.startsWithIgnoreCase(redirectTarget, "http")) {
return redirectTarget;
}
if (StringUtils.equals(redirectTarget, currentPage.getPath())) {
LOG.warn("{} is trying to redirect to self", currentPage.getPath());
return null;
}
return resourceResolver.map(redirectTarget) + ".html";
}
}

redirection.html file to be included in component html :

<sly data-sly-use.redirectModel="${'com.crazy.cms.slingmodels.RedirectPageModel'}"/>

<h1 data-sly-test="${redirectModel.isRedirectConfigured}" class="cq-redirect-notice">
    This page redirects to <a href="${redirectModel.redirectTarget}">here</a>
</h1>



Wednesday 11 October 2017

AEM System/Service User - Ensure Service User of ACS Commons

In the latest AEM version usage of admin resource resolver is deprecated. We have to use the system/service user to access the data. 

You can manually create System user using aem crx explorer console and create a service mapping to access it in your application. But the process of creating system user and setting up the required permissions should be replicated across all the AEM instances, which is a additional burden and error prone. To overcome this ACS commons has provided a beautiful service (Ensure Service User )which can create the System user and set the permissions automatically based on OSGi configuration. 

The Ensure Service User facilitates defining service users and their ACLs in OSGi configurations, and will intelligently ensure they exist on the target AEM instances.

Below is the example configuration 

















Principal Name : The service user name ,can be just the principal name, a relative path, or the absolute path where the user should be stored in the JCR. Service users will ONLY exist under /home/users/system. If a system user exists with the same principal name at a different location then a new user will not be created, pricncipalName cannot be same.


Operation : add OR remove
  • add ensures the existence of the service user and ACLs
  • remove ensures that the service user and any ACLs are removed
Ensure Immediately : Options: true OR false
When set to true, the user creation and permissions update  are performed whenever this bundle is loaded.

ACE's :  List of ACE definitions(permissions) to be set for the service user

                 Format: type=allow;privileges=jcr:read,rep:write;path=/content/myapp
    • type:(Required) allow OR deny
    • privileges: (Required) comma delimited list of valid JCR privileges
    • path: (Required) absolute content path which the ACE will be applied
More details can be found at ACS Commons Documention




Sunday 22 November 2015

UTF- 8 Encoding in AEM

Set UTF-8 as Default Encoding : 

If we do not provide any charset encoding,  AEM uses " ISO-8859-1" as default encoding since this is mandated by Servlet API. But most of the times we need "UTF-8" encoding to support special characters and symbols in our multi lingual web sites.

Default Encoding can be configured within the OSGi configuration available http://localhost:4502/system/console/configMgr

AEM 6.1 : Go to Apache Sling Request Parameter Handling . Change the Default Parameter Encoding to "UTF-8"





AEM 5.6 : Go to Apache Sling Main Servlet . Change the Default Parameter Encoding to "UTF-8"




Encoding & Decoding while posting data to Sling Servlet :

Decoding  in the Servlet.

String id = java.net.URLDecoder.decode(request.getParameter("id"), "UTF-8");

Encoding data before posting to a  Servlet

String id= java.net.URLEncoder.encode("cust®", "UTF-8"); 

Set charset encoding in a JSP :


<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib uri="/libs/CFC/resources/jstl/c.tld" prefix="c" %>
<form method="post">
<input name="searchterm" value="<c:out value="${param.searchterm}" />" />
<input type="submit" />

</form>

We can even set encoding using response.setCharacterEncoding("UTF-8")




Querying in AEM

What can we query for ..?






In a relational database one can query against table columns. But in a hierarchical content repository what can we query for.. ?

Well a tree structure has nodes and node can properties as well as child nodes as shown in picture.And every node will have an absolute path which determines its location on the tree structure.

We can create conditions with these things and that is nothing but our query.




Query is simply a set of the conditions alias predicates. Predicates available within AEM are listed below


Query Predicates
Example
Node Name

Wild Card : *
Case Sensitive
nodename=myapp
nodename=myapp* // searches for any or no character after myapp
nodename=myapp? // searches for any character  after myapp

Node Type
type=cq:Page
type=nt:unstructured
Property

Wild card : %
Case Sensitive
property=jcr:title
property.value=catalog
property.operation=equals (default if not provided)

property=jcr:title
property.value=cat% 
property.operation=like
Path
path=/content/myapp/products/catalog/en
Full Text
(Performs a word search in the  entire repository, or within a path if provided)

Wild Card: *
Non Case Sensitive
fulltext=catalog

path=/content/myapp/products
fulltext=cat*
fulltext.relPath=jcr:title

Full text searches all the content, if we need to search for a particular property relPath can be used



How do we Query ? 

AEM provides us Query Builder API to write the queries. Query Builder is very easy to use and it is a wrapper around the actual query language like XPATH, SQL2 etc. 

You just require all of our query parameters put to a map and Java Query Builder API does the rest. 

 Map<String,String> map = new HashMap<String,String> ();
 map.put("path", "
/content/myapp/products");
 map.put("type", "nt:file");

 Query query = builder.createQuery(PredicateGroup.create(map), session);
 SearchResult searchResult   =  query.getResult();
 List<Hit> resultHits   =  searchResult .getHits();


Now we can iterate through the result hits and achieve what we want.


You can find a bit more at Query Builder API


Sunday 3 August 2014

Query Builder API

Query Builder API

Query Builder is an API to build queries which use JCR XPath underneath. Query Builder is exposed as
  1. Java API
  2. Rest API
 Java API QueryBuilder is available as an OSGi reference. This API can be used to build the queries using  predicate groups, example shown below.
  Map map = new HashMap();
 map.put("path", "/content");
 map.put("type", "nt:file");
 Query query = builder.createQuery(PredicateGroup.create(map), session);
 SearchResult result = query.getResult();

The REST API provides the same functionality through HTTP with response being rendered in JSON. HTTP Query request should start with the url http://localhost:4502/bin/querybuilder.json with all the search filters being provided as URL query parameters. Check out the examples below 

The search parameters provided above are path to search for and type of resource required .We have lot of parameters defined, some of them shown below
type = cq:Page         //  Type of resource
nodename = *.jar    //    Node name/pattern
orderby = @jcr:content/jcr:lastModified    // Order by property defined ( here its based on modified date)
orderby.sor t= desc  // ordering type

To search based on any property we can use 
property = jcr:content/cq:template    // Property name
property.value =apps/myapp/templates/mytemplate  //  Value of the property
To search multiple properties at a time
1.property = jcr:content/cq:template
1.property.value = apps/myapp/templates/mytemplate
2.property = jcr:content/jcr:title
2.property.value = MyTitle

To search for multi valued property - A property can have more than one value  (Array of values)
property = jcr:content/jcr:title
property.1_value = MyTitle1
property.2_value = MyTitle2
property.3_value = MyTitle3

Restricting the result (JSON Response)

By default QueryBuilder returns only default set of properties, If you want to have all the properties in your result use 
p.hits = full    

Most of the times we may be interested in only some selective properties, in that case we can specify the properties required separated by space 
p.hits = selective
p.properties = jcr:title sling:resourceType

By default we will only the first 10 hits, to get the entire search you can use
p.limit = -1
p.limit = 50 // only 50 hits will be retrieved

Query Debugger 

AEM provides Query Debugger which can be used to test, create and check the response time and results retrieved 

The debugger can be found at http://localhost:4502/libs/cq/search/content/querydebug.html
In the box provided you can put down all the query parameters as shown






Once you run this search query the result will be displayed as 







Result will have only 10 hits by default, its displays the time took for the search and the respective XPath query





Sunday 15 June 2014

JSON.jsp

Before writing a JSON.jsp to get custom json data ,lets see how it works.

Sling gives importance to the resource rather than the type of resource.

The above mentioned statement explains what Sling is.Sling gives us the opportunity to render the same data in different types like html,xml and json etc.

Lets go through response from the below requests.

http://localhost:4502/content/geometrixx/en.html

http://localhost:4502/content/geometrixx/en.json

http://localhost:4502/content/geometrixx/en.xml

All the above three urls requests the data at path "/content/geometrixx/en" , where en is node of type page (cq:Page) . But the response what we get differs based on the extension .

More about sling request processing can be found at
http://dev.day.com/docs/en/cq/current/developing/the_basics.html#Sling Request Processing

Now we have a page and we want to customize the json response on the page we need a "json.jsp" on that page  component.

Create a new file by name "json.jsp" for the geometrixx page at path "/apps/geometrixx/components/homepage"and write the code paste the code given below.

<%@ page import="org.apache.sling.commons.json.io.*" %>
<%@include file="/libs/foundation/global.jsp" %>
<% 
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");

JSONWriter writer = new JSONWriter(response.getWriter());
writer.object();
writer.key("Name");
writer.value("English");
writer.key("Code");
writer.value("en");
writer.endObject();
%>

Now we can see our custom JSON response at
http://localhost:4502/content/geometrixx/en/jcr:content.json









Sunday 18 May 2014

CQ Defined objects


CQ and Sling has provided some custom tag libraries which could reduce the development effort. <cq:definedObjects/>, <sling:definedObjects/>  has some regularly used scripting objects.

If "/libs/foundation/global.jsp" is included in the script, these tag libraries will get included by default

Objects defined by Sling:

slingRequest – SlingHttpServletRequest  Object
  <%ResourceResolver rr=slingRequest.getResourceResolver();%>
But resource resolver is already available as a defined object explained below

slingResponse – SlingHttpServletResponse Object

request - HttpServletRequest Object

response -  HttpServletResponse Object

resourceResolver - Current requests ResourceResolver Object                  <%=resourceResolver.getResource("/apps/blog/components/checkboxtest").getName()%>

sling - SlingScriptHelper Object which provides methods for scripts.Most commonly used to fetch an OSGI
           service reference
            <% QueryBuilder queryBuilder=sling.getService(QueryBuilder.class); %>

resource - The current resource same as slingRequest.getResource();
 <%=resource.getResourceType()%>

currentNode - Current resource node Object
      <%=currentNode.getName()%>
log - SLF4J logger
<%log.info("Logging test");%>

Objects defined by CQ:

componentContext - The current component context Object
                                 <%=componentContext.getComponent()%>

component - The current component Object
                     <%=component.getComponentGroup()%>

currentPage - Current page object
                       <%=currentPage.getPath()%>

pageManager - Page manager object useful for performing operations on page
                         <%=pageManager.getPage("page path")%>

pageProperties - Page properties that can be used directly rather than reading them from page again
                           
properties - properties of the current resource
                    <%STring arr[]=properties.get("myProperty",String[].class);%>


More details on the tag libraries available at
http://wem.help.adobe.com/enterprise/en_US/10-0/wem/howto/taglib.html



Friday 16 May 2014

Checkbox component

Creating a Selection (checkbox) component in Adobe CQ

Section below describes step by step procedure for creating checkbox component in Adobe CQ

1.Right click on the folder where you want to create the component and select "Create Component"

2.Enter the details for Label ,Title and Group ,make sure you enter different text for each.
       E.g  Label :checkbox ,Title:My checkbox ,Group: MyComponents
 

      The component will get created along with the jsp associated with it.



3.We need a dialog ,which will enable us to interact with the user.
For that right click on the component and select "Create Dialog",now
your component looks like something as shown .

 


4.Right click on the node "tab1" (which is be created as a part of dialog)
  and create a node by name "items" and type as "cq:WidgetCollection".

5.Right click on "items" and create node by name "checkbox" and type as      "cq:Widget"

6.Add properties to checkbox node

name: "xtype"  value: "selection"
name: "type"    value: checkbox"
name: "name" value: "./checboxtest"


7. Right click on node "checkbox" and create a node    by name"options" and type"cq:WidgetCollection".

8.Right click on "options" and create node "option1" , add properties to the node "text" and "value" with       the data required,create required number of options(option2,option3...) Our component is created.

9.We can drag this component in the page.But by default our component will not be displayed in the side        kick.To make the component visible in side kick ,select the design mode from side kick .Now click on the edit button available on the "parsys" component of the page and enable the component or component group (Group: MyComponents) by selecting the checkbox.
Now we can drag the component on to the page.

You can display the selected value by adding the line "<%=properties.get("checkboxtext",String[].class)%>" to the component jsp

You can download the code package here checkboxtest-1.0