Sunday, February 14, 2021

How To Reuse Editable Template With Multiple Sites — Adobe Experience Manager(AEM)?

Editable templates have been introduced in AEM 6.2, which allows the authors to create and edit templates. Template authors can create and configure templates from the Templates console in the AEM without the help of the development team. Template authors can define the policies, structure, and initial content for the templates that will be shared by all the pages created based on this template.

Editable Templates allow the template-authors to configure the client libraries required to style the websites created based on this template — base/common client libraries are included through page rendering component but site-specific client libraries through template policy configuration.

Image for post

Most of the time we will have a generic template enabled with headers and footers through experience fragment and the container with allowed components that can be managed by Authors while creating the pages, e.g.

Image for post

This will have some constraint while reusing the template with multiple sites

In this tutorial let us see how to enable the editable template with multiple sites that have the different header/footer and styling requirements.

site-specific header/footer:

Most of the time the header and footer are enabled through experience fragment and configured in template level by using experience fragment component so that all pages created based on this template will have the consistent header/footer experience. The issue is while reusing the template with sites that require a localized header and footer, the Experience Fragment core component saves us here, the Experience Fragment core component enables the support for localized fragment content

"Supports references for localized content: if the component is defined in a template and if the fragment is part of a localized structure below /content/experience-fragments that follows the same patterns as the site below /content, the fragment with the same localization (language, blueprint or live copy) as the current page will be rendered".

If the experience fragments and the sites follow the same localized structure, the experience fragment core component uses the localized fragment content based on the site language.

Image for post

The experience fragment components in the template are enabled with default experiment fragment — /content/experience-fragments/my-site/us/en/header/master and /content/experience-fragments/my-site/us/en/footer/master, while accessing the different localized sites the experience fragment core component derives the localized fragment path based on the site structure and applies that to the pages.

e.g while accessing the content from /us/es the following fragments are applied —/content/experience-fragments/my-site/us/es/header/master and /content/experience-fragments/my-site/us/es/footer/master , the same case for other localized sites.

This experience fragment localization feature helps us to reuse the template across different websites that have localized header and footer needs without creating site-specific templates.

site-specific style/theme

Most of the time we may have the need to enable site-specific style/theme for the websites that are built on the same templates. By default Editable template and core components won't support different styles for the websites built on the same template — the client libraries are enabled either in the page rendering component or in the template. The cq:designPath value configured in the site property will not be honored by editable templates and core components, the cq:designPath is more used by the static template.

Let us see a custom approach that will help us to enable site-specific style/themes(client library).

The front end module should generate the site-specific client library that can be configured for individual websites, refer to the link below for the details on enabling site-specific client libraries through the UI frontend module.

The above front end module generates the site-specific client library in the following format

— <site name>.site e.g. site1.site, site2.site

— <site name>.dependencies e.g site1.dependencies, site2.dependencies

As a first step enable the additional property- styleName to the page dialog (if required enable dropdown list with predefined style values, also the property can be enabled in all level to apply different site and page level styles) — the styleName value is equal to the site names used by ui frontend module e.g site1, site2 (use context-aware configuration instead of page properties if required)

{
"jcr:primaryType":"nt:unstructured",
"jcr:title":"Page",
"trackingFeature":"core-components:page:v2",
"helpPath":"https://www.adobe.com/go/aem_cmp_page_v2",
"extraClientlibs":[
"cq.common.wcm",
"core.wcm.components.page.v2.editor",
"cq.wcm.msm.properties",
"granite.contexthub.configuration",
"cq.siteadmin.admin.properties"
],
"mode":"edit",
"sling:resourceType":"cq/gui/components/authoring/dialog",
"content":{
"jcr:primaryType":"nt:unstructured",
"granite:class":"cq-dialog-content-page",
"sling:resourceType":"granite/ui/components/coral/foundation/container",
"items":{
"jcr:primaryType":"nt:unstructured",
"tabs":{
"jcr:primaryType":"nt:unstructured",
"granite:class":"cq-siteadmin-admin-properties-tabs",
"size":"L",
"sling:resourceType":"granite/ui/components/coral/foundation/tabs",
"items":{
"jcr:primaryType":"nt:unstructured",
"basic":{
"jcr:primaryType":"nt:unstructured",
"jcr:title":"Basic",
"sling:resourceType":"granite/ui/components/coral/foundation/fixedcolumns",
"items":{
"jcr:primaryType":"nt:unstructured",
"column":{
"jcr:primaryType":"nt:unstructured",
"sling:resourceType":"granite/ui/components/coral/foundation/container",
"items":{
"jcr:primaryType":"nt:unstructured",
"styleName":{
"jcr:primaryType":"nt:unstructured",
"name":"./styleName",
"fieldLabel":"Style Name",
"sling:orderBefore":"htmlid",
"sling:resourceType":"granite/ui/components/coral/foundation/form/textfield",
"fieldDescription":"Style Name - Site Specific Theme"
}
}
}
}
}
}
}
}
}
}
Image for post

Enable sling model to to return the site specific client library names based on the inherited styleName property passed from customheaderlibs.html and customfooterlibs.html files of the page rendering component.

import javax.inject.Inject;import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.Optional;
@Model(adaptables = {
SlingHttpServletRequest.class
})
public class StyleNameModel {
@Inject @Optional
private String styleName;
public String getStyleNameSite() {return styleName != null ? styleName.concat(".site") : "site1.site";
}
public String getStyleNameDependency() {
return styleName != null ? styleName.concat(".dependencies") : "site1.dependencies";
}
}

Enable the client library categories into the page rendering component(e.g /apps/multisitefrontenddemo/components/page) — customheaderlibs.html and customfooterlibs.html, the inherited style name property is injected to StyleNameModel — the property form current page if not available then from the parent hierarchy

Add the below code to customheaderlibs.html

<sly data-sly-use.style="${'com.multisitefrontenddemo.core.models.StyleNameModel' @styleName=inheritedPageProperties.styleName}"/><sly data-sly-use.clientLib="/libs/granite/sightly/templates/clientlib.html">
<sly data-sly-call="${clientlib.css @ categories=[style.styleNameSite, style.styleNameDependency] }">
</sly>

Add the below code to customfooterlibs.html

<sly data-sly-use.style="${'com.multisitefrontenddemo.core.models.StyleNameModel' @styleName=inheritedPageProperties.styleName}"/><sly data-sly-use.clientLib="/libs/granite/sightly/templates/clientlib.html">
<sly data-sly-call="${clientlib.js @ categories=[style.styleNameSite, style.styleNameDependency] }">
</sly>

Now remove the client library configurations from template policy and enable the required style name to the site root or individual pages

Image for post

Now the site-specific style is applied based on the configuration, the style to the page is applied in the following order

— Apply the style name from the current page

— Apply the style name from any of the available parent page (navigated till root node)

— the default style is applied if no style name configured for the current page or to the parent hierarchy.

The default style is applied while editing the pages through the template editor — the structure and initial content.

The behavior can be changed based on your project need e.g. restrict the style configuration only to the site root node

Demo Project — https://github.com/techforum-repo/youttubedata/tree/master/aem/multisitefrontenddemo

Refer to the below video for more detail





Sunday, February 7, 2021

Multi Tenancy Theme Support For UI Frontend Module - Adobe Experience Manager(AEM)

The AEM Project Archetype includes an optional, dedicated front-end build mechanism based on Webpack. The ui.frontend module is the central location for all of the project’s front-end resources including JavaScript and CSS files. The client library is generated through aem-clientlib-generator npm module and placed under ui.apps module during the build process.

Image for post

The current module structure shown below supports the front-end components for single-tenant/theme, the module can be duplicated to support multiple tenants but that will create constraints to manage also impact the overall deployment timeline.

Image for post

In this tutorial let us see how to enable the front end module to support multiple tenants/themes. The new module structure is as below.

Image for post

The final client libraries are generated in the below structure

Create the theme/tenant specific folders under (copy the default content under webpack to the site-specific folders)

/ui.frontend/src/main/webpack/site1, /ui.frontend/src/main/webpack/site2 etc

The default webpack.common.js is enabled to support a single entry(tenant), enable the required configurations to support additional tenants(e.g site1, site2, etc)

Enable the static HTML for testing different sites through webpack server by default the configurations are enabled to support a single tenant.

Enable the required configurations to generate the client library for different websites — site1.site, site1.dependencies, site2.site, site2.dependencies, etc

The default client library path can be overridden using outputPath e.g. outputPath: CLIENTLIB_DIR+’/clientlib-site2'

Now the style for different sites can be tested through a static index.html file

The site-specific static files can be accessed through

The browser tabs will be reloaded on style/script changes.

