Using Accelerated Mobile Pages (AMP) in Jahia

October 8, 2024

Accelerated Mobile Pages (AMP) is an open source project supported by Google to improve mobile phone web browsing. Simply put, it's an HTML page with dedicated scripts and tags.

Currently AMP technology is mostly used to display news and articles, content that are read on a cellular phone and that needs to be loaded fast. This kind of content match well with Jahia “content templates” approach.

We describe here how to integrate AMP technology with a simple example that creates AMP pages to display content element of a news type based on the Digitall demo site. This example can be transposed easily to any other kind of content

Prerequisites

To understand and reproduce this example, you need to run an instance of Jahia that contains the sample Digitall site.

Creating AMP content templates

As any other content rendering in Jahia, a dedicated AMP view needs to be created for the news. It can be done either with adding it to the template set or creating a dedicated module for it. To ease this example, we have created a sample module that can be found here:

https://github.com/dgriffon/sample-AMP-DX-integration/tree/1-create-module

Compile and deploy it to your Jahia instance.

mvn clean install

 

AMP-1.png

Creating the content template

The goal is to have an AMP content template for news, other type of content may need to get several content templates. First, create a template folder for the news where you will put the AMP content template. This will help to use the same template name for all the AMP content templates.

First, add a dependency to the news module

AMP-2.png

Next create a template, and a content template for the news content type:

AMP-3.png

AMP-4.png

Uncheck "Automatically synchronize name with title:"

Set the same to : amp

AMP-5.png

If you encounter any issue in this phase, please check out the result here:

https://github.com/dgriffon/sample-AMP-DX-integration/tree/2-content-template

Creating views

AMP needs a dedicated structure for its page. You can find more information on it here:

https://www.ampproject.org/docs/get_started/create/basic_markup

In Jahia, it means to create views. First, create the template view, then the view of the news nodetype.

Template view

From the Jahia studio, select the file system tab, and right click on the root folder, select Add view

AMP-6.png

In the selector, type "template" then pick Template. Choose Template as a parent type of "Content template" and pageTemplate.

Copy the content from https://www.ampproject.org/docs/get_started/create/basic_markup and save the view naming it: amp-sample

AMP-7.png

AMP-8.png

The view is now available in the file system tab:

AMP-9.png

Create an area name pagecontent in this view to be able to create content.

To do so, replace the inner <body> section to a Jahia area tag that defines a pagecontent area that displays the main resource:

<template:area editable="true" moduleType="existingNode" view="detail-amp"/>

This is the equivalent of creating a standard area, and drag and drop a main resource area component in it, setting the view to detail-amp.

News nodetype view

As for template, create a new view from the news nodetype but this time, copy the content from the detail view of the news that you can get here:

https://github.com/Jahia/bootstrap-acme-space-templates/blob/master/src/main/resources/jnt_news/html/news.detail.jsp

Create the view as done for the template but selecting "News Entry" instead of template in the nodetypes list, copy the detail view content of the new in it, then save it naming it detail-amp.

You should get the following result:

AMP-10.png

After copying the detail view of the news from github, remove the following lines from the code of your view:

<%@ taglib prefix="bootstrap" uri="http://www.jahia.org/tags/bootstrapLib" %>
<bootstrap:addCSS/>

Now the views are set, compile and deploy your module to get the view working

Click on "compile and deploy", the first icon in the file system tab.

AMP-11.png

Setting the content template view

Now assign the views to the previously created template. Edit the news content template to set the view to amp-sample.

AMP-12.png

Everything is almost set. Let's compile the module once again, and enable it on the Digitall Demo site.

Click on module detail in the top bar, then enable the module on Digitall Demo site.

AMP-13.png

If you encounter any trouble to get it work, you can get the source code of this step here:

https://github.com/dgriffon/sample-AMP-DX-integration/tree/3-create-views

Cleaning up the view

