Take Control of AEM Action Menus with Render Conditions

June 10, 2020 | Patrick Kent
Take Control of AEM Action Menus with Render Conditions blog image

Has this happened to you?

You’re going about your day, working in Adobe Experience Manager (AEM) when a request comes in. Your teammate or client wants five custom user groups, and requires a different complement of authoring options for each of them. Certain menu items and selection actions must only show up for members of designated groups.

screenshot of the AEM menu

Setting RMCDR permissions via /useradmin doesn’t get the whole job done. You need to restrict Quick Publish to an Asset Manager group without causing the Manage Publication action to disappear for non-members. Denying Replicate hides them both. That won’t meet requirements.

screenshot of RMCDR permission Settings

On top of that, you have custom menu actions not tied to RMCDR in any way. They may only display based upon various user, file disposition, and state criteria.

How are you possibly going to be able to complete this request?

Granite Render Conditions 

AEM provides a flexible way to hide or show almost any user interface (UI) element. Render conditions provide this control, and AEM comes with a huge number of them available for your use.

A render condition is a mechanism that decides if a component should be displayed or not. It will drill-down through sub-conditions, evaluating them recursively, and return true or false. The Granite UI Foundation provides a set of built-in render conditions. 

When coding rules of this type, consider the two implementation choices:

  1. Expression properties—placed directly on the granite:rendercondition node.
  2. Stackable references to render condition components—placed underneath the granite:rendercondition node.

Expression properties can only draw from the HTL Global Object context, which does not expose the user’s groups for evaluation. Another gap is the challenge in building up equivalent nested sub-conditions inside EL statements. To address the stated business problem, this post will focus on the second approach.

One of the built-in render conditions appears to do just what we need. It is a component that validates against a group property, and is located at: /libs/fd/fm/gui/components/admin/renderconditions/groups

Step One: Create an Overlay of the Action

In /libs, locate the action that will receive display rules, and overlay it in /apps.

Let’s change the display behavior for the Quick Publish selection action. This controls when Quick Publish will show up after a user has selected one or more DAM assets or pages. It already has a couple of render conditions. We will add a third, restricting display to members of the asset-admin group.

Here is our overlay:

screenshot of Bounteous overlay in AEM

Step Two: Add a Render Condition

Under granite:rendercondition, add:

+ and
  - sling:resourceType = "granite/ui/components/coral/foundation/renderconditions/and"
  + isInGroup
    - sling:resourceType = "/libs/fd/fm/gui/components/admin/renderconditions/groups"
    - group = "asset-admin"

Try it out, and you’ll see that Quick Publish only shows up if you are a member of the asset-admin group.

Step Three: Overlay the Group Render Condition

Next, we’ll want to make another change. This render condition component is located in the /admin section, and won’t run unless you are an admin. We want to move it out of there.

Overlay it in your project folder, and change the sling:resourceType to match. For example: "/apps/<yourproject>/components/renderconditions/groups"

Step Four: Improve the Group Matching Logic

Now our menu element will only show up for members of a single group. But the OOB component does not recognize an array of groups. Wouldn’t that be better? Then it would be more broadly re-usable.

Edit groups.jsp, and change it to:

<%@page session="false"
          import="com.adobe.granite.ui.components.Config,
                  com.adobe.granite.ui.components.rendercondition.RenderCondition,
                  com.adobe.granite.ui.components.rendercondition.SimpleRenderCondition,
                  org.apache.jackrabbit.api.security.user.UserManager,
                  com.adobe.aem.formsndocuments.util.FMUtils" %><%

Config cfg = cmp.getConfig();
UserManager um = resourceResolver.adaptTo(UserManager.class);
boolean isAllowed = false;

String[] groups = cfg.get("groups", String[].class);

for (String group : groups) {
    if( FMUtils.isUserPartOfGroup(request.getUserPrincipal(), um, group) ) {
        isAllowed = true;
        break;
    }
}

request.setAttribute(RenderCondition.class.getName(), new SimpleRenderCondition(isAllowed));
%>

Rename the ‘group’ property to ‘groups’, convert it to String[], and add more groups.

  + isInGroup
    - sling:resourceType = "/apps/myproject/components/renderconditions/groups"
    - groups = "asset-admin,asset-manager"