The site-specific client libraries can be enabled through editable template policy or through page rendering components.

Image for post

The sample project is also enabled with webpack watch and aemfed to deploy/test the style/script changes quickly.

Now the AEM pages can be accessed through the proxy — the changes will be synced to AEM and the browser tab reloads, the webpack proxy inject the local CSS and js files to the AEM pages.

http://localhost:3000/content/multisitefrontenddemo/us/en.html

To deploy the final changes to AEM, navigate to project root folder and execute mvn -PautoInstallSinglePackage clean install — compile the front end module and generate the client libraries to ui-apps and deploy the complete package to AEM

The style/scripts specific to multiple tenants can be managed through a single front-end module, the site specific client libraries are generated and deployed through Maven build to the AEM server. The Webpack server, watch, aemsync, browsersync, aemfed tools can be used for testing the frontend changes quickly from the local machine.

Refer to https://github.com/techforum-repo/youttubedata/blob/master/aem/multisitefrontenddemo for sample front end module enabled with multi-site support( demo is built based on archetype 24 and AEM as cloud service SDK).


References:

https://experienceleague.adobe.com/docs/experience-manager-core-components/using/developing/archetype/uifrontend.html?lang=en#clientlibs



Tuesday, December 15, 2020

AEM Dispatcher Configurations — symlinks


The AMS 2.0 Dispatcher standard/AEM as Cloud Dispatcher configurations enable modularized dispatcher configurations and also simplify and remove the duplicate configuration.

As shown in the below diagram, one of the major changes is using symlinks to avoid duplication of farm files and host configurations. In earlier versions, the farm and vhost files are duplicated under available and enabled folders, this leads to duplication and overhead for management — the changes should be applied in two different files.

In AMS 2.0/AEM as Cloud Service dispatcher configurations, the files are managed through symlinks, the actual file is inside the available folder, and symlink created to the file under the enabled folder. The symlinks are going to be relative to the available folder — ../available_vhosts/test.vhost

e.g

~/dispatcher/src/conf.d/available_vhots/test.vhost — original
~/dispatcher/src/conf.d/enabled_vhosts/test.vhost — symlink

~/dispatcher/src/conf.dispatcher.d/available_farms/test_farm.any— original
~/dispatcher/src/conf.dispatcher.d/enabled_farms/test_farm.any — symlink




The challenge here is enabling the symlink, we had challenges in managing the symlinks as developer use the different OS for their day to day work e.g Windows, Linux, or Mac.

In this tutorial let us see the different approaches to enable the symlinks.

Linux/Mac:


The ln command can be used in Linux/Mac to create the symlinks -The ln command is a standard Unix command utility used to create a hard link or a symbolic link to an existing file or directory.

ln [OPTION]... [-T] TARGET LINK_NAME


Execute the below commands

cd /home/albin/dispatcher/src/conf.d/enabled_vhosts
ln -sfv ../available_vhosts/001_www_example_com.vhost 001_www_example_com.vhost
ls -lrm 001_www_example_com.vhost



Windows:


In Windows, multiple options can be used to create the symlinks


WSL2 — Windows Sub Syetsm For Linux


Windows Subsystem for Linux is a compatibility layer for running Linux binary executables natively on Windows 10 and Windows Server 2019. In May 2019, WSL 2 was announced, introducing important changes such as a real Linux kernel, through a subset of Hyper-V features.

If the WSL feature is already enabled you should be able to execute the Linux commands in Windows System

Refer to the below video for details on enabling WSL2 in Windows



Execute the below commands

cd /mnt/c/Albin/blogData/aem/repo/dispatcher/src/conf.d/enabled_vhosts
ln -sfv ../available_vhosts/001_www_example_com.vhost 001_www_example_com.vhost
ls -l



The 0 KB file is created now with the symlink details.

Remove symlink(execute from enabled_vhosts)

rm 001_www_example_com.vhost



Git Bash


Git Bash allows us to enable the symlink, execute the below commands through an administrator or elevated access

export MSYS=winsymlinks:nativestrict
cd /c/Albin/blogData/aem/repo/dispatcher/src/conf.d/enabled_vhosts
ln -sfv ../available_vhosts/001_www_example_com.vhost 001_www_example_com.vhost
ls -l
rm 001_www_example_com.vhost


mklink


Windows utility creates a directory or files symbolic or hard link in Windows

Syntax — mklink <link> <target>


Execute the below commands(execute through an administrator or elevated access)