Now exists an AMP view for our news. But this view must be clean up to match the AMP constraints. You can find more detail on the AMP format here:

https://www.ampproject.org/docs/guides/responsive_amp

To summarize, no external javascript or css are allowed except the ones provided by AMP, so the view needs to be cleaned to only keep what is allowed by AMP.

Assuming Jahia is installed in root context with an Digitall imported site, the relevant URL displaying a news is:

http://localhost:8080/cms/render/default/en/sites/digitall/home/newsroom/news-entry/article/movies-can-determine-your-succes.html

AMP-14.png

And the AMP view of the news: http://localhost:8080/cms/render/default/en/sites/digitall/home/newsroom/news-entry/article/movies-can-determine-your-succes.amp.html

AMP-15.png

Validating the AMP view

This validation can be done using the debugging tools of AMP to debug as described here:

https://www.ampproject.org/docs/get_started/create/preview_and_validate

In this case, enable the validation activating Chrome dev tools and using the URL

http://localhost:8080/jahia/cms/render/default/en/sites/digitall/home/newsroom/news-entry/article/movies-can-determine-your-succes.amp.html#development=1 to get the following result:

AMP-16.png

As displayed, there are 2 errors that need to get fixed to validate the AMP page. An AMP page must be valid to be served by a cache.

Removing StaticAssetFilter unwanted output

The first error is due to the StaticAssetFilter that manage the resources that the modules can provide. As AMP does not support external resource, the entries added by this filter must be removed. There are 2 possibilities:

  • disabling the filter and override it to define a custom filter that not do the import.
  • Create a custom filter that remove unwanted entries.

In this example, only the 2nd filter is described.

Creating a newJahia Render Filter

Add a java class named AssetsAMPFilter in the package org.jahia.modules.digitall.filters

A default Filter contains

package org.jahia.modules.digitall.filters;
import org.jahia.services.render.RenderContext;
import org.jahia.services.render.Resource;
import org.jahia.services.render.filter.AbstractFilter;
import org.jahia.services.render.filter.RenderChain;

/**
* Render Filter that cleans up the StaticAssetFilter for AMP rendering
*/
public class AssetsAMPFilter extends AbstractFilter{
   @Override
   public String execute(String previousOut, RenderContext renderContext, Resource resource, RenderChain chain) throws Exception {
       return super.execute(previousOut, renderContext, resource, chain);
   }
}

The bean declaration in the spring module file must be set (you have to remove the disabled extension)

src/main/resources/META-INF/spring/amp-sample.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
   <bean id="cleanUpAssetsAMPFilter" class="org.jahia.modules.digitall.filters.AssetsAMPFilter">
   <property name="priority" value="-1"/>
   <property name="applyOnEditMode" value="false"/>
   <property name="applyOnConfigurations" value="page"/>
   <property name="applyOnNodeTypes" value="jnt:news, jnt:article"/>
</bean>
</beans>

Values explanation:

  • priority: The StaticAssetFilter must be changed, this operation must be performed after its execution, meaning that the filter must be before it, less than 0)
  • applyOnEditMode: Due to its constraints, the AMP view is not working in edit mode.
  • applyOnConfigurations: Set to page, as the StaticAssetFilter.
  • applyOnNodeTypes: The applied filter is restricted to the news only.

Excluding unwanted content

<script type="text/javascript">
var contextJsParameters={contextPath:"/jahia",lang:"en",uilang:"en",siteUuid:"40ef8fce-72e8-41ec-9d93-32a2fe9807ef",wcag:true,ckeCfg:""}; var CKEDITOR_BASEPATH="/jahia/modules/ckeditor/javascript/"; var scayt_custom_params=new Array(); scayt_custom_params['sLang']='en_US';
</script>

Using the following code in the AssetsAMPFilter removes it:

private final static String MARKER = "macro LineToRemove not found";
@Override
public String execute(String previousOut, RenderContext renderContext, Resource resource, RenderChain chain) throws Exception {
   String out = super.execute(previousOut, renderContext, resource, chain);

   // Remove unsuported scripts and link lines added by the staticAssetFilter
   if (StringUtils.equals(resource.getTemplate(), "amp")) {
       String[] lines = out.split(System.getProperty("line.separator"));
       for (int i = 0; i < lines.length; i++) {
           if (StringUtils.contains(lines[i], "contextJsParameters")) {
               lines[i] = lines[i - 1] = lines[i + 1] = MARKER;
           } else if (StringUtils.contains(lines[i], "<link id=\"staticAssetCSS")) {
               lines[i] = MARKER;
           }
       }
       StringBuilder result = new StringBuilder();
       for (String s : lines) {
           if (!StringUtils.equals(s, MARKER)) {
               result.append(s).append(System.getProperty("line.separator"));
           }
       }
       out =  result.toString();
   }

   return out;
}

Once compiled and deploy, only one error remains in AMP validation

AMP-17.png

If you encounter any difficulties to get to that point, check-out the source code from here:

https://github.com/dgriffon/sample-AMP-DX-integration/tree/4-create-render-filter

Fixing the news view

The remaining error is due to the image tag that is not supported by AMP as described here:

https://www.ampproject.org/docs/guides/amp_replacements

The news view needs to be updated to fit the AMP usage, changing the <img> tag to <amp-img>

<figure><img src="<c:url value="${image.node.url}" context="/"/>" alt="${image.node.displayableName}"></figure>

With

<c:set var="width" value="${image.node.properties['j:width'].long}"/>
<c:set var="height" value="${image.node.properties['j:height'].long}"/>
<figure><amp-img src="<c:url value="${image.node.url}" context="/"/>" width="${width}" height="${height}">
   <noscript>
       <img src="<c:url value="${image.node.url}" context="/"/>" alt="${image.node.displayableName}">
   </noscript>
</amp-img>
</figure>

AMP-18.png

Code cleanup can also be performed to remove unsupported tag like <template:addResource/>

Now the page is validated.

If you get any trouble to get to that point, you can download the source of this step here:

https://github.com/dgriffon/sample-AMP-DX-integration/tree/5-fix-image

Header links

To be discoverable, a page must declare that an AMP version is available. Add this information in the html version of the amp page. A link to the html version can be set in the AMP page.

https://www.ampproject.org/docs/guides/discovery

For this, adopt the previously used filter. Depending of the context, the corresponding link will be added in the header section.

Editing the filter

String out = super.execute(previousOut, renderContext, resource, chain);
// Build the amp links that will be used in the header
URLGenerator url = renderContext.getURLGenerator();
String resourceUrl = url.getServer() + url.getContext() + url.getBase() + resource.getNode().getPath();
String ampLink = "<link rel=\"ampType\" href=\"" + resourceUrl + "TEMPLATE.html\" />";

// Remove unsuported scripts and link lines added by the staticAssetFilter
if (StringUtils.equals(resource.getTemplate(), "amp")) {
   String[] lines = out.split(System.getProperty("line.separator"));
   for (int i = 0; i < lines.length; i++) {
       if (StringUtils.contains(lines[i], "contextJsParameters")) {
           lines[i] = lines[i - 1] = lines[i + 1] = MARKER;
       } else if (StringUtils.contains(lines[i], "<link id=\"staticAssetCSS")) {
           lines[i] = MARKER;
       }
   }
   StringBuilder result = new StringBuilder();
   for (String s : lines) {
       if (!StringUtils.equals(s, MARKER)) {
           result.append(s).append(System.getProperty("line.separator"));
       }
   }
   out =  result.toString();
   // Set the values for AMP Link
   ampLink = StringUtils.replace(ampLink, "ampType", "canonical");
   ampLink = StringUtils.replace(ampLink, "TEMPLATE", "");
} else {
   // Set the values for AMP Link
   ampLink = StringUtils.replace(ampLink, "ampType", "amphtml");
   ampLink = StringUtils.replace(ampLink, "TEMPLATE", ".amp");
}
// Add amp link
out = StringUtils.replace(out, "</head>", ampLink + "\n</head>");
return out;

