Sunday 20 March 2016

Error Page Handler Using ACS Commons

Goal:

Provide an author-able means for defining, creating and managing custom Error pages per content tree/site.

How to use:

  • Create the proxy overlays for Sling errorhandler scripts (404.jsp and default.jsp) which include the acs-commons counterparts.
  • Create the overlay for 404.jsp
    • /apps/sling/servlet/errorhandler/404.jsp
    • <%@page session="false"%><%%><%@include file="/apps/acs-commons/components/utilities/errorpagehandler/default.jsp" %>
  • In your base page implementation, add the following cq:Widget to the Page Properties dialog
    • <errorpages jcr:primaryType="cq:Widget" path="/apps/acs-commons/components/utilities/errorpagehandler/dialog/errorpages.infinity.json" xtype="cqinclude"/>
  • Create a sling:OsgiConfig node to enable the Error Page Handler
    • /apps/myapp/config/com.adobe.acs.commons.errorpagehandler.impl.ErrorPageHandlerImpl.xml

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
jcr:primaryType="sling:OsgiConfig"
enabled="{Boolean}true"
cache.serve-authenticated="{Boolean}true"
cache.ttl="{Long}300"/>

  • Create a CQ Page that will act as the default Error page, and also contain all custom variations of error pages. Each error page’s “name” (Node name) should correspond to the HTTP Response Status code it should respond to.
    • 500: Internal Server Error
    • 404: Not Found
    • 403: Forbidden
  • Create the above pages under root page of the project structure e.g., /content/projectroot/errors/500
  • Finally, Edit the Page Properties of the site’s root node, and in the new “Error Pages” dialog input
More Links:

Friday 18 March 2016

Getting values to selection dropdown using servlet and listener


Goal:

To get the values of the page into dropdown which is selected in the pathfield.

Dialog Structure:




Create a dialog with one path field and selection field as shown in the image and listeners to the path field which is used to call the servlet and get the page values.

Listener:

Name of the listener is dialogclose :

function(){ 
var dialog = this.findParentByType('dialog');
var selectBox = dialog.findByType('selection')[0];
        $.getJSON('/libs/dropdown?Path=' + this.value, 
            function(jsonData){
selectBox.setOptions(jsonData);
                        });
 }

The above listerner is triggered when the pathfield is selected and when the browse dialog is closed.


Servlet:

package com.geometrixx.sightly.servlets;

import java.io.IOException;

import javax.jcr.Node;
import javax.servlet.ServletException;

import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.apache.sling.commons.json.JSONArray;
import org.apache.sling.commons.json.JSONException;
import org.apache.sling.commons.json.JSONObject;

import com.day.cq.wcm.api.Page;