cd C:\Albin\blogData\aem\repo\dispatcher\src\conf.d\enabled_vhosts
mklink 001_www_example_com.vhost "../available_vhosts/001_www_example_com.vhost"
dir
del 001_www_example_com.vhost




The mklink utility was working without any issues for me, able to deploy the dispatcher configurations to AMS server with the symlink created through mklink in windows machine(easy to use the utility in windows and not require any additional configurations — my recommended option for windows)

The git commit adds the symlinks also to the remote repository so whenever the configurations are checked out for the deployment the symlinks also restored.

After git checkout, the symlinks created and committed under Linux appear as plain text files that contain the link text under Windows.




To recreate the symlinks after checkout, enable the core symlink support globally or on git clone(both options through an administrator or with elevated access).

git config --global core.symlinks true

or

git clone -c core.symlinks=true https://git.xxx.com/xxx/xxx/

Now the symlinks will be recreated after checkout and you will see the files with 0KB.

Feel Free to provide your comments



Friday, December 11, 2020

Sync External Git Repository to Cloud Manager Repository

In the earlier tutorial, we have discussed the basic details on Cloud Manager and how to use CM API/Events to trigger the notification to Social Channel(Teams).

As discussed earlier, the Cloud Manager enables own Git repository to manage the deployment to different environments, for simple projects the Cloud Manager enabled git repository should be enough to manage the day to day development activities of the project. But for complex projects, the feature-based easy to manage repository(CM don’t provide any UI to manage the branches) should be required to manage the day to day development activities. In that case, the customer-specific repository can be used used to manage the day-to-day development activities and the code can be merged to Cloud Manager Git repository branches once ready for deployment.

As a manual process, the branches between the local repository and Cloud Manager repository can be synced by the developer by using a set of Git commands

Sync Local Repository Dev-Branch to CM Repository Dev-Branch, first commit the changes to the local Dev branch then push the changes to CM repository(Dev branch). Execute the below command from the local repository folder.

git remote add sync https://username:[email protected]/xxxx/xxxx/
git checkout dev
git pull
git push sync dev


But this will create overhead for the development team also additional effort to manage the local and CM repositories, In this tutorial let us see the approach to auto-sync the branches from the local repository to the remote repository.

Sync Flow:




The developers will continue to use the local branches for development, the existing development flow can be used, on commit to different branches e.g Dev, UAT, etc the changes will be auto merged to the corresponding CM repository branches e.g. Dev, UAT, etc through a local pipeline — I am using Bitbucket pipeline for sync but different pipelines e.g. Jenkins can be used. The production version of the code is always maintained in the local master branch. The different CM pipelines are triggered based on the commit to the corresponding branch or triggered manually, the pipeline deploys the code to the corresponding environment after conducting the required validations and quality check. This flow helps us to minimize the manual touchpoints across the deployment flow — modify the flow based on your use case.

Configure Sync:


Refer A BitBucket CI/CD Pipeline to Sync Branches With GitHub to sync branches between two different repositories — I am using Bitbucket pipeline to sync branches from bit bucket repository to Github repository(any other pipelines e.g Jenkins can also be used to sync).

The Cloud Manager repository won't support adding SSH keys for remote integrations, the integrations should be enabled through the user name and access password — git remote add sync https://username:password@git.cloudmanager.adobe.com/xxxx/xxxx/ (store the credential in pipeline/repository variable and refer in yml file e.g https://$CM_SYNC_USER_NAME:$CM_SYNC_USER_ACCESS_TOKEN@git.cloudmanager.adobe.com/xxx/xxx/)

Updated pipeline configuration(store bitbucket-pipelines.yml under the root folder of every branch)

The concern here using the credential of a regular user for integration — this will not be the right option as the individual credentials will be configured to enable the sync between the local repository and remote repository. To overcome this create a generic Adobe user and enable the required CM roles(Developer Role) and use this username and access password for integration.

As a first step create an adobe user through account.adobe.com ( e.g [email protected]— not necessary to have a valid email but use the organization domain(xxx)



Add the user(e.g [email protected]) under “Cloud Manager — Developer Role” through the admin console





Login to https://my.cloudmanager.adobe.com/ and Generate an access password through the Manage Git option.




Now the user name and the access password can be used to enable the sync.

Using the local repository provides the required flexibility for day to day development activities, the auto-sync between the local repository branches and CM git repository reduces the manual touch in the CI/CD flow.