Posts tagged "CQ"

How to have changes to bundles recognized and applied during development?

CQ and Sling have watches folders with the name of “install.” Among other things, these folder are watched for *.jar files for bundles being added or changed. These bundles will automatically will be loaded or reloaded in the Felix OSGi framework as needed. Note the emphasis on the phrase, as needed. Just changing a bundle within the CQ repository does not guarantee that your bundle will be reloaded. While developing bundles, it is important to know how CQ/Sling determines that a bundle needs to be loaded or reloaded in Felix after it is changed in the repository. Bundles will be loaded/reloaded after a bundle jar has been added or update when:

  1. The bundle does not already exist in the OSGi framework
  2. The bundle version is different and more recent
  3. The bundle version has “-SNAPSHOT” at the end

It is worth noting that it is good practice to use the “-SNAPSHOT” version for bundles that are in development and are not in their final version. One way the naming convention help is that it keeps from having problems telling which bundle build to use to deploy versus builds used in development. No “-SNAPSHOT” at the end, this is the final version. “-SNAPSHOT” at the end, the developer hasn’t finished it yet and it will change.

Here are some example of how this will work for a bundle called, my-bundle::

Existing: my-bundle-1.0.0
Added to repository: my-bundle-1.0.0
Result: Felix will NOT reinstall the bundle. It is assumed that this is the exact same version of the bundle of a released bundle

Existing: my-bundle-1.0.0
Added to repository: my-bundle-1.0.1
Result: Felix WILL install the bundle with the later version of the bundle added to the repository

Existing: my-bundle-1.0.0-SNAPSHOT
Added to repository: my-bundle-1.0.0-SNAPSHOP
Result: Felix WILL reinstall the bundle. Sling treats the bundle as being in development and considers each installation to be a new build.

 

CQ and AEM Helpful Links – Knowledge Base Articles by Subject

The Knowledge Base for CQ does have pages that list articles by subject:

System Administration
Hot Fixes
Development
Authoring
General Troubleshooting
DAM

Sometimes I have problem finding the right keywords to search by. An old-school list works great.

Automatic versioning of CQ DAM assets

How do you enable automatic versioning for DAM assets? The basic answer is, you can’t in CQ, as is. Pages can have automatic versioning when they are activated. DAM assets, on the other hand, don’t have the same capability without customization to CQ.

In CQ a DAM asset version must be explicitly created by a user in the DAM asset editor. The instructions on how to do this can be found in Versioning in CQ5 DAM.

Automating versioning of CQ DAM assets is more problematic.

One strategy would be to use packages to contain versions. Whole groups of DAM assets could be versioned at the same time using this method. To create a snapshot, make a new package containing the assets, give it a version, and build it. To make a snapshot of a new version of the assets, create a new version of the package and build it. This can be automated by creating scripts that periodically use curl or wget to build the packages with Package Manager.

Another option is to either a listener for change events within the DAM folder or by adding a versioning step to an existing workflow.

This is sample code that shows how to create a version within a workflow step (from Henry Saginor and David Collie):

....
@Reference
private ResourceResolverFactory resolverFactory;
private ResourceResolver getResourceResolver(Session session)
                         throws LoginException
{
     Map authInfo = new HashMap();
     authInfo.put(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, session);
     return resolverFactory.getResourceResolver(authInfo);
}
.....
Resource res = getResourceResolver(session).getResource(assetPath);
Asset asset = res.adaptTo(Asset.class);
Revision rev = asset.createRevision(revisionLabel, null);
.....

 

Internationalization within Sling (and CQ)

Sling, and as an extension, CQ, both use a model for internationalization that is very similar to the one used by Java and Flex. Java and Flex store translations as key/value pairs within properties files. Within Sling, translations are stored within the repository as key/value pairs. Once defined, the developer is able to use the key/value pairs stored in the repository to populate strings used in a graphic interface such as a Web page.

CQ has additional tools for internationalization than those provided by Sling. The CQ additions are not part of the scope of this article.

Locales

Before using internationalization within Sling the developer must be knowledgeable about the concept of locale. Locales within Java can be defined by three different properties: the language, the region and the variant. The primary defining property of a locale is a human language. Regions are used because each language can have several dialects and usage of the language varies greatly based on the region it is spoken and written. For that reason, a region code can be associated with a language. In that way, Canadian French may have its own definitions and translations and European French a different set. Additional information about the locale can be defined in the variant property. This property can contain information about a specific locale that is not covered by the other two properties. Examples of variant information for a locale is type of the language used, the target operating system or the text encoding.