Removing the hardcoded link

In the header section of template.amp-sample.jsp is a hard-coded link from the example that has to be removed

<link rel="canonical" href="http://example.ampproject.org/article-metadata.html" />

Compiling and testing

Once compiled, go to the html page to see in the header section the following link:

<link rel="amphtml" href="http://localhost:8080/cms/render/default/en/sites/digitall/home/newsroom/news-entry/article/movies-can-determine-your-succes.amp.html" />

And in the AMP page this one:

<link rel="canonical" href="http://localhost:8080/cms/render/default/en/sites/digitall/home/newsroom/news-entry/article/movies-can-determine-your-succes.html" />

If you get any trouble to get this result, you can get the source code from:

https://github.com/dgriffon/sample-AMP-DX-integration/tree/6-add-header-links

Making it nicer

Creating custom styles

It is possible to declare custom style. This has to be done in the header section of the page, using the amp-custom style attribute.

https://www.ampproject.org/docs/guides/responsive_amp

<style amp-custom>
 .btn-primary {
   color: #ffffff;
   text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
   background-color: #6600cc;
   background-image: -moz-linear-gradient(top, #6600cc, #6600cc);
   background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#6600cc), to(#6600cc));
   background-image: -webkit-linear-gradient(top, #6600cc, #6600cc);
   background-image: -o-linear-gradient(top, #6600cc, #6600cc);
   background-image: linear-gradient(to bottom, #6600cc, #6600cc);
   background-repeat: repeat-x;
   filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff6600cc', endColorstr='#ff6600cc', GradientType=0);
   border-color: #6600cc #6600cc #400080;
   border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
   filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
   color: #ffffff;
 }
 .btn {
   display: inline-block;
   padding: 4px 12px;
   margin-bottom: 10px;
   font-size: 14px;
   font-weight: bold;
   line-height: 20px;
   text-align: center;
   vertical-align: middle;
   cursor: pointer;
   background-color: #ffffff;
   border: 1px solid #cccccc;
   border-bottom-color: #b3b3b3;
   -webkit-border-radius: 4px;
   -moz-border-radius: 4px;
   border-radius: 4px;
   text-shadow: 0 1px rgba(0, 0, 0, 0.1);
 }
</style>

Adding AMP scripts

Amp scripts can be added to enhance the rendering of the news. For example, using  amp-fit-text.

First add in the header section of the template:

<script async custom-element="amp-fit-text" src="https://cdn.ampproject.org/v0/amp-fit-text-0.1.js"></script>
<amp-fit-text width="300" height="45" layout="responsive" max-font-size="45">
   ${title.string}
</amp-fit-text>

Instead of the <h1> tag

Expected result

AMP-19.png

You can get the source code from here:

https://github.com/dgriffon/sample-AMP-DX-integration/tree/7-styles-and-scripts

And get the compiled module from:

https://github.com/dgriffon/sample-AMP-DX-integration/blob/master/compiled-bundle/sample-AMP-DX-integration-1.0-SNAPSHOT.jar

Going further

Support for files in rich text

This example assumes that the rich text used for the news is pure valid HTML with no image in it. The filter can be improved to change all the <img> tags to the corresponding AMP tags. The same way for video or other resources

Support for links

The goal of an AMP page is to be cached, meaning that the links within a page are not known until other pages are in cache. Google provides an API to get available links from its cache. You can find more information here:

https://developers.google.com/amp/cache/use-amp-url

Further Jahia integration

All the views for the component you want to use in your amp page must be currently created. One of our goal is to provide filters and tools to help content manager build AMP pages from an already stored content and concentrate on their content and let Jahia manage the rendering.