AEM TouchUI dropdown mandatory validation

In AEM 6.2, there is no mandatory validation for dropdowns in dialogs, when having a “Please select” option in place. This is due to a validation check in the out of the box AEM validator which does a check for “value != null”, which is not the case when having a “Please select” option (value would just be a blank String).

There is currently no other option then implementing a custom validation. This requires the following steps:

  1. Create a new clientlib with the following configuration
    <?xml version="1.0" encoding="UTF-8"?>
    <jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
              jcr:primaryType="cq:ClientLibraryFolder"
              categories="[cq.authoring.dialog]"
              dependencies="[_]"/>
  2. Add the following JavaScript to this clientlib. This JS validates all select fields with a specific property set (see step 3)
    /**
     * Fix to allow required select fields.
     *
     * Add validation="select.required" to the field configuration
     *
     * e.g.:
     * <selectField
     *    jcr:primaryType="nt:unstructured"
     *    sling:resourceType="granite/ui/components/foundation/form/select"
     *    validation="select.required">
     */
    (function(window, $) {
      var SELECT_REQUIRED_VALIDATOR = "select.required",
        foundationReg = $(window).adaptTo("foundation-registry");
    
      foundationReg.register("foundation.validation.validator", {
        selector: "select[data-validation='" + SELECT_REQUIRED_VALIDATOR + "'], [data-validation='" + SELECT_REQUIRED_VALIDATOR + "'] select",
        validate: function(el) {
          var value = el.value;
          if (!value || value === '') {
            return "Please fill out this field."
          }
        }
      });
    })(window, jQuery);
  3. To mark select fields as required (and enable this validation), put the property validation=”select.required” on it:
    <selectField
      jcr:primaryType="nt:unstructured"
      sling:resourceType="granite/ui/components/foundation/form/select"
      name="./mySelectField"
      validation="select.required">
       ...
    </selectField>

     

Right-To-Left Keyboard Switch for Granite / Touch UI

In the AEM classic UI, an author entering text in Hebrew can switch the editor to right-to-left mode with the keyboard shortcut ctrl-shift-x.

Unfortunately, this is not possible anymore in AEM’s granite or touch ui. While it sounds strange to create a keyboard shortcut to a touch ui, it is still helpful to have that ability when working from a desktop or laptop computer, so here is a package that implements this functionality:

rte-rtl-switch.zip

This works in both the inline and the full screen rich text editor and has been tested in Internet Explorer, Chrome, Firefox and Safari.

Simply install to you AEM instance using the package manager and the keyboard shortcut works.

enjoy!

TouchUI Page Properties encoding issue with AEM 6.2 CFP3

While implementing a new website based on AEM 6.2 CFP3 for a customer, we faced the following problem:

When editing page properties right from “Sites” > “View Properties”, the encoding of special characters got lost/wrong. When editing the same page from via “Edit” > “Page Properties”, everything worked well.

This resulted in corrupt data:

The reason for this problem: In the “View Properties” dialog, the _charset_ property is missing which is required by AEM to determine the proper encoding of the submitted form data.

There is an easy fix for this:

By adding a hidden field named “_charset_” to the page properties dialog with the value “utf-8”, the problem is solved:

<charset
  jcr:primaryType="nt:unstructured"
  sling:resourceType="granite/ui/components/foundation/form/hidden"
  cq-msm-lockable="_charset_"
  ignoreData="{Boolean}true"
  fieldLabel="Charset"
  name="_charset_"
  value="utf-8"/>

Serving Assets with Selectors

In a project, we recently had the requirement to serve assets with a selector. The reason for adding the selector was that all CUG protected assets need to be served from an specific dispatcher farm.

Unfortunately, the extension of assets is part of the node name in AEM. A sample geometrixx asset is stored as node “GeoCube_Datasheet.pdf” under /content/dam/geometrixx/documents .

When we try to access this with a selector /content/dam/geometrixx/documents/GeoCube_Datasheet.secure.pdf, the resource cannot be resolved, resulting in a 404.

After checking the Sling source code, we’ve found a way to get to the document with the selector: add all the selectors after the extension, and then add extension .res.

Why? that’s quite complicated:

  • adding the selectors after the original extension makes sure that the resource is actually found during resource resolution
  • rendering of resources needs to be done by the org.apache.sling.servlets.get.impl.helpers.StreamRendererServlet, but the servlet only accepts resources without an extension or with .res

But our troubles are not quite over yet: If we render this as a .res resource, Apache will serve this as content type application/x‐dtbresource+xml

The following rewrite rule should take care of that for pdf documents:

RewriteRule ^/content/dam/(.*).secure.pdf /content/dam/$1.pdf.secure.res [PT,L,T=application/pdf]

With this rewrite rule, the asset is actually available for end users at the desired url /content/dam/geometrixx/documents/GeoCube_Datasheet.secure.pdf, hiding the .res extension workaround from the end user.

Why components are not shown the in CoralUI Component Browser

When developing new components in AEM 6.0/6.1 for the new CoralUI frontend you might end up in components not beeing shown in the Component Browser, even if they would be allowed to place on the current page based on the Design configuration.

When are components shown in the Component Browser / Sidekick?

  1. Component is allowed to be placed on the current page (Design configuration)
  2. Component is not hidden
  3. Component has a Dialog OR
  4. Component has a cq:editConfig

Why are components that are matching the criteria not shown in AEM 6.1 Coral UI

Components which are allowed to be placed on the page, are not hidden and do have a valid cq:dialog configuration (CoralUI dialog configuration), will not be shown in the Component Browser nor in the Sidekick. The reason for that is:

The Component Browser is still looking for the existence of the (“old”) extJS dialog.xml configuration. If there is no dialog.xml configuration, the component won’t be shown in the list.

How to resolve this issue

Instead of creating a dialog.xml which will never be used, a simple cq:editConfig can be created to make the components visible again:

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
  jcr:primaryType="cq:EditConfig">
</jcr:root>

Do you need assistance in upgrading to CoralUI or a new AEM version? Just let us know!

Duplicate SSL Filter Bundle in AEM 6.0 SP2 and SlingHttpServletRequest.isSecure()

In AEM 6.0 SP2, there is a new version of the “Apache Felix Http SSL Filter” bundle.

Unfortunately, it has a different package name as the one included in AEM 6.0, which means that both versions of the SSL Filter are active.

SSL_Filter

Now if both filters are active, the second filter does not have the information if originally, the request was sent over https or http.

Therefore the SlingHttpServletRequest.isSecure() method will always return “false”.

This is fixed if the old (0.0.1.R1394715) version of the filter is disabled, one way to do this permanently is the use of the AEM Commons OSGI disabler.

unsatisfied AEM components because of duplicate package names

Today, I had an issue, where some of my AEM components just would not start. They had “unsatisfied” service references, but the referenced services were up and running.

After a lot of searching and thanks to this blog post, I finally found out that the actual problem was that the referenced services’s package name was identical to a package name in my component’s bundle.

So what happened?

Let’s say my Interface was set up like this:

  • bundle name: codeflair-blog-api
  • Interface name: ch.codeflair.blog.api.BlogInterface.java

Package ch.codeflair.blog.api.* was exported and therefore the interface was made available by the bundle.

My implementing class ch.codeflair.blog.impl.BlogInterface.java was set up in a different bundle, let’s say codeflair-core-impl. In this bundle there was another (completely unrelated component) which implemented interface ch.codeflair.blog.api.ConflictingInterface.java in bundle codeflair-core-api.

codeflair-core-api also exported ch.codeflair.blog.api.*, and because the bundle also had a dependency on codeflair-blog-api, all classes from package ch.codeflair.blog.api in codeflair-blog-api were copied to bundle codeflair-core-api and also made available in that bundle. Now our ch.codeflair.blog.api.BlogInterface.java is availabe in two different bundles, so an adapter factory will not recognize that ch.codeflair.blog.impl.BlogInterface.java  actually implements ch.codeflair.blog.api.BlogInterface.java from codeflair-blog-api.

So the bottom line is: choose a good package struckter und really make sure a package is neever user in two different bundles!

AEM / CQ assets with xmpRights:WebStatement

if a DAM asset has property xmpRights:WebStatement set (usually automatically extracted from the file during the import workflow), CQ redirects to the value in the property, assuming that the property contains the path to a page where the user has to accept a license agreement for the asset.

Note that this only happens when the asset directly is accessed, but not when specific renditions are accessed.

To switch off this behavior, access the system console’s config for the BinaryProviderServlet:

http://localhost:4502/system/console/configMgr/com.day.cq.dam.core.impl.servlet.BinaryProviderServlet

Uncheck the option “Enable Digital Rights Management” and your assets are displayed correctly!

Limiting allowed cq:tags-path within namespaces

A common issue when working with CQ tags is the lack of possibility to limit the user entering only specific trees of tags within a namespace in a specific TagInputField.

Example

We have a tag structure like:

  • /etc/tags/search/keyword/…
  • /etc/tags/search/client/…
  • /etc/tags/search/…/…

Now we want the user to enter keywords (and only keywords) in the page property field “Keywords”.

The issue we’re no facing is, that in CQ only allowed namespaces can be limited, but not paths inside a namespace.

To solve this, the following listener attached to the TagInputField in the dialog.xml might help:

<tags
  jcr:primaryType="cq:Widget"
  cls="cq-propsdialog-tags"
  fieldLabel="Keywords"
  fieldDescription="Keyword for the search engine"
  name="./cq:tags"
  xtype="tags">
    <listeners jcr:primaryType="nt:unstructured"
      addtag="function(field, tag){
                if (tag.path.indexOf('/keyword/') == -1) {
                  field.removeTag(tag);
                }
              }"
  />
</tags>

This listener is fired each time a tag is added to the TagInputField. The listener checks if the tag is located inside the desired path (in our case “/keyword”). If not, it will be removed again immediately.

This is a really easy way to restrict TagInputField to certain tag paths.

CQ5 / AEM: Simulating Publish on Author

For the first blog post, we have something small but quite handy:

Some components in CQ are administered in one place and displayed in multiple places.

This post shows how you can display a section of content without the authoring capabilities to improve the author experience.
At the same time, we omit any html decoration tags usually included on the author, this often makes it easier to make advanced html markup look nice on author.

To do this, we need two imports in our jsp file:

<%@page import="com.day.cq.wcm.api.WCMMode" %>
<%@page import="com.day.cq.wcm.commons.WCMUtils" %>

Then, before the section to be displayed like on publish, place the following code:

<%
  WCMMode origMode = WCMMode.fromRequest(request);
  WCMMode.DISABLED.toRequest(request);
  WCMUtils.getComponentContext(request).setDefaultDecorationTagName("");
%>

To revert back to regular author mode, add this piece of code:

<%
  origMode.toRequest(request);
  if (WCMMode.fromRequest(request) == WCMMode.EDIT && WCMMode.fromRequest(request) == WCMMode.DESIGN) {
    WCMUtils.getComponentContext(request).setDefaultDecorationTagName("div");
  }
%>

done!