Sling uses a standard representation of locale. The language is represented as the ISO 639‑1 code for the language in lower case. This is usually a two letter code. English, for example, would be represented as en. If needed, a region code can be specified. This is done by adding an underscore plus the ISO 3166-1 region code in upper case to the language code. So for English as spoken and written in the USA, the full locale code would be en_US. Very rarely would there be a variant defined in a locale for Sling.

Sling uses this standard because browsers use this standard for locale. Within Sling, the typical usage of internationalization would be to return the content of a Web page using the default locale of the Web browser. The default locale or locales are passed to Sling as part of the request. The site developer has the choice of respecting the locale of the Web browser and sending back content appropriate for that locale.

Key/value pairs

What do key/value pairs define? I call them translations but technical they are translation segments. Translation implies taking a word or phrase and finding the best match in another language. Translation segment is what you get after someone has done the translation for a specific use and the results are saved in a look-up table. The value of translation segment has a very specific meaning. When the phrase, OK, is used on a button the translation segment in another locale for that phrase is not necessarily a direct translation of the word, OK, but what the OK button is called in the target locale. Translation segment is very context driven. Because of that it can contain style information or substitutions indicated for data set at run time. It can even be a pattern for how information, such as a date or number, is rendered in a locale.

Within Sling, key/value pairs are organized by locale. If a site supports two languages, matching key/value pairs should exist for both. The locales CQ defines key/value pairs for can be seen at /libs/wcm/core/i18n.

Those keen of eye may notice that the locale code, en, is not there. In Java, if the value of a key is requested for a locale and that key does not exist, an exception is thrown. Not so in Sling. If a key is not defined in a locale Sling does not throw an exception. Instead, the key itself is returned. The keys for the key/value pairs in /libs/wcm/core/i18n are the translations in English. Since the locale for English does not exist the key is returned when the value of a key for the locale, en, is requested.

In CQ, the default for a key is the English phrase. The key and the English phrase are the same. Using the English phrase as the key for the key/value pair is an architectural decision and is not the only way to build translation memory in CQ.

I prefer to not use defaults for the phrases as keys. One thing that is constant in development is change. And it is likely that the text for the user interface will change. If it does, the key must be changed in all instances in the code and for each locale. Where multiple teams edit each of these items this can get to be a logistical challenge. I would use, hello_world, as a key versus the default, English, value for the phrase, Hello World! Doing things in that way allows the translators and the developers to work relatively independently of one another once the key/value pairs are defined.

Using the English phrase as the key does have its advantages and this is the reason this is the convention CQ uses. The advantage to using a default phrase as key is that the keys themselves will show up in the interface when the translation does not yet exist in a locale. Having non-phrase keys within a user interface does look like a mistake. When the phrase in English is the key and the translation does not exist in the target locale then the English phrase is shown.

Which convention to use is a matter of preference and the specific use case.

Translations in the repository

As can be seen for the CQ translations at /libs/wcm/core/i18n, the key/value pairs are stored by locale. The locale node must use the mixin, mix:language, and have the property, jcr:language, defined with a locale. In addition, the locale node may have the property, sling:basename. The sling:basename property is either a String or String array of names that can be used a labels or tags to filter translations with. The name of the locale node is not significant and can be anything. Be nice to others who have to edit what you have done, though. Name the locale node to match the locale within jcr:language or name of the language. For example, name a locale node for English either English or en. The primaryType of the locale node is not significant for translations.

Within the locale node are key/value child nodes, each one containing the information for a single key/value pair. These child nodes must either have the primaryType of sling:MessageEntry or they must have sling:Message as a mixin. The key/value node must contain a property, sling:message, that is the value for the key. If the property, sling:key, exists then that value is used as the key. If the sling:key property does not exist then the name of the node is used as the key. Following the Be Nice Rule, name the key/value node the name of the key. If you do that, the sling:key property is redundant.

Here is an example of key/value pairs for both English and French:

/apps/myapp/i18n
             +-- en (nt:folder, mix:language)
             |    +-- jcr:language = "en"
             |    +-- hello_world (sling:MessageEntry)
             |    |    +-- sling:key = "hello_world"
             |    |    +-- sling:message = "Hello world!"
             |    +-- goodbye (sling:MessageEntry)
             |         +-- sling:key - "goodbye"
             |         +-- sling:message = "Goodbye!"
             +-- fr (nt:folder, mix:language)
                  +-- jcr:language = "fr"
                  +-- hello_world (nt:unstructured,sling:Message)
                  |    +-- sling:key = "hello_world"
                  |    +-- sling:message = "Bonjour tout le monde!"
                  +-- goodbye (nt:unstructured,sling:Message)
                       +-- sling:key - "goodbye"
                       +-- sling:message = "Au revoir!"
-

Using internationalization within JSP

Within this blog post I will only give an example of using key/value pairs for locales within JSP. They can be used elsewhere in Sling, as well.

When the <sling:defineObjects /> tag is used within a JSP page, the slingRequest value is created. The slingRequest implements the SlingHttpServletRequest interface and has a couple of very useful methods for internationalization: getLocale(), getLocales(), getResourceBundle(Locale), and getResourceBundle(String, Locale).

The getLocale() method gets the default Locale of the request. The getLocales() method gets all of the methods for a request. Typically the value for these come from the browser.

The method, getResourceBundle(Locale), gets a ResourceBundle instance that has all of the found key/value pairs for the locale. The method, getResourceBundle(String, Locale), gets a ResourceBundle with all of the key/value pairs for a base name and a locale. The ResourceBundle interface has a method, getString(String), in which the argument is the key and the value of the key is returned. If the ResourceBundle does not have that key, the value returned is the key itself. One thing to keep in mind is that a ResourceBundle is not the same thing as an OSGi bundle. They are two different concepts using the same name.

An example:

<%@ page contentType=”text/html; charset=UTF-8″ language=”java” errorPage=”” %><%
%><%@ page session=”false” %><%
%><%@ page import=”javax.jcr.*,
org.apache.sling.api.resource.Resource”
%><%
%><%@ taglib prefix=”sling” uri=”http://sling.apache.org/taglibs/sling/1.0″ %><%
%><sling:defineObjects /><%
%><!DOCTYPE HTML>
<html>
<head>
<meta charset=”UTF-8″>
<title>Test</title>
</head>
<body>
<%= slingRequest
.getResourceBundle(slingRequest.getLocale())
.getString(“hello_world”) %>
</body>
</html>
/xml]

See also:

Apache Sling – Internationalization Support

Setting default values on CQ components

Components are self-contained portions of code within CQ. While not necessarily visual or part of the user interface, oftentimes they are. Within the CQ framework components can be dragged from the SideKick and placed within a page layout.

Within the CQ page editor, a component can have a dialog to configure values for the component instance that is on the page. Documentation exists that describes how to create a component for use within CQ. It is not necessary for the page author to open the component editor and edit all of the values each time a component is used. Properties can be defined with a default value which is applied automatically.

Prerequisites

To create a default value on a component, knowledge of how to create and edit components within CRXDE or CRXDE Lite is necessary.

Setting a default value on a CQ component

Go to the widget within the component’s dialog that is used to set the property on the component and add a property to the widget itself: defaultValue. If it is possible to select multiple values the defaultValue property should be an array.

Example

In this example I will have a simple viewer component that has a single configurable property: alignProp. Alignment may either be a Left or Right. By default, the value should be Left.

Within the /apps/dekesmith/examples/components/ directory I created a new component node called simple-viewer:

{
    " jcr:title":"Simple Viewer",
     "allowedParents":["*/*parsys"],
     "sling:resourceSuperType":"foundation/components/parbase",
     "componentGroup":"Media",
     "jcr:primaryType":"cq:Component",
     "dialog":{
          "jcr:primaryType":"cq:Dialog",
          "items":{
               "jcr:primaryType":"cq:WidgetCollection"
               "items":{
                    "jcr:primaryType":"cq:WidgetCollection",
                    "tab1":{
                        "jcr:primaryType":"cq:Panel",
                        "title":"tab1",
                        "items":{
                            "jcr:primaryType":"cq:WidgetCollection",
                            "alignment":{
                                  "jcr:primaryType":"cq:Widget",
                                  "fieldLabel":"Alignment",
                                  "type":"select",
                                  "xtype":"selection",
                                  "defaultValue:","Left",
                                  "name":"alignProp",
                                  "options":{
                                       "jcr:primaryType":"cq:WidgetCollection",
                                       "Left":{
                                            "jcr:primaryType":"nt:unstructured",
                                            "value":"Left",
                                            "text":"Align Left"    
                                       },
                                       "Right":{
                                            "jcr:primaryType":"nt:unstructured",
                                            "value":"Right",
                                            "text":"Align Right"
                                       }
                                  }
                            }
                        }               
                    } 
                }
          }
     }
}
 

This component has an edit dialog for setting properties of the component within the CQ site editor. The edit dialog has a single tab with a single selection menu, alignment, that sets a property for the component, alignProp. The alignment menu has two choices, Right and Left. The default value should for the property should be added on the widget that sets that property. To give the alignProp property a default of Left I added a defaultValue to the selection widget, alignment.

Using sling:Mapping to expose repository values

Besides conventional client access to Web sites using a browser, client applications can be built within applications such as Flex and still access data in our repositories. A quick way of doing this is by having Flex access that data as JSON. There is one thing that makes this problematic for production. When a publish instance is accessed through Dispatcher it is suggested that any access to URLs ending with the .json suffix should be forbidden. For security reasons many directories in the repository should not be accessible through outward facing connections. Like I mentioned, this can be problematic if JSON is being accessed by Flex.

Sling, and by extension CQ, has a redirect mechanism that can selectively expose values.

Pre-requisites

Knowledge of the use of CRXDE or CRXDE Lite to edit nodes and properties is required for the example.

What are sling:Mapping nodes?

Sling uses sling:Mapping nodes much the same way that Apache’s mod redirect uses its configurations in httpd to define redirect behavior. The Apache Sling Web site has documentation for mappings. Existing mappings can be seen in the Sling Resource Resolver Console at http://[host]:[port]/system/console/jcrresolver.

A brief explanation is that redirect behavior can be configured by setting properties on sling:Mapping nodes. There should be one sling:Mapping node per redirect rule. The sling:Mapping nodes can have nested sling:Mapping nodes, but that is not needed for my simple example and I won’t go there at this time. Add the sling:Mapping nodes to the repository within the /etc/map/ directory. Sub-folders can be created in this directory and that has an effect on how URLs are matched for redirecting. Once again, this article does not need this and I won’t get into that. This framework can be very powerful and has many ways of being configured. The use case for the sling:Mapping node will be basic and simple in this example.

Using redirection to access JSON data behind a securely configured Dispatcher

For this example I will show how to create a redirect that allows access to the possible values of a dialog used to configure a component. The component is the Flash component. The values are for the menu property of the Flash component. That value is contained within the dialog configuration of the Flash component:

|- libs
   |- foundation
      |- components
         |- flash
            |- dialog
               |- items
                  |- menu
                     |- options
 

The url I used to get the JSON version of this on my local, sample server is [1]. Calling this JSON as admin returns:

{
     "jcr:primaryType":"cq:WidgetCollection",
     "show":{
          "value":"show",
          "jcr:primaryType":"nt:unstructured",
          "text":"Show"
     },
     "hide":{
          "value":"hide",
          "jcr:primaryType":"nt:unstructured",
          "text":"Hide"
     }
}
 

The direct URL to this value is not available behind Dispatcher. Dispatcher should not allow calls either to the /libs directory nor any resource with a .json suffix.

To make it so the Flex client can query this value we need to set up a mapping for it.

  1. Create a node called, menudata, of type sling:Mapping within the /etc/maps/ directory
  2. Create a property called, sling:internalRedirect, of type String to the menudata node. Set it to:/libs/foundation/components/flash/dialog/items/advanced/items/menu/options.1.json
  3.  Create a property called, sling:match, of type String to the menudata node. Set it to:^[^/]+/[^/]+/menu-options$

Once saved, the redirect takes effect. You should see the values of this mapping if you go to Sling Resource Resolver Console.

The sling:internalRedirect property is the relative URL to a resource within the repository. There are, of course, exceptions. But for this example the value of the internalRedirect property is returned without sending a redirect message back to the browser.

The sling:match property is a regular expression pattern used to match the URL. If this property were not present in the sling:Mapping node it would use the node’s name to match with.

Now, when I go to the URL that calls menu-option ([2] on my local server), the value of the options node is returned.

