Friday, September 25, 2020

How to enable image maps for Adobe Experience Manager(AEM) assets? | Launch Map in Adobe Experience Manager

  • An image map is an image with clickable areas. The areas are defined with one or more <area> tags. The HTML <map> tag defines an image map.

    <img src="workplace.jpg" alt="Workplace" usemap="#workmap"><map name="workmap">
    <area shape="rect" coords="34,44,270,350" alt="Computer" href="computer.htm">
    <area shape="rect" coords="290,172,333,250" alt="Phone" href="phone.htm">
    <area shape="circle" coords="337,300,44" alt="Coffee" href="coffee.htm">
    </map>

    The idea behind an image map is that you should be able to perform different actions depending on where in the image you click.

    In this tutorial let us see how to enable image maps for AEM assets through the Image core component.

    AEM enables the support to define the image map for assets along with Image Core components, the image maps can be defined through the Asset UI and the Image Core components enable the map configurations to output HTML

    To enable the image map, edit the image through Asset UI

    Image for post

    Click on Launch Map — Launch Map feature(AEM 6.3 with Service Pack 3 or AEM 6.4 with Service Pack 2 or other higher versions)along with core component(2.1.0+) enables the support to configure the image map — clickable areas to the assets.

    Image for post

    Now you should be able to enable the Rectangular or Circular Map

    Image for post

    Define the URL path, Alt Text, and the Target for each Map’s

    Image for post

    The image map details are stored to the repository upon saving

    Image for post

    The Image core component can be used to author the image into the pages, the image core component(https://github.com/adobe/aem-core-wcm-components/blob/master/content/src/content/jcr_root/apps/core/wcm/components/image/v2/image/image.html) generates the image map HTML based on the configuration enabled by the author.

    <img src="${image.src}" class="cmp-image__image" itemprop="contentUrl" data-cmp-hook-image="image" data-sly-attribute.usemap="${image.areas ? usemap : ''}"                 alt="${image.alt}" title="${image.displayPopupTitle && image.title}"/><map data-sly-test="${image.areas}" data-sly-list.area="${image.areas}" name="${resource.path}" data-cmp-hook-image="map">                
    <area shape="${area.shape}" coords="${area.coordinates}" href="${area.href}" target="${area.target}" alt="${area.alt}" data-cmp-hook-image="area" data-cmp-relcoords="${area.relativeCoordinates}">
    </map>

    This will generate the required HTML output and enable the clickable area based on the configuration that can direct the users to different URLs based on the area where they clicked, redirect the user to a specific URL based on where the user clicked.

    Image for post

    This is the useful feature and can be used by authors to define the image maps through visual editor, AEM internally calculates the coordinates for the selected area and assign the target URL.

    The image map configuration changes will not reflect automatically to the images already referenced in the pages, the images should be reconfigured into the pages to reflect the latest image map configurations.

    Another observation is, in some cases, the calculated image coordinates are not working as expected — the required HTML is enabled but the areas are not clickable in the UI.



Friday, September 11, 2020

Adobe Experience Manager: Reporting on User’s Last Login Date

 In this tutorial let us see the details on how to build a custom user report in AEM to get the user profile data along with last login details.

AEM won’t provide any OOTB feature to track the last login details of the users — timestamp of the user’s login.

Sometimes we may have the requirement to report the last login timestamp of the users for auditing purposes e.g identify the users who are not login to the system for the last 1 month, identify the inactive users, etc

This can be achieved by enabling a Custom AuthenticationInfoPostProcessor to capture the last login timestamp and building a custom ACS AEM Commons report to fetch the required user profile data along with the last login timestamp.

As a first step define a custom AuthenticationInfoPostProcessor component to update the last login timestamp to the user profile.

AuthenticationInfoPostProcessor

AuthenticationInfoPostProcessor allows bundles to modify the AuthenticationInfo object after authentication has been performed.

AuthenticationHandler#extractCredentials invokes AuthenticationInfoPostProcessor#postProcess with AuthenticationInfo on successful authentication. The “postProcess” can modify the AuthenticationInfo or perform other operations based on the requirement in our case updating the user profile with login timestamp.

Let us enable a custom AuthenticationInfoPostProcessor that will update the user profile with the last login timestamp.


Enable a service user with the name “custom-user-manager” and provide the jcr:read/jcr:write access to /home/users also enable the user service mapping

Image for post
Image for post

Now the user profile will be updated with the login timestamp on every login to the custom property “lastloggedin”.

Image for post

Let us now build a custom ACS AEM Commons report to fetch the basic user data

Tools →ACS AEM Commons →Reports

Add a new Report with the name “user-report” (I am generating this in AEM as Cloud Author instance with the latest — 4.8.0 ACS AEM Commons package)

Image for post

Edit the report and add the “JCR Query Report configuration” component in the Configuration section.

Query — this excludes the system-specific users, modify the query based on your requirement to fetch the report for different scenarios.

SELECT * FROM [rep:User] AS user WHERE ISDESCENDANTNODE([/home/users]) AND NOT ISDESCENDANTNODE([/home/users/system/cq:services/internal]) AND NOT ISDESCENDANTNODE([/home/users/system/acs-commons]) AND NOT ISDESCENDANTNODE([/home/users/system]) AND NOT ISDESCENDANTNODE([/home/users/system/translation])

Query Language — JCR SQL2

Page Size -50

Image for post

Now configure the required fields including “lastloggedin” under Result Columns with component Type “ACS Commons Report Builder Text Column”

Image for post

Now open the report and click on “Execute Report”, the report can be downloaded as a CSV file if required — the report now includes the last login timestamp of the users.

Image for post

The same report can also be generated through Tools →ACS AEM Commons →User to CSV Exporter, this report includes the additional details like “group names” but includes all the users in the system, the report can be downloaded as a CSV file.

Image for post

Even the query builder can be used to identify the user’s login to the system within a specific time( the parameters can be modified to fetch the data for different scenarios)

p.hits=selective
p.limit=-1
path=/home/users
type=rep:User
1_relativedaterange.property=profile/lastloggedin
1__relativedaterange.lowerBound=-1M
orderby=@profile/lastloggedin
p.properties=profile/lastloggedin profile/givenName profile/familyName profile/email rep:authorizableId

The 1__relativedaterange.lowerBound value can be changed based on your requirement to 1s 2m 3h 4d 5w 1M 1y

http://localhost:4502/bin/querybuilder.json?1_relativedaterange.lowerBound=-1M&1_relativedaterange.property=profile%2flastloggedin&orderby=%40profile%2flastloggedin&p.hits=selective&p.limit=-1&p.properties=profile%2flastloggedin%20profile%2fgivenName%20profile%2ffamilyName%20profile%2femail%20rep%3aauthorizableId&path=%2fhome%2fusers&type=rep%3aUser

This will provide the JSON with user details who log in to the system for the last one month.

{
"success": true,
"results": 2,
"total": 2,
"more": false,
"offset": 0,
"hits": [
{
"rep:authorizableId": "albin",
"profile": {
"lastloggedin": "2020-09-10T22:15:41.807+05:30",
"givenName": "Albin",
"familyName": "Issac",
"email": "[email protected]"
}
},
{
"rep:authorizableId": "admin",
"profile": {
"lastloggedin": "2020-09-11T01:52:56.358+05:30",
"familyName": "Administrator"
}
}
]
}

The Custom AuthenticationInfoPostProcessor can be modified even to add other scenarios e.g capturing the number of times the user login to the system.




Saturday, August 22, 2020

Content Preview/Review Solution in AEM/AMS

 This tutorial explains the approach to define a preview solution for AEM(Adobe Experience Manager)/AMS(Adobe Managed Server) platform to preview the content through Author instance before activating the content to publishers.

Some of the options to enable the preview options are

  1. Share the author URL with wcmmode=disabled, this requires the reviewer should be on-boarded into the Author environment to carry the review
  2. Enable an additional preview(publish) instance, establish the workflow to send the content to the preview server for review/approval before publishing the content to live, the reviewer can use the dedicated review URL to review the content. This approach needs an additional server license to review the content.
  3. Move the content to stage servers for review, the workflow can be established to move the content to a Stage environment for review, this approach has some restriction from AMS to move the content from Production to Stage servers through the workflow. This approach can be used for on-premise setup, refer the following URL for more details — https://www.albinsblog.com/2018/02/approach-to-implement-content-preview-in-adobe-experience-manager.html#.X0FhqOhKjb0

Let us now see an alternative simple approach to enable the content review solution in AEM

Content in an AEM Author instance is accessible with a special query string parameter that disables the AEM authoring user interface — ?wcmmode=disabled.

Additionally, by configuring a read-only user account within AEM with an explicit password set, the default single sign-on (“SSO”) behavior that redirects regular users to SAML for authentication is bypassed when sending those credentials via a standard HTTP Authorization header.

Combining these facts, a reverse proxy configuration using CloudFront can be set up within an AWS account to provide a lightweight, scalable review
solution with affordable CloudFront data transfer costs.

Image for post

Cloudfront Configuration

As a first step, let us create the required configurations for CloudFront — the AMS managed CloudFront instance can’t be used to enable these configurations, the custom CloudFront should be used.

Create two distinct AWS Lambda@Edge functions(Node JS) to be fired on the viewer request and origin request events

AEM-Preview-Solution

config.js

"use strict";module.exports = {
authUser: "reviewuser",
authPass: "reviewuser!",
aemUser: "reviewaemuser",
aemPass: "reviewaemuser!",
aemAuthorDisableParam: "wcmmode=disabled"
};

origin-request-handler

When a request hits this CloudFront request, the origin request Lambda script to append the ?wcmmode=disabled query string parameter to the end of the URL (merging with any existing parameters) before forwarding to the origin(AEM Author).

Additionally, ensures the HTTP Authorization header with AEM preview user is added before the origin request;

"use strict";const config = require("./config.js");exports.handler = (event, context, callback) => {
const cfRequest = event.Records[0].cf.request;
// Reject root requests
if (cfRequest.uri === "/") {
callback(null, {
status: "400",
body: "Invalid preview URL."
});
return;
}
// Disable authoring mode
cfRequest.querystring += (cfRequest.querystring === "" ? "" : "&") + config.aemAuthorDisableParam;
// Set authorization header
cfRequest.headers["authorization"] = [
{
key: "Authorization",
value:
`Basic ${new Buffer(`${config.aemUser}:${config.aemPass}`).toString("base64")}`
}
];
// Send back
callback(null, cfRequest);
};
AEM-Preview-Solution

viewer-request-handler

Sets an Authorization header when the viewer request event is fired, ensuring the content at this preview hostname is protected by a standard HTTP Basic Authentication prompt — users prompted with basic authentication to access the preview domain.

"use strict";const config = require("./config.js");exports.handler = (event, context, callback) => {
const cfRequest = event.Records[0].cf.request;
// Construct the auth string
const authString =
`Basic ${new Buffer(`${config.authUser}:${config.authPass}`).toString("base64")}`;
// Add auth header
if (
typeof cfRequest.headers.authorization === "undefined" ||
cfRequest.headers.authorization[0].value !== authString
) {
const response = {
status: "401",
statusDescription: "Unauthorized",
body: "Unauthorized",
headers: {
"www-authenticate": [{ key: "WWW-Authenticate", value: "Basic" }]
}
};
callback(null, response);
return;
}
// Continue request processing if authentication passed
callback(null, cfRequest);
};
AEM-Preview-Solution

Configure a new CloudFront distribution with the below settings:

General

  • The alternative domain name (CNAMEs): preview-example.com
  • Add a custom SSL certificate corresponding to the above hostname
    Origin

Origin

  • Origin domain name: author-prod-example.com(the author domain name, this domain will be used CloudFront to connect to author server)
  • Origin protocol policy: HTTPS Only

Behavior

  • Origin protocol policy: HTTPS Only
  • Viewer Protocol Policy: Redirect HTTP to HTTPS
  • Allowed HTTP methods: GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE(enable the minimum required options)
  • Cache based on selected request headers: Whitelist
  • Whitelist: Authorization, Host
  • Minimum TTL, Maximum TTL, Default TTL: all set to 60 — CloudFront caches the request for a configurable duration (60 seconds, currently), to avoid applying an unnecessary load to the Author instance;
  • Forward cookies: None
  • Compress objects automatically: Yes
  • Lambda function associations:
    Origin request: point to the ARN of the origin-request-handler
    Viewer request: point to the ARN of the viewer-request-handler
Image for post

Point a new DNS hostname preview-author.example.com(CNAME) to a new AWS CloudFront distribution through DNS Zone manager(e.g xxxxxxxxx.cloudfront.net).

preview-author.example.com CNAME xxxxxxxxx.cloudfront.net

AEM Configuration

Create a preview user in the Author instance with read-only access to the appropriate content/DAM and an explicit password set- reviewaemuser/reviewaemuser!(Please validate with Adobe before implementing this solution if there is any licensing issue with this common user, my understanding is there should not be any impact)

The Author instance is configured with this preview hostname as an additional VHOST in author-vhosts.any — author-vhosts.any, this file enable the supported vhosts for author-farm.any, also disable the http to https redirect in the default.vhost for the specific domain, even though the preview URL is enabled through https the SSL forward related headers will not be sent to the Dispatcher e.g “X-Forwarded-Proto”

default.vhost

RewriteEngine on
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteCond %{HTTP_HOST} !^preview-author.example.com$
RewriteCond %{REQUEST_URI} !^/dispatcher/invalidate.cache
RewriteRule !/eagle/check https://%{SERVER_NAME}%{REQUEST_URI} [L,R]

author-vhosts.any

"author-prod-example.com"
"preview-author.example.com"

AEM renders the page in the Author instance with the AEM authoring user interface disabled, effectively producing HTML markup identical to the output of the Publish instance once activated

Now the configurations are ready, Web publishers can construct a URL similar to the below, reflecting a page location within AEM, and circulate this to any user needing to preview the currently-authored content before it is activated live to the public-facing website. This URL is accessible with complete content path, but behind the credentials provided(reviewaemuser/reviewaemuser!). https://preview-author.example.com/content/test/us/en/home.html