Tuesday 29 November 2016

External redirect for an AEM page

In order to redirect an AEM page to external url follow the below steps
  • Create a page with sling:resourceType=foundation/components/redirect. 
  • Goto page properties, assign the redirect url
  • Open the page in either publisher or in author instance append ?wcmmode=disabled. Now page redirect to external url.

Using the Java Query Object Model within Adobe Experience Manager

You can create an AEM application that searches the CQ repository for JCR data and displays results to the end user. For example, you can search CQ pages under a specific repository node (for example, nodes under /content) and look for a specific search term. All content that satisfy the search criteria are included in the search results. To search the Experience Manager repository, you use the JCR Query Object Model (JQOM) API. For information about the API, see Interface QueryObjectModel
JQOM is an AEM query language that is like ‘prepared statements’ and SQL2 is like ‘statements’ in JDBC queries. For example, a use case may require all JCR nodes from ‘Geometrixx’ which has the property pageTitle.

Selector selector = qf.selector("cq:PageContent", "s"); 
Constraint constriant = qf.descendantNode("s", "/content/geometrixx");
constriant = qf.and(constriant, qf.propertyExistence("s", "pageTitle"));
QueryObjectModel qm = qf.createQuery(selector, constriant, null, null);
QueryObjectModelFactory gets the instance of the JCR Object Model.  The Selector is used to set the type of node that the query needs to look at. The s parameter is simply an alias. Constraint is used to add all the constraints which is like a where condition into the query model. Finally a query is created with the selector and constraint that captures the response as a QueryObjectModel. 

JQOM Results

Read More

Friday 25 November 2016

Uncaught TypeError: Cannot read property 'extend' of undefined' - cq.extend

When we try to create a custom widget we extend the widget from the CompositeField. When we render this custom widget script, we may get error like "Uncaught TypeError: Cannot read property 'extend' of undefined' ".

Solution:

When custom component adds a dependency on cq.widgets it will always fail in the publisher.

When we include below statement in clientlibs, this script is also evaluated in the publisher for the purpose of assigning the variable, even if it is never used.