Standard cautions apply. Be very conservative about what you expose to the outside world. But the redirect mechanism in Sling is very powerful and it is worth exploring.

[1] http://localhost:4502/libs/foundation/components/flash/dialog/items/advanced/items/menu/options.1.json

[2] http://localhost:4502/menu-options

Editing the content directory in CRXDE

CRXDE is the eclipsed-based tool used to to develop within the CQ and CRX repository. By default, only a few directories within the repository are visible. The content directory is not one of them. A setting in the repository must be change before it can be seen.

Go to /etc/crxde/profiles/default. There is a property, crxde:paths. This property is a list of String. Add /content to that list and restart CRXDE.

Output Exceptions in CQ Blog Feeds

Symptom

The Atom blog feed from CQ that is obtained by using the .feed suffix on a URL has exceptions within it. The output is an error witinh the feed.jsp or feedentry.jsp file and it contains the following:

Caused by: java.lang.IllegalStateException: output stream already obtained
 

What Can Be Done?

There is a structural supposition within CQ that the output of JSP pages will be HMTL. Because of that, <div> tags are generated for sections of content automatically. Writing these <div> tags causes writes to the output buffer that should not happen. Even if writing to the output buffer did not cause the exception, it would create bad Atom files with <div> tags sprinkled throughout in places they should not be. Changes need to be made to the JSP pages that handle the *.feed suffix. Those files can be found here:

/libs/foundation/components/page/feed.jsp /libs/foundation/components/page/feedentry.jsp /libs/collab/blog/components/page/feed.jsp /libs/collab/blog/components/page/feedentry.jsp

Within ALL feed oriented JSP files (*.feed.jsp, *.feedentry.jsp, etc) the following must be added:

// This bypasses the normal component handling with its automatic output of content
request.setAttribute(ComponentContext.BYPASS_COMPONENT_HANDLING_ON_INCLUDE_ATTRIBUTE, "true");

// This changes the tag used for blocking areas of content from "div" to "". 
// When empty, a tag will not be written to create blocks of content.
IncludeOptions.getOptions(request,true).setDecorationTagName("");

These nodes are part of the foundation components for CQ. Make backups. And be aware that these components can be overwritten without warning in future updates. You will need to document well the changes that have been made in case problems arise later.

Watched File System Folders in CQ/CRX

Greg Klebus suggested using WebDAV to mount repository directories on the file system. Doing this, he said, the end user has access to file system directories on which CQ can automatically run workflows.

A quick example of this is mounting the DAM directory as WebDAV. When content is added or changed within the DAM directory of CQ, a workflow process will run automatically.

These are the steps I go through for using WebDAV with Windows 7 to do that very thing.

I mount the CRX repository as a mapped drive within Windows 7 by right-clicking on the computer and selecting the Map Network Drive menu item. I pick the drive letter and, for my local installation, use the CRX WebDAV URL for my server [1].

Windows 7, out of the box, may not support WebDAV. To use WebDAV on a computer with Windows 7 you may need to install an update from Microsoft: Software Update for Web Folders [2]. You may want to see if it works without this update. It is an old software update and only lists Windows 2003 and Vista specifically. For instructions on how to get Windows 7 64bit, specifically, to work with WebDAV go to [3].

Within CQ 5.4, you can set the permissions of a directory under /var/dam to allow read/write or write privileges for the end user. When they drop an image into this folder there is a workflow that explodes the image, generates thumbnails, and creates the dam:asset for that image. The result of the process can be seen in the /content/dam directory. For CQ 5.5, images placed into /content/dam and its sub-directories will trigger the workflow.

[1] http://localhost:4502/crx/repository/crx.default

[2] http://www.microsoft.com/en-us/download/details.aspx?id=15123

[3] http://shon.org/blog/2010/03/04/howto-fix-windows-7-64bit-webdav/

Register a Custom Namespace within CQ

It is possible to add your own namespaces within CQ. Just as there are predefined namespaces such as cq, jcr and sling, you can have a namespace for your repository metadata and xml processing.

Go to the node type administration page, http://<host>:<port>/crx/explorer/nodetypes/index.jsp. The right-most button at the top of this pages is Namespaces. Clicking that button will open a pop-up window containing the namespace administration page. At the bottom of the page is the New button use to add a namespace.

The namespace follows the XML namespace convention. There is the id in the form of a URI and the prefix associated with that id.