@Component(immediate = true, description="DropDown Servlet")
@Service(value = javax.servlet.Servlet.class)
@Properties(value={
@Property(name="sling.servlet.extensions",value={"html","json"}),
@Property(name="sling.servlet.methods",value={"GET","POST"}),
@Property(name="sling.servlet.paths",value={"/libs/dropdown"})
})
public class DropDown extends SlingAllMethodsServlet{

/**
*/
private static final long serialVersionUID = 1L;
protected void doGet(final SlingHttpServletRequest req,
            final SlingHttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }
protected void doPost(final SlingHttpServletRequest req,
            final SlingHttpServletResponse resp) throws ServletException, IOException {
JSONArray jsonArray = new JSONArray();
JSONObject jsonObject = new JSONObject();
try {
String pagePath = req.getParameter("Path");
Page selectedPage = req.getResourceResolver().getResource(pagePath).adaptTo(Page.class);
String pageTitle = selectedPage.getPageTitle();
String pageName = selectedPage.getName();
String pageNavigationTitle = selectedPage.getNavigationTitle();
jsonObject.put("text", "PageTitle");
jsonObject.put("value", pageTitle);
jsonArray.put(jsonObject);
jsonObject = new JSONObject();
jsonObject.put("text", "PageName");
jsonObject.put("value", pageName);
jsonArray.put(jsonObject);
jsonObject = new JSONObject();
jsonObject.put("text", "NavigationTitle");
jsonObject.put("value", pageNavigationTitle);
jsonArray.put(jsonObject);
resp.getWriter().println(jsonArray.toString());
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

Above servlet is used to generate the JSON response to get the options into dropdown as TEXT and VALUE pair.




Monday 7 March 2016

AEM Important Links

How to find the runmodes of a AEM instance

In your start-script of your CQ5 instance you can extend the runmodes of your CQ5 instance like this:

::* runmode(s)
set CQ_RUNMODE=author,localhost,europe

What I never could find is the option to check whether cq really picked up this change.

Well here is how to do it:

Go to your Felix console (/system/console).
Click on “Configuration Status” (/system/config)

Use these arrows to navigate through all the tabs, until you see ‘Sling Settings’.

In this tab the runmodes are displayed.

In CQ 5.6.1 there is an easier way to find the runmodes, just go to this url:

http://server:port/system/console/status-slingsettings

Thursday 3 March 2016

Customize Left Navigation for Projects.html

Goal:

Add one more menu for the http://localhost:4502/projects.html



New in AEM(CQ) 6.1 is Sling Resource MergerWith resource merger you overlay the path in /apps and add only new nav items (in this case, Users)

1) Create the following folders in CRXDE Lite (http://localhost:4502/crx/de) or use the overlay creator servlet discussed here

            /apps/cq - nt:folder
            /apps/cq/core - nt:folder
            /apps/cq/core/content - sling:OrderedFolder
            /apps/cq/core/content/nav - nt:unstructured

  The structure above is an overlay of /libs/cq/core/content/nav

2) Create node /apps/cq/core/content/nav/onemkhomepage of type nt:unstructured with the following properties

             id - onemk-homepage-link
             href - /content/geometrixx-sightly/English.html
             jcr:description - Open Home Page
             jcr:title - OneMk HomePage

     All properties are self-explanatory. Property id is to uniquely identify the nav item.




Tuesday 1 March 2016

How to Configure Stemming for AEM search

Stemming:


Decide the stemming strategy based on your use case. For mor info on strategies, please see this article.

It is highly recommend you use Porter Stemming because of its simplicity and flexibility. It usually is sufficient to yield the majority of stemming based search results.

Porter is a transforming algorithm for the English language that reduces any of the forms of a word such as "walks, walking, walked" or "teach, teaching" to their elemental roots "walk" and "teach".

Porter is based on rules and does not need a dictionary.

Go to the /oak:index/ntHierarchyNode/analyzers/default/filters path in the repository.

Create a node with these properties:
  • Name: PorterStem
  • Type: nt:unstructured
Finally, test stemming functionality by:

Typing the keyword "teach". You will get results for the term "teaching" as well.

For more info refer :



CQ User Creation Using UserManager API

Introduction:

Users:

Users will log in to AEM with their account. Each user account is unique and holds the basic account details, together with the privileges assigned.
Users are often members of Groups, which simplify the allocation of these permissions and/or privileges.

Groups:

Groups are collections of users and/or other groups; these are all called Members of a group.
Their primary purpose is to simplify the maintenance process by reducing the number of entities to be updated, as a change made to a group is applied to all members of the group. Groups often reflect:
  • a role within the application; such as someone who is allowed to surf the content, or someone who is allowed to contribute content.
  • your own organization; you may want to extend the roles to differentiate between contributors from different departments when they are restricted to different branches in the content tree.
Therefore groups tend to remain stable, whereas users come and go more frequently.
With planning and a clean structure, the use of groups can reflect your structure, giving you a clear overview and an efficient mechanism for updates.


User Creation Servlet:

Below Servlet creates user under specified paths and add them to specified group when you hit the servlet as 

http://localhost:4502/libs/cqusercreation?email=mani@gmail.com&firstName=firstName&lastName=lastName&givenName=givenName&displayName=displayName

with different parameters you can use these servlet in any async call to make a call from the component.

Servlet

package com.geometrixx.sightly.servlets;

import java.io.IOException;
import java.security.Principal;
import java.util.HashMap;
import java.util.Map;

import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.servlet.ServletException;

import org.apache.commons.lang.StringUtils;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.apache.jackrabbit.api.JackrabbitSession;
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.value.StringValue;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;



@Component(immediate = true, description="User Creation")
@Service(value = javax.servlet.Servlet.class)
@Properties(value={
@Property(name="sling.servlet.extensions",value={"html","json"}),
@Property(name="sling.servlet.methods",value={"GET","POST"}),
@Property(name="sling.servlet.paths",value={"/libs/cqusercreation"})
})
public class CQUserCreationServlet extends SlingAllMethodsServlet {
private static final Logger Log = LoggerFactory
.getLogger(CQUserCreationServlet.class);
protected void doGet(final SlingHttpServletRequest req,
            final SlingHttpServletResponse resp) throws ServletException, IOException {
Log.debug("doGet");
        doPost(req, resp);
    }
protected void doPost(final SlingHttpServletRequest req,
            final SlingHttpServletResponse resp) throws ServletException, IOException {
Log.debug("doPost");
createNewCqUser(req,resp);
}
public void createNewCqUser(SlingHttpServletRequest req,
SlingHttpServletResponse resp) {
// TODO Auto-generated method stub
Log.debug("createNewCqUser");
Map<String, String> userProperties = new HashMap<String, String>();
String userEmailId = req.getParameter("email");
final String loginId= userEmailId;
String firstName = req.getParameter("firstName");
String lastName = req.getParameter("lastName");
String givenName = req.getParameter("givenName");
String displayName = req.getParameter("displayName");
userProperties.put("profile/email", userEmailId);
userProperties.put("profile/firstName", firstName);
userProperties.put("profile/lastName", lastName);
userProperties.put("profile/givenName", givenName);
userProperties.put("profile/displayName", displayName);
Log.debug("Map {}",userProperties );
Session session = req.getResourceResolver().adaptTo(Session.class);
Group userGroup;
Principal principal;
User newUser;
try {
UserManager userManager = ((JackrabbitSession) session)
.getUserManager();
Log.debug("userManager {}",userManager );
userGroup = (Group) userManager
.getAuthorizableByPath("/home/groups/e/everyone");
principal = new Principal() {
public String getName() {
return loginId;
}
};
newUser = userManager.createUser(loginId, "123456", principal,
"/home/users/geometrixx");
Log.debug("newUser {}",newUser );
if (null != newUser) {
updateAndInsertUserDetails(newUser, userProperties);
userGroup.addMember(newUser);
}
session.save();
}catch(Exception e){
}
}
public void updateAndInsertUserDetails(User user, Map<String, String> map)
throws RepositoryException {
for (Map.Entry<String, String> entry : map.entrySet()) {
setUserStringProperty(user, entry.getKey(), entry.getValue());
}
}
public void setUserStringProperty(User user, String property, String value) {
try {
if (isNotBlank(value)) {
user.setProperty(property, new StringValue(value));
}
} catch (RepositoryException e) {
}
}
public boolean isNotBlank(String value) {
return StringUtils.isNotBlank(value);
}
}


UserManager API Link: