Tuesday 17 December 2013

How to set up Run modes in AEM

Recommendation

Run modes allow you to tune your CQ instance for a specific purpose; for example author, publish or development. This is done by defining collections of configuration parameters for each run mode. A basic set is applied for all run modes, additional sets are each tuned to the purpose of your specific environment.

All configuration settings are stored in the one repository and activated by setting the Run Mode.

Standard run modes are:

  • author
  • publish

You can also create environment specific run mode such as,

  • author, development
  • publish, test
  • author, intranet, us or as required...

There are two mechanisms for setting standard and environment specific run mode for your instance:

  • To set up standard run mode Use the naming convention:
   cq-<standard-run-mode>-<port-number>

For example, set a standard run mode by naming the jar file cq-author-4502 or cq-publish-4503


  • To set up environment specific run mode there are two methods,

Method 1:

Through <cq-installation-dir>/crx-quickstart/config/sling.properties Add the following properties (following example is for author, prod, marketing):

   sling.jcrinstall.folder.name.regexp=.*/(install|config)?
sling.run.modes=author,prod,marketing

In above case config.author.prod.marketing will get picked up (Or whatever with maximum match)

Method 2:

Through <cq-installation-dir>/crx-quickstart/config/sling.properties and system property (In start script):

 sling.jcrinstall.folder.name.regexp=.*/(install|config)?      #<------ In sling.properties file
-Dsling.run.modes=publish,prod,marketing #<----- In start script or system property

In above case config.publish.prod.marketing will get picked up (Or whatever with maximum match)

Configuration values for the run modes are saved in the repository. You can store all configurations in one repository as the run mode is indicated by a suffix on the folder name; for example:

 config, applicable for all run modes
config.author, used in author run mode
config.publish, used in publish run mode
config.<standard-run-mode>.<env-specific-mode>, used in the applicable run mode

Java Content Assist/IntelliSense in CRXDE is not working for CQ 5.5

Below are the series of steps:

1) Stop CRXDE if it is running
2) Upload crxde-missing-libs-1.0.zip into our CQ 5.5 instance through package manager - You can download it from Packgae Share
3) Refresh /etc/crxde folder in CRXDE Lite
4) Start CRXDE - Contextual help should start working here
5) If it still doesn’t work then Stop CRXDE, delete .crxde folder from your user home directory and restart CRXDE. It should work after this action.

Query Builder in Brief


  • QueryBuilder is an API used to build Queries for Query Engine.
  • The generated queries are compatible with HTML forms also.
  • Allows us to add and remove conditions
  • Allows us to copy and paste of Queries
  •  Easily extensible
  • This QueryBuilder resides at server side and accepts a query description, creates and runs an XPath query.
  • The Query Description is simply a set of predicates.
  • Predicates are nothing but conditions. For each predicate type there is an evaluator component PredicateEvaluator that knows how to handle that specific predicate for XPath, filtering and facet extraction.

Standard Predicates:

  •  path
  •  property
  • type
  • fulltext
  •  range
  •  daterange
  • nodename
  •  similar
  • tagid & tag
  • language
  • event
  •  Also we can have the following predicates: 
    • group of predicates
    • like brackets 
    • order by predicates

Example usages

 From HTTP Form Post Request:
 Session session = request.getResourceResolver().adaptTo(Session.class);
 Query query = queryBuilder.createQuery(PredicateGroup.create(request.getParameterMap()), session);
 SearchResult result = query.getResult();
 ...
 
From key/value map:
 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();
 ...
 
From predicates:
 PredicateGroup group = new PredicateGroup();
 group.add(new Predicate("mypath", "path").set("path", "/content"));
 group.add(new Predicate("mytype", "type").set("type", "nt:file"));
 Query query = builder.createQuery(group, session);
 SearchResult result = query.getResult();
 ...
 
QueryBuilder Debugger:
You can test and debug the queries at this URL



//Sample Application that retrieves the images from a DAM

<%@include file="/libs/foundation/global.jsp"%><%
%><%@page session="false"%>
<%@page import="com.day.cq.tagging.*, com.day.cq.wcm.api.*" %>

<%@page
import ="java.util.*,
javax.jcr.*,
org.apache.sling.api.resource.*,
org.apache.sling.api.scripting.*,
org.apache.sling.jcr.api.*,
com.day.cq.search.*,
com.day.cq.dam.api.Asset,
com.day.cq.search.result.*"
%>

