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, January 12, 2021

Different Approaches to Block Non-Prod URL’s from search indexing

One of the major SEO concerns while working on the website is blocking the non-production URLs appearing from search engines result(index), the search engines can index the non-prod URL if those URL’s are by mistake linked from live URL’s or exposed through any other external links. The indexing of non-prod URL can cause duplicate content issues that also impact the ranking of live URL’s, the end-user may access the non-production URL instead of the Live URL. This can sometimes lead to a compliance issue if the content bound to compliance and non-live content is exposed to the end-user.

In this tutorial let us discuss the details on how to block the non-prod URLs appearing from search engines.

You can search for site:<domainname> e.g site:www.albinsblog.com to identify whether the specific domain is indexed by the search engine, also you can use some of the third party SEO tools to identify the URL’s indexed in search engines.

Image for post

Let us now see some of the options to block search engines from indexing non-production URL.

HTTP Basic Authentication:

Server-side HTTP Basic Authentication for domains block the search engines from crawling and indexing the domain content. Enable HTTP basic authentication in the web server for the non-prod domains so that the non-prod domains will be blocked for search engines but the live content will be indexed as expected.

Basic Authentication for Apache 2.4 Virtualhost

<Location />
AuthType Basic
AuthBasicProvider file
AuthUserFile /etc/httpd/conf.d/.htpasswd
#create the user through htpasswd, htpasswd -c /etc/httpd/conf.d/.htpasswd testuser
AuthName "Authentication Required"
Require valid-user
</Location>

If the same configuration is shared for different environments, enable the authentication based on the condition(e.g based on an ENV_TYPE environment variable or based on the incoming domain value)

<Location />  <If "'${ENV_TYPE}' =~ m#(dev|uat|stage)#">  
AuthType Basic
AuthBasicProvider file
AuthUserFile /etc/httpd/conf.d/.htpasswd
AuthName "Authentication Required"
Require valid-user
</If>
<Else>
Require all granted
</Else>
</Location>

The basic authentication will create challenge while performance testing the websites in stage or other environments(part of a pipeline or outside ) mainly on the caching behavior — the basic authentications enabled websites are skipped from caching, the workaround is disabling the basic authentication whenever the performance testing is executed in the environment(or execute the testing with basic authentication but the test result will not reflect the live behavior).

IP Restriction:

IP restriction helps you to allow only the known IPs to access the non-production URLs, whitelist the known IP ranges so that the external search engines will not have access to crawl/index the non-production websites but the intended users will be able to access the non-prod websites for testing.

The IP restriction can be enabled in load balancer or web server, the webserver will give more control and flexibility to modify the restrictions whenever required by the development team.

IP restriction configuration for Apache 2.4 Virtualhost

<Location />
<RequireAny>
Require ip xxx.xx.0.0/24
Require ip xxx.xx.0.0/24
</RequireAny>
</Location>

If the same configuration is shared for different environments, enable the IP restriction based on the condition(e.g based on an ENV_TYPE environment variable or based on the incoming domain value)

<Location />
<If "'${ENV_TYPE}' =~ m#(dev|uat|stage)#">
<RequireAny>
Require ip xxx.xx.0.0/24
Require ip xxx.18.0.0/24
</RequireAny>
</If>
</Location>

The main challenge of IP whitelisting is enabling the Whitelist rules while working with distributed teams and the dynamic IP’s involved to access the servers.

Robots Meta Tag:

The robots metatag in the page source can help to keep the non-production URL’s out of the search engine index, enable the required robots meta tag in the page source, you need to apply the required custom logic to enable the meta tag only for non-prod environments.

<meta name="robots" content="noindex, nofollow, noarchive, nosnippet, nocache" />

noindex — Do not show this page in search results.

nofollow — Do not follow the links on this page.

noarchive — Do not show a cached link in search results.

nosnippet — Do not show a text snippet or video preview in the search results for this page

nocache — Same as noarchive, but only used by MSN/Live.

The challenge with this approach is the robots metatag can be applied only for HTML resources, not for different assets — pdf, png, jpg and etc also compare to the above two approaches the search engines should crawl all the individual pages to identify the pages are not enabled for indexing.

Robots Meta Tag HTTP header:

The X-Robots-Tag in the HTTP response header can help to keep the non-production URL’s out of the search engine index, the response header can be enabled for both HTML documents and other assets. Better to add this header to the response of all non-prod resources through webserver e.g Apache.

Configuration for Apache Virtualhost

Header set X-Robots-Tag “noindex, nofollow, noarchive, nosnippet, nocache”

If the same configuration is shared for different environments, enable the header based on the condition(e.g based on an ENV_TYPE environment variable or based on the incoming domain value)

<Location />
<If "'${ENV_TYPE}' =~ m#(dev|uat|stage)#">
Header set X-Robots-Tag "noindex, nofollow, noarchive, nosnippet, nocache"
</if>
</Location>

This approach is very easy to manage compared to enabling the metatag in the page source, the search engines should crawl all the individual pages to identify the pages that are not enabled for indexing.

Robots TXT:

Robots.txt file gives the instruction to search engines not to crawl the websites through Disallow tags but this will not ensure the pages are excluded from the index. The pages blocked by robots.txt can still appear in the index if the page is linked from external sources.

Enable a simple robots.txt in the root of every non-prod websites to block the search engines crawling the non-production URL’s(ensure the same Disallow rules are not enabled for live sites by mistake otherwise it will impact the live site indexing)

User-agent: *
Disallow: /

This approach can be used along with Robots metatag or HTTP header to block the search engines crawling the website content after removing them from the index — if the page is already in the index that should be removed first from the index before blocking the crawling through robots.txt otherwise search engine will not able to crawl and see the metatag or header enabled to the pages.

URL Removal:

The Search engine URL Removal tool can be used to remove the already indexed URL from the search engine(also from cache) if something indexed that shouldn't be indexing e.g non-prod URL.

The Removals tool enables you to temporarily block pages from Google Search results on sites that you own. When a page’s URL is requested for removal, the request is temporary and persists for at least 90 days, meanwhile, anyone of the approach discussed above should be enabled to block the search engines completely from crawling and re-indexing the pages again.

Either specific URL, specific section, or the complete site can be removed from the indexing(the site property should be owner can only perform this activity), for google, this can be performed through google search console

Image for post

The authentication and IP restriction will be the most promising approach to keep non-production URLs safer from search engines, in case these two approaches do not work for your case try enabling Robots Meta Tag HTTP header (use robots metatag if required) from webserver for non-prod domains along with robots.txt blocking the crawling.