After this refactor, the component will iterate through all values in the groups array, returning true if any match. It can fulfill all our group matching directives.

Step Five: More Complex Requirements

Now that our teammate and/or client know what we can do, they’re getting more demanding. They’ve added the requirement that regular authors should not be able to request publication of videos. Only video authors should be allowed to do this. But managers and admins are allowed to publish anything. That would mean restricting the Manage Publication contextual action based upon multiple criteria. Here’s how we can model the behavior.

After searching through the JCR for applicable components, we stumble across this one:

/libs/dam/gui/coral/components/commons/renderconditions/videoasset.

It should do nicely. Let’s combine it with our new groups component and leverage the not ‘operator.’ This one of six base render condition components. See the Granite UI Foundation docs.

+ managepublication
  - sling:resourceType = "granite/ui/components/coral/foundation/collection/action"
  + data
    - href.uritemplate = "/mnt/overlay/dam/gui/content/commons/managepublicationwizard.html{?item*}"
  + granite:rendercondition
    - sling:resourceType = "granite/ui/components/coral/foundation/renderconditions/or"
    + condition1
      - sling:resourceType = "granite/ui/components/coral/foundation/renderconditions/and"
      + isInGroup
        - sling:resourceType = "/apps/myproject/components/renderconditions/groups"
        - groups = "asset-video-author,asset-manager,asset-admin"
      + isVideo
        - sling:resourceType = "/libs/dam/gui/coral/components/commons/renderconditions/videoasset"
    + condition2
      - sling:resourceType = "granite/ui/components/coral/foundation/renderconditions/and"
      + isInGroup
        - sling:resourceType = "/apps/myproject/components/renderconditions/groups"
        - groups = "asset-author,asset-manager,asset-admin"
      + not
        - sling:resourceType = "granite/ui/components/coral/foundation/renderconditions/not"
        + isVideo
          - sling:resourceType = "/libs/dam/gui/coral/components/commons/renderconditions/videoasset"

Notice how in some cases we can reference OOB render condition components from /libs without doing an overlay?

Tip: When customizing DAM actions behavior, you can freely reference everything in /libs/dam/gui/coral/components/commons.

Step Six: Sweep Away the Clutter

The DAM menu is a little too busy for some user types, wouldn’t you say?

For example—why should users who only have read on /content/dam be able to see the Copy contextual menu option? Where can they copy to? This action is enabled by default for all users in AEM 6.5. No longer.

+ copyasset
  - sling:resourceType = "granite/ui/components/coral/foundation/collection/action"
  + granite:rendercondition
    - sling:resourceType = "granite/ui/components/coral/foundation/renderconditions/and"
    + mainasset
      - sling:resourceType = "dam/gui/coral/components/commons/renderconditions/mainasset"
    + not
      - sling:resourceType = "granite/ui/components/coral/foundation/renderconditions/not" 
      + isInGroup
        - sling:resourceType = "/apps/myproject/components/renderconditions/groups"
        - groups = "asset-viewer"

Also, why do read-only users see a Manage Tags option? Tags are already viewable via Properties. I know they can’t actually save the tags, but come on. Read-Only can’t ‘manage’ anything. Hasta la vista, Baby.

+ managetags
  - sling:resourceType = "granite/ui/components/coral/foundation/collection/action"
  + granite:rendercondition
    - sling:resourceType = "granite/ui/components/coral/foundation/renderconditions/and"
    + data
      - href.uritemplate = "/aem/managetags.html{+item}"
    + not
      - sling:resourceType = "granite/ui/components/coral/foundation/renderconditions/not" 
      + isInGroup
        - sling:resourceType = "/apps/myproject/components/renderconditions/groups"
        - groups = "asset-viewer"

AEM’s OOB Render Conditions

And there you have it! Now, you can create custom user groups with specific authoring options for each—hiding or showing menu items and selection actions for each of the designated groups as needed.

Below you will find a list of all the render conditions that ship with AEM 6.5. Many of these can be referenced from libs. Others can serve as inspiration for overlays. The sky is really the limit here. Adobe has given us a great head start and a large library to draw from.


screens/dcc/components/renderconditions/hasProperty

cq/workflow/admin/console/components/rendercondition/overriddenmodel

cq/workflow/admin/console/components/rendercondition/emptyproperty

