Saturday, December 15, 2018

Different approaches to dynamically include custom scripts into websites - Adobe Experience Manager(AEM)

Different approaches to include custom scripts dynamically into websites - Adobe Experience Manager(AEM)


Sometimes we may need to include scripts dynamically into the websites with out changing the code, this post will explain the different approaches to include the scripts dynamically in the websites without performing the code changes.

Tag Manager:


Inject the custom scripts remotely through tag manager systems, this will provide better management of tags without changing the code.

GTM(Google Tag Manager) - Refer https://www.albinsblog.com/2018/12/how-to-include-dynamic-custom-scripts-website-gtm.html for details on injecting the custom script through GTM.

AdobeLaunch-Refer https://www.albinsblog.com/2018/12/how-to-include-dynamic-custom-scripts-adobe-launch-aem.html for details on injecting the custom script through Adobe Launch.

AEM Cloud configuration:


This approach uses the custom AEM Cloud Configuration to include the dynamic scripts - header and footer to the website

Define a Cloud configuration to enable the header and footer scripts and attach the cloud configuration to the required websites to inject the custom dynamic scripts.

Steps-

In CRXDE Lite, create a new node under /apps:

Name: utilities
Type: nt:folder

Create 2 new nodes under /apps/utilities:

Name: components
Type: sling:Folder
and
Name: templates
Type: sling:Folder

Right click on /apps/utilities/components, create a new component genericscriptpage

Label - genericscriptpage
Title - genericscriptpage

group - .hiddenSuperType-cq/cloudserviceconfigs/components/configpage

Add the below additional properties

cq:defaultView - html
allowedParents - utilities/templates/genericscript

Remove the default genericscriptpage.jsp and create content.jsp file under /apps/utilities/components/genericscriptpage with the following content

<%@page session="false" 
  contentType="text/html"
  pageEncoding="utf-8"%><%
%><%@taglib prefix="cq" uri="http://www.day.com/taglibs/cq/1.0" %><%

%><cq:defineObjects/>
<div>
    <h3>General Script Settings</h3>   
    <ul>
<li><div class="li-bullet"><strong>Head Script: </strong><br><%= xssAPI.encodeForHTML(properties.get("headScript", "")).replaceAll("\\&\\#xa;","<br>") %></div></li>
<li><div class="li-bullet"><strong>Foot Script: </strong><br><%= xssAPI.encodeForHTML(properties.get("footScript", "")).replaceAll("\\&\\#xa;","<br>") %></div></li>
    </ul>
</div>

dynamic-script-aem


Create a new node under/apps/utilities/components/genericscriptpage/:

Name: dialog
Type: cq:Dialog

Properties:

title - Generic Script Configuration
xtype - dialog

Create a new node under/apps/utilities/components/genericscriptpage/dialog:

Name: items
Type: cq:WidgetCollection

Create a new node under /apps/utilities/components/genericscriptpage/dialog/items:

Name: panel
Type: cq:Panel

Properties:

title - Script Configuration
xtype - panel

Create a new node under /apps/utilities/components/genericscriptpage/dialog/items/panel:

Name: items
Type: cq:WidgetCollection

Create a new node headScript under /apps/utilities/components/genericscriptpage/dialog/items/panel/items
Name: headScript
Type: cq:widget

Properties:

fieldLabel - Head Script
name - ./headScript
xtype - textarea

Create a new node footScript under /apps/utilities/components/genericscriptpage/dialog/items/panel/items

Name: footScript
Type: cq:widget

Properties:

fieldLabel - Foot Script
name - ./footScript
xtype - textarea

Create a new component genericscript under /apps/utilities/components

Label - genericscript
Title - genericscript

Remove the default file genericscript.jsp, create a file headScript.jsp and add the below content 

<%--

--%><%@page session="false" 
import="org.apache.sling.api.resource.Resource,
                org.apache.sling.api.resource.ValueMap,
                org.apache.sling.api.resource.ResourceUtil,
                com.day.cq.wcm.webservicesupport.Configuration,
                com.day.cq.wcm.webservicesupport.ConfigurationManager" %><%
