ContentPolicyManager is a service that facilitates the management and application of content policies to components. Content policies are configurations that define how components should behave and appear on a page. They help enforce consistency in design, styling, and behavior across different instances of the same component.
So if you want to pick up different value for a component based on different policy attached to a component we can use ContentPolicyManager which gives us the exact value attached to that component in design dialog based on the policy.
Let’s see how we can write the code to get the values
To get the object of the content policy manager we need to have resourceResolver. So in first step I am getting the resource resolver object from the request and later in step two i am getting the current resource . Once we have Content Policy manager we can get the content policy for the current resource which will be specific to ONLY current resource i.e current component. Once I have content policy object we get which ever design dialog property we want to fetch.
Some of the useful APIs of Content Policy are:
Some of the useful APIs of Content Policy Manager are:
NOTE: Above implementations works when you have a policy correctly attaches to a resource i.e component in AEM.
In this article we will try out a custom Coral UI Javascript in AEM and all a functionality to a dropdown where, dropdown will have n number of values and will have Select All and De-select All option as well in the dropdown. This is really helpful when you have lot’s of options in a dropdown and you have to select all and de-select all.
First create a simple AEM dropdown field with multiselect functionality and add some 5-6 options in the dropdown and then add two option with following text and value combination which will help us select all fields in dropdown as well as de-select all fields in dropdown.
NOTE: The name property used in this article is ./country for the dropdown.
Select All option:
text: Select All (You can keep any name as it just shows on the UI). value: all (We are keeping all here because we will be using the same value in JS)
Deselect All option:
text: De-Select All (You can keep any name as it just shows on the UI). value: none (We are keeping none here because we will be using the same value in JS)
Now create a clientlibrary in AEM with category name as cq.myproject.select-deselect. And then attach this clientlib at the cq:dialog node of the component by adding the following property at cq:dialog node.
The Core Components implement several patterns that allow easy customization, from simple styling to advanced functionality reuse.
The Core Components were designed from the beginning to be flexible and extensible. A look at an overview of their architecture reveals where customizations can be made.
Now let’s see how we can customise our AEM Core components business logic. We will try to customise the OOTB image core component. And the customisation that we are going to do is that, if image alt text is not authored, we will pick up the image alt text from the image name.
In above java class we have used the adapter for Image.class and implemented the OOTB functionality for Image Component and using @Self annotation we have created a object of Image class so that we will go ahead and update the image.{{variable}} with model.image.{{variable}} to load the untouched functionality of the OOTB java class. whereas the customized altText here can be referred by model.alt.
No we can go ahead and replace the data-sly-use.image=”com.adobe.cq.wcm.core.components.models.Image” with data-sly-use.model=”com.myproject.core.components.models.ImageComponent”.
Now we can replace the custome altText which was image.alt to model.alt and rest of the image variable with model.image.{{variable}}.
As we all know that when it comes to apply the maxlegth property to a richtext field in AEM, it doesn’t actually work. Because RTE in dialog uses the concept of validation from the Form Validation in Granite UI. And, since, Inline-editing does not behave as a form, it can’t use that validation concept.
So we will make use of Granite UI api to create our custom validator which will help us to add validation of maxlength even on the richtext field in AEM TouchUI.
Create a validator.js in clientlibs with following Jquery: Here we are targeting the richtext-container and inside that container we are targeting a class that we will provide to the richtext field i.e rich-custom using Granite UI Api.
;(function (window, $) {
'use strict';
var RichTextMaxLengthValidation= function () {
var CONST = {
TARGET_GRANITE_UI: '.coral-RichText-editable',
ERROR_MESSAGE: 'Your text length is {0} but character limit is {1}!',
};
function init() {
// register the validator which includes the validate algorithm
$(window).adaptTo('foundation-registry').register('foundation.validation.validator', {
selector: CONST.TARGET_GRANITE_UI,
validate: function (el) {
var $rteField = $(el);
var $field = $rteField.closest('.richtext-container').find('input.rich-custom');
var maxLength = $field.data('maxlength');
var textLength = $rteField.text().trim().length;
if (maxLength && textLength > maxLength) {
return Granite.I18n.get(CONST.ERROR_MESSAGE, [textLength, maxLength]);
}
return null;
}
});
// execute Jquery Validation onKeyUp
$(document).on('keyup', CONST.TARGET_GRANITE_UI, function (e) {
executeJqueryValidation($(this));
});
}
function executeJqueryValidation(el) {
var validationApi = el.adaptTo('foundation-validation');
if (validationApi) {
validationApi.checkValidity();
validationApi.updateUI();
}
}
return {
init: init
}
}();
RichTextMaxLengthValidation.init();
})(window, Granite.$);
2. Now add this validator.js in your js.txt and give the category of your clientlibs as cq.author.maxvalidator
#base=js
validator.js
3. Now once we have our validator.js ready now we will create our AEM dialog which will have a richtext field and we will give two properties to this richtext field in dialog.
maxlength – {String} – 60 //This is the maxlength of the richtext text.
class – {String} – rich-custom //This is the custom class that is used in validator to target the richtext field.
Also we need to add the extraClientlibs property on the cq:dialog to load the custom validator that we created in Step 1.
Following the dialog.xml of one my component where this custom validator is used to restrict the character count based on maxlength.
4. Now go ahead and check the authoring dialog for the maxlength validation for the richtext. It should look something like below:
NOTE: This is tested for AEMaaCs and should work for AEM TouchUI dialogs in other versions too. Also this works perfectly fine with the richtext in multifield too.
If you are looking for an article to create a multifield and fetch it’s data in HTL using just single Java class then you are at the right place. So let’s go ahead and start the creation of a multifield in dialog and then use a java class to fetch the data and then render it on the HTML.
Below is the dialog.xml for the multifield which is of type composite which means the data that you enter in the fields of multifield will be saved in different nodes in content hierarchy i.e, item 0, item 1, and so on.
Now lets create our Model Class and try to fetch the values of the multifield. Also follow the comments mentioned in the Java class to identify how fields are fetched and used.
package com.myproject.core.models;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.injectorspecific.SlingObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Model(adaptables = SlingHttpServletRequest.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class MultiDataFetcher {
private static final Logger log = LoggerFactory.getLogger(SupportCarousel.class);
@SlingObject
private Resource componentResource; //Used to get the current resource of the component
private List<Map<String, String>> details = new ArrayList<>();
public List<Map<String, String>> getMultiDataMap() {
return details;
}
@PostConstruct
protected void init() {
Resource supportPoints = componentResource.getChild("details"); //now we will get the details resource which holds the data in different item node.
if (supportPoints != null) {
for (Resource supportPoint : supportPoints.getChildren()) {
Map<String, String> detailsMap = new HashMap<>();
detailsMap.put("title",
supportPoint.getValueMap().get("title", String.class)); // Saving the property into a map, which will be fetched in HTML.
detailsMap.put("link",
supportPoint.getValueMap().get("link", String.class));
details.add(detailsMap);
}
}
}
}
Now once we are ready with out List which holds the data of the multifield, we will now go ahead and render the data in the HTML