Recently, a client approached us with a requirement to update content fragment elements data, Assets metadata, and Folder metadata via an Azure-based API. Our initial solution was to implement a JMX script, which could be started by an admin user group with valid parameters. However, this was a rather limiting solution, as normal AEM authors did not have the necessary permissions to perform these operations.

To solve this problem, the client requested us to provide a custom action button in the AEM Assets Menu. This button would enable AEM authors who have permission to modify the AEM Asset metadata to update the AEM assets by triggering the action button for the selected AEM assets. This custom implementation proved to be very helpful to the client, as they could easily access the AEM Assets Menu and trigger the custom action button.

AEM Asset – Preview Custom Action Button

This blog post will discuss how we implemented this custom action button.

Overlay the Node

Go to AEM’s CRXDE.
Jump on /libs/dam/gui/content/assets/jcr:content/actions/selection Path
Right-click on the Selection node and select the Overlay Node option from the pop-up menu.

AEM – Overlay Node Pop-Up Menu

AEM – Overlay Node setup

Make sure the Overlay location is under /apps/ folder, and the Match Node Types option should be checked.
This will look as follows.

AEM- Overlay Path location in CRDEX

Add Custom Action Bar Node

To add a custom action button in the Action menu, we need to create one action bar child node below Selection Now. Let’s do that!

Create a node named custom-action-btn (you can put any name here) and set the following properties on a node.

AEM CRXDE – Custom Action Button Node

XML Format

<custom-action-btn
granite:class=”foundation-collection-action suraj-custom-action-btn”
jcr:primaryType=”nt:unstructured”
sling:resourceType=”granite/ui/components/coral/foundation/collection/action”
activeSelectionCount=”single”
icon=”refresh”
text=”Refresh Asset Data”
title=”Refresh Asset Data”
variant=”actionBar”>
<granite:rendercondition
jcr:primaryType=”nt:unstructured”
sling:resourceType=”granite/ui/components/coral/foundation/renderconditions/and”>
<haspermission
jcr:primaryType=”nt:unstructured”
sling:resourceType=”dam/gui/coral/components/commons/renderconditions/haspermissions”
path=”${requestPathInfo.suffix}”
privileges=”[modify_property]”/>
</granite:rendercondition>
</custom-action-btn>

You can also set granite:rendercondition if you want to handle specific scenarios in order to display this custom action button (example Provided in the above code to display this action button to the AEM user who has modify_property permission)

we are done with the overlay part. The next step is to write a custom action using Ajax call, or else you can also use custom wizards to perform any operation using this action button. But here, I will be handling custom actions using Ajax calls.

To show the action button for specific conditions, let’s consider action button should only display when the AEM author selects the Content Fragment; in that case, we need to write a custom JS code to enable and disable the visibility for the action button.

Create a JS file inside the client library folder by specifying clientlib categories as dam.gui.actions.coral

Asset-editor-action-btn-visibility.js
(function(document, $) {
“use strict”;

//initially hide the refressh asset button
$(document).trigger(“foundation-contentloaded”);
$(“.suraj-custom-action-btn”).addClass(“foundation-collection-action-hidden”);

// hide/show refressh asset btn when author select CF asset.
$(document).on(“foundation-selections-change”, “.foundation-collection”, function(e) {
let view = $(“body”).data(“shell-collectionpage-view-layoutid”);
if (view === “list” || view === “column”) {
let selectedItem = $(this).find(“.foundation-collection-item.foundation-selections-item”);
let itemType = $(selectedItem).data(“editorkey”);
if (itemType?.trim() === “contentfragment”) {
var contentFragmentPath = $(selectedItem).data(“foundation-collection-item-id”);
if(contentFragmentPath.includes(“/sample-cf/”)){
$(“.suraj-custom-action-btn”).removeClass(“foundation-collection-action-hidden”);
}
}
} else if (view === “card”) {
let selectedItem = $(this).find(“.foundation-collection-item.foundation-selections-item”);
let itemType = $(selectedItem).find(“[data-editorkey]”).data(“editorkey”);
if (itemType?.trim() === “contentfragment”) {
var contentFragmentPath = $(selectedItem).data(“foundation-collection-item-id”);
if(contentFragmentPath.includes(“/projects/”)){
$(“.suraj-custom-action-btn”).removeClass(“foundation-collection-action-hidden”);
}
}
}
});
})(document, Granite.$);

Now create another JS file to handle the custom action when the Author triggers the custom action button.

Trigger-custom-action-btn.js
(function(document, $) {
“use strict”;

// On click of Refresh Asset button update the selected CF
$(document).on(“click”, “.foundation-collection-action.suraj-custom-action-btn”, function(e) {
let selectedElements = $(“.foundation-collection-item.foundation-selections-item.is-selected”);
var items = [];
$(selectedElements).each(function(e) {
let data = $(this).data(“foundation-collection-item-id”);
items.push(data);
});
let message = “<h3>Updating data for following Selected CF</h3>” + items.toString().replaceAll(“,”, “<br>”);
//Show wait ticker untill response is received from ajax call.
const $UI = $(window).adaptTo(“foundation-ui”);
var $waitTicker = $UI.waitTicker(“Updating Asset”, message);

// Ajax to update selected CF.
$.ajax({
type: ‘POST’,
url: ‘<Enter servlet path or API Path which will return the JSON output>’,
data: JSON.stringify(items),
contentType: “application/json”,
dataType: “json”,
success: function(response) {
if (response.status === 200) {
// do some additional stuff with response as per requirement.
$UI.alert(“Success”, response.message, “success”);
$waitTicker.clear();
} else {
$UI.alert(“Error”, “Failed to update selected Content Fragments”, “error”);
$waitTicker.clear();
}
},
error: function() {
$UI.alert(“Error”, “Failed to update selected Content Fragments”, “error”);
$waitTicker.clear();
}
});
});
})(document, Granite.$);

Include both the JS files in the js.txt file of your client library folder.

We are done with the implementation part. Now go to the Asset folder and select any Content Fragment. The action button named Refresh Asset Data item appears in the top action bar asset Menu Navigation. Click the Refresh Asset data button to see the following pop-up window for trigger action!

AEM – Final Output of Custom Action Button

Checkout my Adobe AEM-related perficient blogs here