%><%@taglib prefix="cq" uri="http://www.day.com/taglibs/cq/1.0" %><%
%><cq:defineObjects/><%

String[] services = pageProperties.getInherited("cq:cloudserviceconfigs", new String[]{});
ConfigurationManager cfgMgr = resource.getResourceResolver().adaptTo(ConfigurationManager.class);
if(cfgMgr != null) {
String scriptCode = null;
Configuration cfg = cfgMgr.getConfiguration("generic-script", services);
if(cfg != null) {
scriptCode = cfg.get("headScript", null);
}

if(scriptCode != null) {

%><%= scriptCode %><%

}
}
%>


Create a file footScript.jsp with following content under /apps/utilities/components/genericscript 

<%--

--%><%@page session="false" 
import="org.apache.sling.api.resource.Resource,
                org.apache.sling.api.resource.ValueMap,
                org.apache.sling.api.resource.ResourceUtil,
                com.day.cq.wcm.webservicesupport.Configuration,
                com.day.cq.wcm.webservicesupport.ConfigurationManager" %><%
%><%@taglib prefix="cq" uri="http://www.day.com/taglibs/cq/1.0" %><%
%><cq:defineObjects/><%

String[] services = pageProperties.getInherited("cq:cloudserviceconfigs", new String[]{});
ConfigurationManager cfgMgr = resource.getResourceResolver().adaptTo(ConfigurationManager.class);
if(cfgMgr != null) {
String scriptCode = null;
Configuration cfg = cfgMgr.getConfiguration("generic-script", services);
if(cfg != null) {
scriptCode = cfg.get("footScript", null);
}

if(scriptCode != null) {

%><%= scriptCode %><%

}
}
%>
dynamic-script-aem



Create a file servicefooterlibs.jsp under /apps/utilities/cloudserviceconfigs/components/servicelibs(create the folders utilities/cloudserviceconfigs/components/servicelibs under /apps)

<%@page session="false"%>
<%
%><%@include file="/libs/foundation/global.jsp" %><%
%><%@page import="org.apache.sling.api.resource.ResourceResolver,
                  org.apache.sling.api.resource.ResourceResolverFactory,
  com.day.cq.wcm.webservicesupport.ConfigurationManager,
                  com.day.cq.wcm.webservicesupport.Service,
  java.util.ArrayList,
                  java.util.Comparator,
    java.util.Iterator,
    java.util.List"%>
                  <%

String[] servicePaths = pageProperties.getInherited("cq:cloudserviceconfigs", new String[] {});
if (servicePaths.length > 0) {
    ResourceResolver resolver = resource.getResourceResolver();
    ConfigurationManager cfgMgr = resolver.adaptTo(ConfigurationManager.class);
    Iterator<Service> services = cfgMgr.getServices(servicePaths, new Comparator<Service>() {
        //sort ascending by inclusionRank
        public int compare(Service s1, Service s2) {
            return s1.getInclusionRank().compareTo(s2.getInclusionRank());
        }
    });

    // scripts 
    List<String> libsScripts = new ArrayList<String>();

    ResourceResolverFactory  resolverFactory = sling.getService(ResourceResolverFactory.class);

    try {
        ResourceResolver adminResolver=resolverFactory.getAdministrativeResourceResolver(null);// Getting getAdministrativeResourceResolver(null) is not recommended, this will work only in Author. Reterive the resolver through Service User

        while (services.hasNext()) {
        Service service = services.next();
            String defaultScript = getDefaultScript(adminResolver, service.getComponentReference());
            if (defaultScript != null) {
            libsScripts.add(defaultScript);
            }


        }
    } catch (Exception re) {
        log.error("Cannot retrieve scripts for inclusion",re);
    } 

      for (String script : libsScripts) {
    %><cq:include script="<%=script%>"/><%
    }
}

%>

<%!
/**
 * Returns the path to the footScript.jsp script if the script 
 * can be found. Otherwise <code>null</code> is returned.
 * 
 * @return Full path to footScript.jsp or <code>null</code>
 */