<%        SlingRepository slingRep=sling.getService (SlingRepository.class);
            Session session=slingRep.loginAdministrative (null);
            //Instantiation of Map Object useful for storing the Query Description
Map<String, String> map=new HashMap<String, String>();
QueryBuilder queryBuilder;
            //Writing Query Description that goes to form a Query
map.put ("type", "dam:Asset");
map.put ("property", "jcr:content/metadata/dc:format");
map.put ("property.value", "image/jpeg");
            //resource resolving
queryBuilder=resource.getResourceResolver ().adaptTo(QueryBuilder.class);
            //creating query based on the Query Description
Query query=queryBuilder.createQuery (PredicateGroup.create(map),session);
            //Getting and storing the Results
SearchResult searchRes=query.getResult ();
String assPath=null;
            //Iterating for the no of times the requested resource found
for (Hit hit:searchRes.getHits()){
                       //Storing the path where the resource found
           String path1=hit.getPath ();
                       //Getting the resource from the path found
           Resource rs=resourceResolver.getResource (path1);
                       //Storing the resource into Asset object
          Asset asset=rs.adaptTo (Asset.class);
                      //Getting the path from the Asset object where the fetched data stored
          assPath=asset.getPath();
                      //finally rendering the asset data on the Page
             %><img src="<%=asset.getRendition ("cq5dam.thumbnail.140.100.png").getPath() %>" ></img>
<%
}
%>

Monday 16 December 2013

Script

 

Scripts:

JSP Scripts or Servlets are usually used to render components.

According to the request processing rules of Sling the name for the default script is <componentname>.jsp.

global.jsp

The JSP script file global.jsp is used to provide quick access to specific objects (i.e. to access content) to any JSP script file used to render a component.

Therefore global.jsp should be included in every component rendering JSP script where one or more of the objects provided in global.jsp are used.

Note:

The path /libs/wcm/global.jsp, which was used by earlier versions of CQ, is now obsolete. In CQ 5.3, the correct global.jsp path changed to /libs/foundation/global.jsp

More details: http://dev.day.com/docs/en/cq/current/developing/scripts.print.html

cq:defineObject

 

The Day provided global.jsp declares the Sling, CQ and JSTL taglibs and exposes the regularly used scripting objects defined by the
<cq:defineObjects /> tag
The above tag exposes the following regularly used, scripting objects which can be referenced by the developer. It also exposes the objects defined by the <sling:defineObjects> tag.

  1. componentContext

        Package:  com.day.cq.wcm.api.components

public  interface ComponentContext

          The current component context object of the request.

   2. component

        Package: com.day.cq.wcm.api.components.Component    interface

        The current cQ5 component object of the current resource

   3. currentDesign

        Package: com.day.cq.wcm.api.designer.Design   interface

        The current design object of the current page

   4. currentPage

        Package: com.day.cq.wcm.api.Page  interface

        The current CQ WCM page object.

GoogleMap Component

 

        In order to add a Google map to your site, you need to get a Google Maps API key for that site. This allows you to embed the maps into your web pages, and provides you with the terms and services for using the API key.
Steps:
1. Create a component called 'GoogleMap'
    a. Create a dialog which consists address, latitude and longitude fields.
    b. GoogleMap.jsp
    GoogleMap.Jsp  LOGIC:
    1. Read Address or location from user through dialog.
    2. Get the latitude and longitude values based on location
    3. Output the map on page.
    GoogleMap.jsp code:
============================
<%--
Google Map component.
NA
--%><%
%><%@include file="/libs/foundation/global.jsp"%><%
%><%@page session="false" %>
<%
String searchLoc= properties.get("address","");    
%>
<!DOCTYPE html>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript">
function initialize() {
var geocoder = new google.maps.Geocoder();
var address = '<%=searchLoc %>' ;
alert(address);
geocoder.geocode( { 'address': address}, function(results, status)
{
alert("--------------");
if (status == google.maps.GeocoderStatus.OK)
{
var latitude = results[0].geometry.location.lat();
var longitude = results[0].geometry.location.lng();
alert(latitude);
alert(longitude);
//document.write(latitude);
var latlng = new google.maps.LatLng(latitude,longitude);
var myOptions = {
zoom: 18,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
var myMarker = new google.maps.Marker({ position: latlng, map: map, title:"About.com Headquarters" });
}
});
return true;

</script>
<div>Google Map</div>
<div id="map_canvas" style="width:300px; height:300px;"></div>
<script type="text/javascript">
initialize();      /// call Java script function automatically...
</script>
============================
References: 
http://code.google.com/apis/maps/documentation/javascript/tutorial.html
http://webdesign.about.com/od/javascript/ss/add-google-maps-to-a-web-page.htm
http://briancray.com/2009/04/01/how-to-calculate-the-distance-between-two-addresses-with-javascript-and-google-maps-api/

Dialog & Design_Dialog

 

dialog:
The Dialog is a special kind of window with a form in the body and a button group in the footer. It is typically used to edit content, but can also just display information.
design_dialog:


    design_dialog (nt:unstructured) -specifies the design editing options for this component.
=============================================
1. How to retrieve values read from dialog (widget) to jsp?
Sol: String var= properties.get("<name of widget>","");
2. How to retrieve values from design_dialog to jsp?
Sol: String var= currentStyle.get("<name of widget>","");
==============================================

Installing FileVault (VLT)

 

Problem: I've create a Bundle and wrote a java bean(Example.java). When I tried to Generate Getter and Setter methods I got error as follows:
Error Message: The resource is not on the build path of a java project

Solution: Installing FileValut (VLT)

FileVault (VLT) is a tool developed by Day that maps the content of a CRX instance to your file system. The VLT tool has similar functionalities to those of an SVN client, providing normal check in, check out and management operations, as well as configuration options for flexible representation of the project content.

To install VLT, follow the steps:

  1. In your file system, go to <cq-installation-dir>/crx-quickstart/opt/filevault. The build is available in both tgz and zip formats.

  2. Extract the archive.

  3. Add <cq-installation-dir>/crx-quickstart/opt/filevault/vault-cli-<version>/bin to your environment PATH so that the command files vlt or vlt.bat are accessed as appropriate. For example, <cq-installation-dir>/crx-quickstart/opt/filevault/vault-cli-1.1.2/bin

  4. Open a command line shell and execute vlt --help. Make sure it displays the following help screen:

Customize Sidekick

Version: CQ 5.4

      Have a requirement where we need to add 'Preview Publish Page' button/item under the Page tab in the Sidekick. On click of the button/item it would popup a new window with the target url of that page in Publishing instance.

In short I want a capability of the adding a feature in side kick which will give me a publishing instance preview of the page.   

My approach:  We can find sidesick.js (libs/cq/ui/widgetes/source/widgets/wcm/SideKick.js) where all the properties  like Copy Page,

Move Page, Activate Page and so on..are configured..

So I'm going to add one more entry called 'preview Publish page' in the Same sidekick.jse and I can able to see 'prview publish page' button in the sidekick.Now, I will configure that entry to open a new window which redirects to the publish page url.

Customize sidekick:

----------------------------------------------------------------------------------------------------

Here I’ve customized the SideKick.js such a way that it adds one more button called ‘Preview Publish Page’ under the Page tab in the sidekick. When we click on the button it opens a new window and opens the current page in preview mode.

Customization starts at Line no: 100 to 107 in SideKick.js

/** -------------- Custom ------------------**/

/**

* @cfg {String} previewPublishText

* The text for the Preview Publish button (defaults to "Preview Publish Page").

*/

    previewPublishText:null,

/** ----------------------------------------**/

Customization starts at Line no: 558 & Ends at 607 in SideKick.js

/** ------------------------------------------------------------------------------------------------------------------------------**/

/**

* Returns the config for the default Create Sub Page button.

* @private

* @return {Object} The config for the default Create Sub Page button

*/

    getPreviewPublishPageConfig: function() {

var allowed = CQ.User.getCurrentUser().hasPermissionOn("wcm/core/privileges/modifyhierarchy",this.getPath()) &&

                CQ.User.getCurrentUser().hasPermissionOn("create", this.getPath());

return {

"text": this.previewPublishText,

"disabled": !allowed,

"handler": function() {

if (!CQ.WCM.isPreviewMode()) {

this.wcmMode = CQ.WCM.setMode(CQ.WCM.MODE_PREVIEW);

if (!this.previewReload) {

                    CQ.WCM.getContentWindow().CQ.WCM.hide();

                } else {

                    CQ.Util.reload(CQ.WCM.getContentWindow());

                }

this.collapse();

            } else {

// make sure the button stays pressed

this.previewButton.toggle(true);

            }

var myRef = window.open(''+self.location,'mywin',

'left=20,top=20,width=1000,height=700,toolbar=1,resizable=0', CQ.WCM.setMode(CQ.WCM.MODE_PREVIEW));

/**

* This opens the specified url in new the tab

window.open('http://www.techaspect.com/'); // --> this opens a new tab on my browser

Ext.Ajax.request({

url: 'PHP function call',

success: function(response) {

window.open('http://www.techaspect.com/'); // --> this opens a new window, not tab

}

});

*/

            },

"context": [

                CQ.wcm.Sidekick.PAGE

            ]

        };

    },

/** --------------------------------------------------------------------------------------------------------------------------------**/

Added Line no: 3264

"previewPublishText": CQ.I18n.getMessage("Preview Publish Page"),

Customization starts at Line no: 3680 - 3687

/** --------------------------------------------------------------***/

/**

* The value for {@link #actions} to create a PreviewPublish page button.

* @static

* @final

* @type String

*/

CQ.wcm.Sidekick.PREVIEWPUBLISH = "PREVIEWPUBLISH";

------------------------------------------------------------------

Added Line no: 3790

CQ.wcm.Sidekick.PREVIEWPUBLISH,

After adding the above lines of code in SideKick.js, we can see a customized button in sidekick as shown in the above figure.

-------------------------------------------------------------------------------------------

=======================================================================

Customize column control

Version: CQ 5.4

      OOTB provides components related to the  column controls, but If we directly drag and drop the components on to the page, we can't find such number of columns in the column control. Because we need to add styles to define different columns:
Here you go..
Customize column control:
------------------------
Add the following styles in css:
/* layout 0 : 50% 50%  i.e., two columns wiht 50% , 50%*/
div.cq-colctrl-lt0 { }
div.cq-colctrl-lt0-c0 { width: 340px; margin-right:10px}
div.cq-colctrl-lt0-c1 { width: 340px; margin-left: 10px}
/* layout 1 : 33% 33% 33%  Three columns */
div.cq-colctrl-lt1 { }
div.cq-colctrl-lt1-c0 { width: 220px; margin-right: 10px;}
div.cq-colctrl-lt1-c1 { width: 220px; margin-left: 10px; margin-right: 10px;}
div.cq-colctrl-lt1-c2 { width: 220px; margin-left: 10px; }
Add follwoing colctrl design mode:
-----------------------------------
2;cq-colctrl-lt0      2 Columns (50%, 50%)
3;cq-colctrl-lt1      3 Columns (33%, 33%, 33%)
so that we can get options for two & three columns.

Store Form Data

Task: Create a page which has form by default where we can add different form fields like text, radio buttons, file upload and so on..
Sol:
  Step 1:  Create a template and add properties as shown in the following example. Here the template name is 'formtemplate'
   Also add resourceType property to the template..

Step 2: 
      Add property called Sling:resourceType as 'foundation/components/parsys'
Step 3:
     Will be updated soon...
Note:
   1. While configuring Action to store the data, we specify the path as follows:
      Eg: /tmp/formdata   or /tmp/formdata/
   To Override the form data, We have to specify like
      /tmp/formdata
   To store data in another node, We have to specify like
      /tmp/formdata/

Sunday 15 December 2013

Scheduled Publish

 

Method 1:

Select the page that you want to publish in future..

    1. Right click on the page.
           --> Expand On/Off Time under the Blog Tab
           --> Set On time at which the page to be activated
       --> click 'Ok'
    2. Active page

Method 2:

Select Activate from the menu
  -- select Activelater  and set date & time
Result: The Scheduled page will be stored in the Publishing server but it will published on Scheduled time only.

On/Off Time Behavior:

On Time:
The date and time at which the published page will be activated. When published this page will be kept dormantuntil the specified time. Leave these fields empty for pages you want to publish immediately (the normal scenario).
Off Time:
The time at which the published page will be deactivated. Again leave these fields empty for pages you want to publish immediately.

Ref: http://dev.day.com/docs/en/cq/current/getting_started/first_steps_for_authors.html

Configuring Email Notification

 

In order to send email from CQ, the Day CQ Mail Service needs to be configured properly.
Procedure:

1. Go to felix web console (http://<host-name>:<port>/system/console/configMgr)
2. Search for Day CQ Mail Service
3. Edit and configure as follows:
a.  The SMTP server port must be 25 or higher.
b.  The SMTP server host name must not be blank.
c.  The "From" address must not be blank. 

Screen shot:

Simple code to send an email

There are difference ways to send an email.
Method 1: Using SimpleEmail


        SimpleEmail email = new SimpleEmail();
        email.setHostName('smtp.gmail.com");
        email.addTo("mail@gmail.com", "John Doe");
        email.setFrom("yourmail@gmail.com", "Me");
        email.setSubject("Test message");
        email.setMsg("This is a simple test of commons-email");
        email.send();

Method 2: Using HtmlEmail without using MessageGateway
    

        HtmlEmail email = new HtmlEmail();
        email.setHostName("smtp.gmail.com");
        email.addTo("mail@domain.com", "John Doe");
        email.setFrom("yourmail@domain.com", "Me");
        email.setSubject("Test email with inline image");
        // set the html message //
        email.setHtmlMsg("<html>The apache logo - <img src=\"cid:"
        +cid+"\"></html>");
        // set the alternative message
        email.setTextMsg("Your email client does not support HTML messages");
        email.send();


Note: We can also read smtp details from the felix console configurations instead of passing smtp details directly.
Ref: http://www.blogger.com/blogger.g?blogID=8547815981944994348#editor/target=post;postID=3901523167605550399

Retrieve OSGI Configurations

Scenario: How to retrieve configuration details in jsp/class ?
Solutions:
@Reference
ConfigurationAdmin configAdmin;
Configuration config = configAdmin.getConfiguration("com.day.cq.mailer.DefaultMailService");
Dictionary<?, ?> d = config.getProperties();
log.debug("Configurations Dictionary:{}"+d);
String hostName= (String) d.get("smtp.host");

Quick Notes

How to get a Resource when we have node path:
In Java:
@Reference
private ResourceResolverFactory resourceResolverFactory;
ResourceResolver resolver=null;
resolver = resourceResolverFactory.getAdministrativeResourceResolver(null);
Resource res=null;
res= resolver.getResource("PATH");

In JSP:
Resource res= resourceResolver.getResource("PATH");
How to get a Node, Get node property, Add/Update node property:
Node node = res.adaptTo(Node.class);
Get propoerty:
String prop= node.getProperty("Prop_name").getValue().getString();
String prop = node.setProperty("Prop_name","Value");
Note: Don't forget to save the session when the node is updated.
Update Node using PersistableValueMap:
Resource resource = resolver.getResource("Node Path");
if(resource !=null)
{
PersistableValueMap valueMap= resource.adaptTo(PersistableValueMap.class);
valueMap.put("propname","value");
valueMap.save();
}


Note: PersistableValueMap is deprecated. So use ModifiableValueMap instead.
Example:

if(resource !=null)
{
ModifiableValueMap valueMap= resource.adaptTo(ModifiableValueMap.class);
valueMap.put("propname","value");

}
resource.commit();   //Once ResourceResolver.commit() is called, the changes are finally persisted.

How to get a JackRabbit session in the Workflow:
final JackrabbitSession jackrabbitSession = (JackrabbitSession) wfsession.getSession();
UserManager userManager = jackrabbitSession.getUserManager();



How to get Page object using resolver?
@Reference 
private PageManagerFactory pageManagerFactory;

@Reference 

private ResourceResolverFactory resourceResolverFactory;

ResourceResolver resolver = null;

//get Resource resolver using resource resolver factory.
resolver = resourceResolverFactory
.getAdministrativeResourceResolver(null);

//Get page manager object using page manager Factory
PageManager pmanager = pageManagerFactory.getPageManager(resolver);

//Get page object using PageManager
Page page = pmanager.getContainingPage(path);

Guidelines - Assets Upload into CQ DAM

Here are the few guidelines suggested by CQ Experts (from google groups):
1. Folder/Asset Naming convention
a. Is it good a have a folder/Asset name contains underscore ( _ ).
Eg: /content/dam/Test/Advanced_buttons
Is CQ providing any best naming conventions.
2. Assets (Images/pdf/docs whatever)
a. What is the max size limit of an Asset?
3. What is the max number of Assets that we can upload in a dam folder.

Comments:
1. Yes folders can have underscores. I didn't see any issue till now.
2. Max size limit I am not sure but I injected around 10 to 30 MB size with out any issues.
3. There should not be much issue even if you inject 100 to 150 gigs of assets. we migrated around 130 gigs with out much issues. In terms of number of assets inside a particular folder (Number of nodes at one level) Adobe recommends dont use more than 200 to 250 at one level. If it is more than that front end DAM UI will slows down (still repository doesn't show any issue)