cq/personalization/touch-ui/components/renderconditions/localoractivetargetactivity

cq/personalization/touch-ui/components/renderconditions/activetargetactivity

cq/personalization/touch-ui/components/renderconditions/nondeactivatedactivity

cq/personalization/touch-ui/components/renderconditions/inactivetargetactivity

cq/personalization/touch-ui/components/renderconditions/hastargetconfig

cq/personalization/touch-ui/components/renderconditions/cancreateaudience

cq/personalization/touch-ui/components/renderconditions/targetactivity

cq/gui/components/renderconditions/canwriteworkflow

cq/gui/components/renderconditions/notfound

cq/gui/components/renderconditions/policy

cq/gui/components/renderconditions/canmodify

cq/gui/components/renderconditions/enablefragmentidentifier

cq/gui/components/renderconditions/isinapps

cq/gui/components/renderconditions/islocked

cq/gui/components/renderconditions/islaunchresource

cq/gui/components/renderconditions/canreplicate

cq/gui/components/renderconditions/ismobileresource

cq/gui/components/renderconditions/hasanalytics

cq/gui/components/renderconditions/canreadworkflowmodels

cq/gui/components/workflow/editor/rendercondition/workflow

cq/gui/components/projects/admin/renderconditions/noMasterSpecified

cq/gui/components/projects/admin/renderconditions/parentInMasterFolder

cq/gui/components/projects/admin/renderconditions/taskwriteaccess

cq/gui/components/projects/admin/renderconditions/withoutteamreference

cq/gui/components/projects/admin/renderconditions/workflowinproject

cq/gui/components/projects/admin/translation/renderconditions/addtranslationobjectaccess

cq/gui/components/siteadmin/admin/properties/renderconditions/canedit

cq/gui/components/coral/common/admin/renderconditions/mediaportal-config

cq/gui/components/authoring/editors/rendercondition/template

cq/gui/components/authoring/editors/rendercondition/page

cq/gui/components/authoring/pageinfo/renderconditions/replicate

cq/translation/cloudservices/rendercondition/isProjectAdmin

cq/translation/cloudservices/rendercondition/isWorkflowUser

cq/inbox/gui/components/inbox/itemdetails/tabs/rendercondition/isworkitem

cq/inbox/gui/components/inbox/itemdetails/tabs/rendercondition/istask

cq/inbox/gui/components/inbox/itemdetails/tabs/rendercondition/hasprojectinfo

cq/inbox/gui/components/inbox/itemdetails/tabs/rendercondition/isfailureitem

cq/inbox/gui/components/inbox/itemdetails/tabs/rendercondition/hasworkflowinfo

fd/af/dor/rendercondition/tab

fd/af/authoring/editors/rendercondition/theme

fd/af/authoring/editors/rendercondition/form

fd/af/authoring/editors/rendercondition/template

fd/fm/gui/components/admin/renderconditions/groups

commerce/gui/components/admin/collections/renderconditions/productbasedcollection

commerce/gui/components/admin/collections/renderconditions/collectionbasedcollection

commerce/gui/components/admin/collections/renderconditions/querybasedcollection

social/console/components/renderconditions/createtenant

social/console/components/renderconditions/createGroupBtnRenderCondition

social/console/components/renderconditions/createsite

dam/components/configurations/dm/youtube/edit/channellist/renderconditions/channels

dam/cfm/admin/components/renderconditions/associatedcontent

dam/cfm/admin/components/renderconditions/fragment

dam/cfm/admin/components/renderconditions/contentfragment

dam/cfm/components/renderconditions/hasfragment

dam/cfm/models/console/components/renderconditions/displayInNav

dam/gui/components/s7dam/sets/datasources/singleassetdatasource/renderconditions/asset

dam/gui/components/s7dam/common/rendercondition/dm

dam/gui/components/s7dam/common/rendercondition/dms7

dam/gui/components/s7dam/dmrenderconditions/dmasset

dam/gui/components/s7dam/dmrenderconditions/assettyperendercondition

dam/gui/components/s7dam/dmrenderconditions/remoteasset

dam/gui/components/s7dam/dmrenderconditions/s7assetready

dam/gui/components/s7dam/dmrenderconditions/canwrite

dam/gui/components/admin/renderconditions/cancheckout