protected String getDefaultScript(ResourceResolver resolver, String componentPath) {
      if (componentPath != null) {
    Resource compRes = resolver.getResource(componentPath);
    Iterator<Resource> it = resolver.listChildren(compRes);
    while (it.hasNext()) {
        Resource script = it.next();
        if ("footScript.jsp".equalsIgnoreCase(script.getName())) {
            return script.getPath();
        }
    }
    }
    return null;
}
%>


Create a file serviceheaderlibs.jsp under /apps/utilities/cloudserviceconfigs/components/servicelibs

<%@page session="false"%>
<%
%><%@include file="/libs/foundation/global.jsp" %><%
%><%@page import="org.apache.sling.api.resource.ResourceResolver,
                  org.apache.sling.api.resource.ResourceResolverFactory,
  com.day.cq.wcm.webservicesupport.ConfigurationManager,
                  com.day.cq.wcm.webservicesupport.Service,
  java.util.ArrayList,
                  java.util.Comparator,
    java.util.Iterator,
    java.util.List"%>
                  <%

String[] servicePaths = pageProperties.getInherited("cq:cloudserviceconfigs", new String[] {});
if (servicePaths.length > 0) {
    ResourceResolver resolver = resource.getResourceResolver();
    ConfigurationManager cfgMgr = resolver.adaptTo(ConfigurationManager.class);
    Iterator<Service> services = cfgMgr.getServices(servicePaths, new Comparator<Service>() {
        //sort ascending by inclusionRank
        public int compare(Service s1, Service s2) {
            return s1.getInclusionRank().compareTo(s2.getInclusionRank());
        }
    });

    // scripts 
    List<String> libsScripts = new ArrayList<String>();

    ResourceResolverFactory  resolverFactory = sling.getService(ResourceResolverFactory.class);

    try {
        ResourceResolver adminResolver=resolverFactory.getAdministrativeResourceResolver(null);// Getting getAdministrativeResourceResolver(null) is not recommended, this will work only in Author. Reterive the resolver through Service User

        while (services.hasNext()) {
        Service service = services.next();
            String defaultScript = getDefaultScript(adminResolver, service.getComponentReference());
            if (defaultScript != null) {
            libsScripts.add(defaultScript);
            }


        }
    } catch (Exception re) {
        log.error("Cannot retrieve scripts for inclusion",re);
    } 

      for (String script : libsScripts) {
    %><cq:include script="<%=script%>"/><%
    }
}

%>

<%!
/**
 * Returns the path to the headScript.jsp script if the script 
 * can be found. Otherwise <code>null</code> is returned.
 * 
 * @return Full path to headScript.jsp or <code>null</code>
 */
protected String getDefaultScript(ResourceResolver resolver, String componentPath) {
      if (componentPath != null) {
    Resource compRes = resolver.getResource(componentPath);
    Iterator<Resource> it = resolver.listChildren(compRes);
    while (it.hasNext()) {
        Resource script = it.next();
        if ("headScript.jsp".equalsIgnoreCase(script.getName())) {
            return script.getPath();
        }
    }
    }
    return null;
}
%>
dynamic-script-aem

Create a template genericscript under /apps/utilities/templates

Label - genericscript
title - Generic Script Configuration
description - Generic Script Configuration
resourceType - cq/cloudserviceconfigs/templates/configpage
allowedChildren - utilities/components/genericscriptpage
allowedPaths - /etc/cloudservices/generic-script(/.*)?

dynamic-scripts-aem


Add below properties to /apps/utilities/templates/genericscript/jcr:content

cq:cloudservicename - genericscript
sling:resourceType - /apps/utilities/components/genericscriptpage(modify the existing value)
thumbnailPath - /apps/utilities/templates/genericscript/thumbnail.png

dynamic-script-aem


Upload thumbnail.png with required icon under /apps/utilities/templates/genericscript


Create a page with name generic-script under /etc/cloudservices/

Add the below properties to /etc/cloudservices/generic-script/jcr:content

componentReference - utilities/components/genericscript
cq:template - /libs/cq/cloudserviceconfigs/templates/servicepage
jcr:description - Generic Script Configuration
sling:resourceType - cq/cloudserviceconfigs/components/servicepage
thumbnailPath - /apps/utilities/templates/genericscript/thumbnail.png