Search.CustomWidget = CQ.Ext.extend(CQ.form.CompositeField, { 
You can make this work by having the authoring part completely separated from the rest of the component, so that the publisher never tries to execute any part of it. For example by using a different category which is only included using WCMMode.fromRequest(request) != WCMMode.DISABLED

Thursday 24 November 2016

Adobe AEM - Cross Domain AJAX Request

A common problem for developers is a browser to refuse access to a remote resource. Usually, this happens when you execute AJAX cross domain request using jQuery or plain XMLHttpRequest. As result is that the AJAX request is not performed and data are not retrieved.


jquery ajax cross domain
Figure 1. The same-origin policy restriction in effect

ERROR:

XMLHttpRequest cannot load http://remote-domain/url. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://localhost:5433' is therefore not allowed access

SAME-ORIGIN POLICY

This is a security policy who defines the rules of how a web page can access an external resource (e.g. fonts, AJAX requests). Under the same-origin policy, web browsers do not permit a web page to access resources who origin differ than that of the current page. The origin is considered to be different when the scheme, hostname or port of the resource do not match that of the page. Overcoming the limitations of same-origin security policy is possible using a technique called Cross-origin resource sharing or simply CORS.

Wednesday 23 November 2016

Hooking to Cache Invalidation in AEM 6.1

If you need to implement custom cache invalidation based on some event, you can write a workflow service or servlet (Which ever suits best to your requirement). In this Java class you can write code to handle this. The OOTB Flush Service can be utilized for this purpose-

    import com.adobe.cq.social.ugcbase.dispatcher.api.FlushService;
    @Reference
    FlushService flushService;

Then you can simply request a cache invalidation for the corresponding page-
flushService.sendFlushUrl("userId",
                    FlushService.FlushType.IMMEDIATE_FLUSH, resourceUrl,
                    FlushService.RefetchType.IMMEDIATE_REFETCH,
                    new String[] { resourceUrl });

Here resourceUrl is the path of the page without html extension.

In order to make the flush service working properly, create flush agents for each dispathcer with following additional settings-

1. Set the Request method - "POST" because this service expects a POST method flush agent.
2. Send additional parameter for "resource only replication" 

-To debug any issues you can add a custom logger from OSGi configurations console on this package-
com.adobe.cq.social.ugcbase.dispatcher.impl.FlushServiceImpl

ACM AEM Commons tool also provides options to help implementing cache invalidation for you through OSGi configurations.

Image upload issue in Internet Explorer from Author Dispatcher

If you came to happen to  face issue with image upload from AEM author through dispatcher, this article may help you. You may face issue when you upload an image from image component or uploading images through Assets dashboard. Performing the following things may fix this issue for you-

1. Increase the Upload Buffer Size from OSGi Console (http://host:port/system/console/configMgr)
   Configure service - "Apache Felix Jetty Based Http Service"
Increase following two parameters- "Request Buffer Size" and "Response Buffer Size" to 65536  or a reasonable limit as per your application requirement.

   

Input Field validation

AEM6 uses Coral UI framework for touch UI rendering. So if you need to implement field validation, you should register a validator on that field supported by Coral UI. Refer the below example-

Sample code to implement mandatory validation on an Input field with trim function-

1. In your component field properties add two new properties- validation-trim (Set value to true) and trimMessage (Set value to the validation message). Look at the screenshot below-


AEM Tag field validation

AEM6 uses Coral UI framework for touch UI rendering. So if you need to implement field validation, you should register a validator on that field supported by Coral UI.

To implement validation on tag input field you can use the following sample code.

1. In your tagspicker input field add one property- tagsMessage (Set value to the validation message). Look at the screenshot below-


Hide a field based on the User Role in a Dialog

If you need to hide a field in a dialog based on User role i.e. user group, you can do it through an Ajax call-

$.ajax({
    type: "GET",
    url: "/libs/granite/security/currentuser.json.rw.userprops.json?props=declaredMemberOf",
    cache: false
}).done(function(data, textStatus, jqXHR) {
    var isMember = false;
    /* The Group ID for which the widget has to be disabled */
    var groupId = "site-editor";
    var membershipInfo = data.declaredMemberOf;
    if (membershipInfo) {
        for (var membershipIdx = 0; membershipIdx < membershipInfo.length; membershipIdx++) {
            if (membershipInfo[membershipIdx].authorizableId == groupId) {
                isMember = true;
                break;
            }
        }
    }
    /*This example disables the Hide in Navigation checkbox on Page Properties dialog of the page*/
    if (isMember) {
        $('[name="./hideInNav"]').prop('disabled', 'true');
    }
});

Ajax call should be invoked at "dialog-ready" event of document. You need to put the above code in a clientlibs JS file with category "cq.authoring.dialog". 

Restriction on size in multifield

Following sample code can be used to put size restriction in a multifield -

1. Add a new property- "maxlinksallowed" to the multifield node in your cq:dialog. Refer the screenshot below-




2. Use this JS code-

$(document).on("dialog-ready", function () {
$(".js-coral-Multifield-add").click(function() {
    var field = $(this).parent();
    var size = field.attr("data-maxlinksallowed");
    if (size) {
        var ui = $(window).adaptTo("foundation-ui");
        var totalLinkCount = $(this).prev('ol').children('li').length;
        if (totalLinkCount >= size) {
            ui.alert("Warning", "Maximum " + size + " links are allowed!", "notice");
            return false;
        }
    }
});
});

You need to put the above code in a clientlibs JS file with category "cq.authoring.dialog".

Auto populate title field based on Path browser path selection

To auto populate page title in a title field based on path value selected in path browser field, you can refer following code snippet-

$(document).ready(function() {
    var textInput;
    var textToInput;
    var inputText;
    var ui = $(window).adaptTo("foundation-ui");
    var s = $(document).on("dialog-ready", function() {
        textInput = $('.js-coral-pathbrowser-button').parent().prev();
        textToInput = $('.js-coral-pathbrowser-button').parent().parent().parent().parent().parent().parent().parent().find("input[name='./page'].coral-Form-field");
      
        $(document).on("click", ".js-coral-pathbrowser-confirm", function() {
            setTimeout(function() {
                inputText = $(textInput).val();
                //$(textToInput).val(inputText.substring(inputText.lastIndexOf("/")+1).split('.')[0]);
                if (!$(textToInput).val()) {
                    $(textToInput).val(inputText.substring(inputText.lastIndexOf("/") + 1));
                }
            }, 1000);
        })
        $(document).on('click', '.js-coral-pathbrowser-button', function() {
            textInput = $(this).parent().prev();
            textToInput = $(this).parent().parent().parent().parent().parent().parent().parent().find("input[name='./page'].coral-Form-field");
        })
        $(document).on("change", " .js-coral-pathbrowser-input", function() {
            inputText = $(this).val();
            if (inputText.indexOf("/content") > 0) {
                textToInput = $(this).parent().parent().parent().parent().parent().parent().parent().find("input[name='./page'].coral-Form-field");
                
                if (!$(textToInput).val()) {
                    $(textToInput).val(inputText.substring(inputText.lastIndexOf("/") + 1));
                }
            }
        });
    });
});

Change the field selector input[name='./page'].coral-Form-field as per your dialog configuration where the title value is to be auto populated.

You need to put the above code in a clientlibs JS file with category "cq.authoring.dialog".

Prefill text field with http, mailto

You can use the below sample code to prefill an input text field with URL/ EMail prefixes based on user input. It prefixes mailto syntax if user input forms an email pattern.

(function($) {
    "use strict";
    $(document).on("change", ".js-coral-pathbrowser-input", function(e) {
        // If it's a relative path - do nothing
        if (this.value.indexOf('/') != 0) {
            // It's not a relative path - treat it as either a mail address or webb address
            if ((this.value.indexOf('@') > -1) && (this.value.indexOf('mailto:') == -1)) {
                // It's a mail address
                $(this).prop("value", 'mailto:' + this.value);
            } else if ((this.value.indexOf('http://') == -1) && (this.value.indexOf('https://') == -1) && (this.value != "") && (this.value.indexOf('mailto:') == -1) && ($(this).parent().parent().hasClass('js-cq-TagsPickerField') == false)) {
                // It's a webb address
                $(this).prop("value", 'http://' + this.value);
            }
        }
        if (this.value.indexOf(' ') >= 0) {
            $(this).prop("value", $.trim(this.value));
            $(this).prop("value", this.value.replace(new RegExp(' ', 'g'), '%20'));
        }
    });
})(Granite.$);

You need to put the above code in a clientlibs JS file with category "cq.authoring.dialog".

RTE validator plugin

You can use the belowJS for sample validator plugin for Rich text editor-

CUI.rte.plugins.ValidatorPlugin = new Class({

    toString: "ValidatorPlugin",

    extend: CUI.rte.plugins.Plugin,

    /**
     * RTE plugin for maximun length check. If maxlength is set to 0 the validation will not happen. Set a number 
     * greater than 0 for maxlength property.
     */

    /**
     * @private
     */
    alertShown: false,
    errorEl: null,

Changing Default Favicon of AEM Admin Interface

To change the default favicon of the touch UI admin interface, you need to overlay the /libs/granite/ui/components/foundation/page/favicon/favicon.jsp. Just change this path in your overlayed file and remove the Adobe copyright comment text.

Config cfg = cmp.getConfig();
AttrBuilder attrs = new AttrBuilder(request, xssAPI);
attrs.add("rel", "shortcut icon");
attrs.addHref("href", cfg.get("href", "/libs/granite/core/content/login/favicon.ico"));
// https://en.wikipedia.org/wiki/Favicon#How_to_use
%><link <%= attrs.build() %>>

Change the favicon path highlighted in red above.

Refer the below scrrenshot to overlay the OOTB favicon.jsp-


Tuesday 22 November 2016

AEM Troubleshooting

Sharing few utility tools/ links-

1. To find the generated java files in AEM6.1

Generated JSP JAVA files used to be at -  /var/classes location but AEM6.1 on wards it's no longer present there. This is due to change of repository ClassLoader, now it uses FSClassLoader (File System ClassLoader) for better performance and throughput.

Generated JSP java files are located at- [AEM_INSTALL_DIR]/crx-quickstart/launchpad/felix/bundle[BUNDLE_ID]/data/classes


Source: For details read following article - https://www.danklco.com/posts/2015/06/23/new-apache-sling-fs-classloader-console/

Integration with Translations.com

Translations.com's GlobalLink Project Director integrates with Adobe Experience Manager, providing users with a powerful solution to initiate, automate, control, track, and complete all facets of the translation process.

Refer the following links for more details-
http://www.translations.com/products/globallink-AEM-adaptor,
http://www.translations.com/globallink/partners/adobe.html

Translations.com provide packages (in Zip farmat) which can be installed through AEM package manager. They provide two packages- 

a.       GlobalLink-Adaptor-4.7.6.zip (Check the latest packages from TDC site)

b.      GlobalLink-Workflows-2.1.zip (Check the latest packages from TDC site)

Please note that this is not an open source product and you need to contact to TDC to get these packages. Once packages installed, required page templates will be available. Look at the screenshot below-


Non English Characters not displaying on page

If you are facing issue with display of Non-English characters in your site/ page then you can check following things-


1. Change Request Parameter Encoding 

a. Go to OSGi configuration console (http://localhost:4502/system/console/configMgr) and search for "Apache Sling Request Parameter handling" Service
b. Change Encoding to "UTF-8"



c. If changing this does not work then try restarting AEM after this change.

2. Set Character Encoding to "UTF-8" in Ajax and Servlets.

Sunday 20 November 2016

Implement Translator connector in AEM

The purpose of a Translation Connector in any Content Management System (CMS) is to automate content transmission between CMS and Translation Service Providers making it quicker and cost-effective. Such integration is required when there is a need to maintain multiple languages in CMS. Similarly, for projects in Adobe Experience Manager (AEM) that involves website localization and multilingual SEO management, translation connectors implementation becomes a mandate.
Translation Connectors are simply used to provide an interface between AEM and Translation Management System (TMS), where TMS is nothing but programs which support complex translation tasks.

Using Dynamic Tag Manager in AEM

You can integrate Adobe Experience Manager (AEM) with Adobe Marketing Cloud Activation Core Services (formerly named Dynamic Tag Management). Activation is an Adobe Marketing Cloud Core Service that allows a digital marketer to manage Adobe and third-party tags used for tracking or other analytics purposes. It is done through client-side scripting that injects tag related code throughout the pages of the site. 
You define rules in the Activation web client, as shown in this illustration. 

Thursday 10 November 2016

AEM Deployment with Jenkins

In this article, I am going to explaing how to deploy aem packages using Jenkins, The Jenkins can be used as a continuous integratiion server using which we can deploy packages automatically on either author or publish instances of a AEM development and qa envrionment’s, I dont recommed to install it on production envrionment, we can do lot of things with Jenkins, we can configure notifications when build fails and also using plugin’s we can connect to different version control systems such GIT, SVN etc…
Here, I am going to cover how to deploy AEM builds using Jenkins, the first thing which we  need to do is download and install Jenkins from this link
Once the Jenkins is installed then the next step is go to dashboard of Jenkins and install plugins as part of this I have installed Maven plugin
In the dashboard left navigation there is an item called “Manage Jenkins”click on this, on the Manage Jenkins screen you will find Manage Plugins link

Friday 4 November 2016

Explore Request Log in AEM


Request log in AEM plays a very vital role towards analysis of site performance. For each request and response one entry is written into request.log file, you can analyze this log file to find out maximum time taken by a request to load and then troubleshoot the issue to increase overall site performance.

Extending the Multi Site Manager using the Experience Manager MSM API

You can use the Adobe Experience Manager Muli Site Manager (MSM) API to create a custom service that is invoked when you perform a MSM operation such as create and activate a LiveCopy page. Multi Site Manager enables you to easily manage multiple web sites that share common content. MSM lets you define relations between the sites so that content changes in one site are automatically replicated in other sites.
For example, web sites are often provided in multiple languages for international audiences. When the number of sites in the same language is low (three to five), a manual process for syncronizing content across sites is possible. However, as soon as the number of sites grows or when multiple languages are involved, it becomes more efficient to automate the process.
A service that works with Experience Manager MSM functionality is implemented as an OSGi bundle and uses APIs located in the com.day.cq.wcm.api package.
Note:
For information about MSM functionality, see Multi-Site Management.