dam/gui/components/admin/renderconditions/isparentassetcheckedout

dam/gui/components/admin/renderconditions/cancheckin

dam/gui/components/admin/renderconditions/isviewablebycurrentuserandhasnotexpired

dam/gui/components/admin/renderconditions/fragmentsenabled

dam/gui/components/admin/renderconditions/islivecopysource

dam/gui/components/admin/renderconditions/scene7

dam/gui/components/admin/renderconditions/ischeckedout

dam/gui/components/admin/renderconditions/dynamicmedia

dam/gui/components/admin/renderconditions/contentfragment

dam/gui/components/admin/renderconditions/isparentassetcheckedoutbycurrentuser

dam/gui/components/admin/renderconditions/checkedout

dam/gui/components/admin/renderconditions/iscurrentuseradmin

dam/gui/components/admin/renderconditions/ischeckedoutbycurrentuser

dam/gui/coral/components/commons/renderconditions/propertyValue

dam/gui/coral/components/commons/renderconditions/isimage

dam/gui/coral/components/commons/renderconditions/mainasset

dam/gui/coral/components/commons/renderconditions/stockaccessible

dam/gui/coral/components/commons/renderconditions/haspages

dam/gui/coral/components/commons/renderconditions/isdiskusagereport

dam/gui/coral/components/commons/renderconditions/stockassetlicensed

dam/gui/coral/components/commons/renderconditions/propertypagepermission

dam/gui/coral/components/commons/renderconditions/hasreviewstatus

dam/gui/coral/components/commons/renderconditions/videoasset

dam/gui/coral/components/commons/renderconditions/isnotlinksharereport

dam/gui/coral/components/commons/renderconditions/onpage

dam/gui/coral/components/commons/renderconditions/canextract

dam/gui/coral/components/commons/renderconditions/isfolder

dam/gui/coral/components/commons/renderconditions/haspermissions

dam/gui/coral/components/commons/renderconditions/removeprivaterendercondition

dam/gui/coral/components/commons/renderconditions/isassetexpired

dam/gui/coral/components/commons/renderconditions/istextasset

dam/gui/coral/components/commons/renderconditions/stockasseteditorial

dam/gui/coral/components/commons/renderconditions/hasannotations

dam/gui/coral/components/commons/renderconditions/singleitem

dam/gui/coral/components/commons/renderconditions/hassubassets

dam/gui/coral/components/commons/renderconditions/stockasset

dam/gui/coral/components/commons/ui/shell/datasources/assetsdatasource/renderconditions/directory

dam/gui/coral/components/commons/ui/shell/datasources/assetsdatasource/renderconditions/asset

dam/gui/coral/components/admin/renderconditions/userprop

dam/gui/coral/components/admin/renderconditions/macshare

dam/gui/coral/components/admin/renderconditions/locked

dam/gui/coral/components/admin/renderconditions/sync-config

dam/gui/coral/components/admin/foldershare/renderconditions/hasmetadataschema

dam/gui/coral/components/admin/foldershare/renderconditions/foldermetadataschema

dam/gui/coral/components/admin/collections/collectiondatasource/renderconditions/collection

dam/gui/coral/components/admin/stock/renderconditions/stockassetlicensed

wcm/msm/components/touch-ui/renderconditions/isblueprint

wcm/msm/components/touch-ui/renderconditions/islivecopy

wcm/msm/gui/components/renderconditions/capability

wcm/designimporter/components/touch-ui/renderconditions/isimporterpage

wcm/designimporter/components/touch-ui/renderconditions/hascanvas

granite/ui/components/foundation/renderconditions/feature

granite/ui/components/foundation/renderconditions/or

granite/ui/components/foundation/renderconditions/privilege

granite/ui/components/foundation/renderconditions/simple

granite/ui/components/foundation/renderconditions/not

granite/ui/components/foundation/renderconditions/and

granite/ui/components/coral/foundation/renderconditions/feature

granite/ui/components/coral/foundation/renderconditions/or

granite/ui/components/coral/foundation/renderconditions/privilege

granite/ui/components/coral/foundation/renderconditions/simple

granite/ui/components/coral/foundation/renderconditions/not

granite/ui/components/coral/foundation/renderconditions/and

granite/oauth/components/renderconditions/revocationactive