dynamic-script-aem


dynamic-script-aem


Create a configuration page(sample-script) under /etc/cloudservices/generic-script

dynamic-scripts-aem



dynamic-scripts-aem


Add the below line to the header file of your page rendering component - this will displays the header script configured in the generic-script cloud configuration to head section

<cq:include script="/apps/utilities/cloudserviceconfigs/components/servicelibs/serviceheaderlibs.jsp"/> - JSP

<sly data-sly-include="/apps/utilities/cloudserviceconfigs/components/servicelibs/serviceheaderlibs.jsp" /> - Sightly HTML

dynamic-script-aem


Add the below line to the footer file of your page rendering component - this will displays the footer script configured in the generic-script cloud configuration to the end of body section
<cq:include script="/apps/utilities/cloudserviceconfigs/components/servicelibs/servicefooterlibs.jsp"/> - JSP

<sly data-sly-include="/apps/utilities/cloudserviceconfigs/components/servicelibs/servicefooterlibs.jsp" /> - Sightly HTML

dynamic-script-aem


Configure the cloud configuration in parent page properties

dynamic-script-aem


Now the website displays the header and footer scripts

dynamic-script-aem

dynamic-script-aem

AEM Custom Component:


In this approach define a custom component and include that wherever required in the page to inject the custom scripts and html dynamically.

Steps- 

Create a new component

Name: OpenHtml
Title: OpenHtml
Group - Add the required component group name e.g We.Retail

Add the below additional properties

allowedParents String[] */parsys


Define Dialog

Right click the component /apps/weretail/components/content/OpenHtml and select Create, Create Node.

Enter the following values:

Name: cq:dialog
Type: nt:unstructured

Add the below properties

jcr:title - Open Html/Script
sling:resourceType - cq/gui/components/authoring/dialog

Click on the following node: /apps/weretail/components/content/OpenHtml/cq:dialog

Right click and select Create, Create Node. Enter the following values:

Name: content
Type: nt:unstructured

Add the below properties

sling:resourceType - granite/ui/components/coral/foundation/fixedcolumns

Click on the following node - /apps/weretail/components/content/OpenHtml/cq:dialog/content

Right click and select Create, Create Node. Enter the following values:

Name: items
Type: nt:unstructured

Click on the following node: /apps/weretail/components/content/OpenHtml/cq:dialog/content/items

Right click and select Create, Create Node. Enter the following values:

Name: column
Type: nt:unstructured

Add the below properties

sling:resourceType - granite/ui/components/coral/foundation/container

Click the following node - /apps/weretail/components/content/OpenHtml/cq:dialog/content/items/column

Right click and select Create, Create Node. Enter the following values:

Name: items
Type: nt:unstructured

Click the following node: /apps/weretail/components/content/OpenHtml/cq:dialog/content/items/column/items

Right click and select Create, Create Node. Enter the following values:

Name:OpenHtml
Type: nt:unstructured

Add the below properties

fieldLabel - Enter the valid Html/Script
fieldDescription - Enter valid html and/or script. Ensure all tags are closed
name - ./openHtml
sling:resourceType - granite/ui/components/foundation/form/textarea

Add the following content into OpenHtml.jsp

<%@include file="/libs/foundation/global.jsp"%>
<%@page
import="org.apache.sling.api.resource.*,
                  com.day.cq.wcm.api.WCMMode"%>
<%@page session="false" %><%

request.setAttribute("isEdit", WCMMode.fromRequest(slingRequest) == WCMMode.EDIT);
%>

<c:if test="${(isEdit) }">
    <p>
       Open HTML
    </p>
</c:if>
<!-- Open HTML component -->
${properties.openHtml}
<!-- end of Open HTML component -->


Drag and drop the component to the parsys on required website and configure the script - make sure the script/html is valid and all the tags are closed.

In Edit mode the place holder string "Open Html" is displayed

dynamic-script-aem-component


dynamic-script-aem-component
Now the html content and the script is included into the website page


dynamic-script-aem-component



No comments:

Post a Comment