HST Runtime Rewriting of Rich Text Fields

Overview

This page describes how to modify rich text content in documents at runtime using a custom content rewriter in Bloomreach Content.

Use Cases

You may need to change rich text fields dynamically based on context or runtime conditions. Common scenarios include:

  • Removing <a> elements when internal links cannot be resolved
  • Adding tooltips to links
  • Serving lower-resolution images for mobile channels
  • Implementing image lightboxes (e.g., clickable thumbnails that open larger images)
  • Adjusting image behavior based on context

You can implement a custom content rewriter in the following ways:

  • Configure per template (HST front ends)
  • Override the global default (HST front ends)
  • Override the default content rewriter Spring bean (Delivery API)

Rewriting Rich Text Content in HST Front End

Configure Per Template

To display rich text content, you typically use the following tags:

JSP

<hst:html hippohtml="${requestScope.document.html}"/>

Freemarker

<@hst.html hippohtml=document.html />

By default, these tags use the built-in HST SimpleContentRewriter. To use a custom content rewriter, specify it as an attribute:

JSP

<hst:html hippohtml="${requestScope.document.html}" contentRewriter="${requestScope.myContentRewriter}"/>

Freemarker

<@hst.html hippohtml=document.html contentRewriter=myContentRewriter />

You must also set myContentRewriter as a request attribute. For example, in your BaseComponent:

public abstract class BaseComponent extends BaseHstComponent { public static final MyContentRewriter myContentRewriter = new MyContentRewriter(); @Override public void doBeforeRender(HstRequest request, HstResponse response) { // Make the custom content rewriter available for the request request.setAttribute("myContentRewriter", myContentRewriter); }

Configure Global Default

Info: Available in HST 3.0.2 and 3.1.0 and later.

By default, HST uses SimpleContentRewriter. To override the default, set the default.hst.contentrewriter.class property in hst-config.properties. This file is typically located at site/webapp/src/main/webapp/WEB-INF.

Implementing a Custom Content Rewriter

A custom content rewriter must implement org.hippoecm.hst.content.rewriter.ContentRewriter. The recommended approach is to extend org.hippoecm.hst.content.rewriter.impl.AbstractContentRewriter or SimpleContentRewriter for access to utility methods.

For example, to highlight broken internal links in red, extend SimpleContentRewriter and override the rewrite method. The example below uses org.htmlcleaner.HtmlCleaner to parse and modify the HTML.

If you override rewrite, ensure you handle both links and images. If you only modify links, call super.rewrite(..) to preserve image rewriting logic.

To access HstRequest or HstResponse in your ContentRewriter, use:

HstRequest hstRequest = HstRequestUtils.getHstRequest( requestContext.getServletRequest()); HstResponse hstResponse = HstRequestUtils.getHstResponse( requestContext.getServletRequest(), requestContext.getServletResponse() );

Example: BrokenLinksMarkerContentRewriter

import javax.jcr.Node; import org.apache.commons.lang.StringUtils; import org.hippoecm.hst.configuration.hosting.Mount; import org.hippoecm.hst.content.rewriter.impl.SimpleContentRewriter; import org.hippoecm.hst.core.linking.HstLink; import org.hippoecm.hst.core.request.HstRequestContext; import org.htmlcleaner.CleanerProperties; import org.htmlcleaner.HtmlCleaner; import org.htmlcleaner.TagNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class BrokenLinksMarkerContentRewriter extends SimpleContentRewriter { private final static Logger log = LoggerFactory.getLogger(SimpleContentRewriter.class); private static boolean htmlCleanerInitialized; private static HtmlCleaner cleaner; private static synchronized void initCleaner() { if (!htmlCleanerInitialized) { cleaner = new HtmlCleaner(); CleanerProperties properties = cleaner.getProperties(); properties.setOmitHtmlEnvelope(true); properties.setTranslateSpecialEntities(false); properties.setOmitXmlDeclaration(true); properties.setRecognizeUnicodeChars(false); properties.setOmitComments(true); htmlCleanerInitialized = true; } } protected static HtmlCleaner getHtmlCleaner() { if (!htmlCleanerInitialized) { initCleaner(); } return cleaner; } @Override public String rewrite(final String html, final Node node, final HstRequestContext requestContext, final Mount targetMount) { if (html == null) { if (html == null || HTML_TAG_PATTERN.matcher(html).find() || BODY_TAG_PATTERN.matcher(html).find()) { return null; } } try { TagNode rootNode = getHtmlCleaner().clean(html); TagNode [] links = rootNode.getElementsByName("a", true); // Rewrite links: if a link cannot be resolved, remove href and set style for (TagNode link : links) { String documentPath = link.getAttributeByName("href"); if(isExternal(documentPath)) { continue; } else { String queryString = StringUtils.substringAfter(documentPath, "?"); boolean hasQueryString = !StringUtils.isEmpty(queryString); if (hasQueryString) { documentPath = StringUtils.substringBefore(documentPath, "?"); } HstLink href = getDocumentLink(documentPath,node, requestContext, targetMount); // If the link is not found, mark the element and remove href if (href == null || href.isNotFound() || href.getPath() == null) { link.removeAttribute("href"); setAttribute(link, "style", "color:red"); } else { String rewritterHref = href.toUrlForm( requestContext, isFullyQualifiedLinks()); if (hasQueryString) { rewritterHref += "?"+ queryString; } setAttribute(link, "href", rewritterHref); } } } // Rewrite image src attributes (same behavior as SimpleContentRewriter) TagNode [] images = rootNode.getElementsByName("img", true); for (TagNode image : images) { String srcPath = image.getAttributeByName("src"); if(isExternal(srcPath)) { continue; } else { HstLink binaryLink = getBinaryLink(srcPath, node, requestContext, targetMount); if (binaryLink != null && binaryLink.getPath() != null) { String rewrittenSrc = binaryLink.toUrlForm( requestContext, isFullyQualifiedLinks()); setAttribute(image, "src", rewrittenSrc); } else { log.warn("Skip href because url is null"); } } } // Return the rewritten body content TagNode [] targetNodes = rootNode.getElementsByName("body", true); if (targetNodes.length > 0 ) { TagNode bodyNode = targetNodes[0]; return getHtmlCleaner().getInnerHtml(bodyNode); } else { log.warn("Cannot rewrite content for '{}' because there is no 'body' element" + node.getPath()); } } catch (Exception e) { throw new RuntimeException(e); } return null; } private void setAttribute(TagNode tagNode, String attrName, String attrValue) { if (tagNode.hasAttribute(attrName)) { tagNode.removeAttribute(attrName); } tagNode.addAttribute(attrName, attrValue); } }

Rewriting Rich Text Content in the Delivery API

When using the Delivery API in a headless implementation, the default content rewriter is org.hippoecm.hst.pagemodelapi.v10.content.rewriter.HtmlContentRewriter. You can override this rewriter using Spring bean configuration.

Extend HtmlContentRewriter

Create a custom rewriter by extending org.hippoecm.hst.pagemodelapi.v10.content.rewriter.HtmlContentRewriter. For example:

site/components/src/main/java/org/example/CustomRewriter.java

package org.example; import javax.jcr.Node; import org.hippoecm.hst.configuration.hosting.Mount; import org.hippoecm.hst.core.request.HstRequestContext; import org.hippoecm.hst.pagemodelapi.v10.content.rewriter.HtmlContentRewriter; import org.htmlcleaner.HtmlCleaner; public class CustomRewriter extends HtmlContentRewriter { // implementation here }

Override the HtmlContentRewriter Spring Bean

Override the org.hippoecm.hst.pagemodelapi.v10.content.rewriter.HtmlContentRewriter Spring bean in an XML file located at classpath*:META-INF/hst-assembly/addon/org/hippoecm/hst/pagemodelapi/v10/*.xml. For example:

site/components/src/main/resources/META-INF/hst-assembly/overrides/addon/org/hippoecm/hst/pagemodelapi/v10/custom.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.xsd"> <bean id="org.hippoecm.hst.pagemodelapi.v10.content.rewriter.HtmlContentRewriter" class="org.example.CustomRewriter"> <constructor-arg> <bean class="org.hippoecm.hst.content.rewriter.HtmlCleanerFactoryBean" /> </constructor-arg> <property name="removeAnchorTagOfBrokenLink" value="${pagemodelapi.v10.removeAnchorTagOfBrokenLink:false}" /> </bean> </beans>
Share Feedback
Page: /build/miscellaneous/hst-2-rewriting-rich-text-field-runtime
Section: Build
Category *
HST Rewriting rich text field runtime | Bloomreach Content Documentation