Component Filtering with HstComponentWindowFilter
Introduction
Goal
Apply a filter to the page component hierarchy for certain requests.
Use Case
Assume you have the use case that based on some criteria, for example whether the visitor is authenticated or not, you want to show or hide certain components (blocks) from a page. Assume this very www.onehippo.org as example, and that you do not want to show the Hippo Developer Training component for authenticated visitors. Assume the page configuration used extends from base and looks like :
/hst:abstractpages: jcr:primaryType: hst:pages /base: jcr:primaryType: hst:component /main; jcr:primaryType: hst:component /content: jcr:primaryType: hst:component /container: jcr:primaryType: hst:containercomponent /hippo-developer-training: jcr:primaryType: hst:containeritemcomponent
You can write code in every component its class (controller) that does some checks, like whether a visitor is authenticated, and for example if so, have the renderer output nothing. The downside is that the components still have their controller (java class) and view (jsp/freemarker template) invoked.
There is a way more elegant and flexible solution to achieve the above, which effectively just removes any component completely from the tree of components for a single request.
HstComponentWindowFilter Interface
HstComponentWindowFilter is an interface in the HST API that can be used to achieve the above requirement. It looks as follows:
public interface HstComponentWindowFilter { /** * @param requestContext * @param compConfig the {@link HstComponentConfiguration} from which <code>window</code> is created * @param window The {@link HstComponentWindow} to decorate * @return A {@link HstComponentWindow} instance which can be an enhanced or decorated version of the * <code>window</code>. If the <code>window</code> should be entirely disabled/skipped, <code>null</code> * should be returned * @throws HstComponentException */ HstComponentWindow doFilter(HstRequestContext requestContext, HstComponentConfiguration compConfig, HstComponentWindow window) throws HstComponentException; }
Note from javadoc above the subtle difference between returning from doFilter:
- null
- The HstComponentWindow window after invoking window.setVisible(false)
Example Implementation
Below is an example implementation of HstComponentWindowFilter that checks whether there is a component parameter hide-for-authenticated-users = true, and if there is one, and the visitor is authenticated, the component gets completely skipped:
public class AuthenticatedBasedComponentFilter implements HstComponentWindowFilter { @Override public HstComponentWindow doFilter(final HstRequestContext requestContext, final HstComponentConfiguration compConfig, final HstComponentWindow window) throws HstComponentException { Boolean hideForAuthenticatedUsers = (Boolean)ConvertUtils.convert( compConfig.getParameters().get("hide-for-authenticated-users"), Boolean.class); if (!hideForAuthenticatedUsers) { return window; } Subject subject = requestContext.getSubject(); if (subject == null) { return window; } return null; } }
Inject the Filter in the Request Processing
After you have implemented your HstComponentWindowFilter you need to add some Spring configuration in your META-INF/hst-assembly/overrides to make sure the filtering is applied:
<?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 class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="targetObject" ref="org.hippoecm.hst.core.container.HstComponentWindowFilter.list"/> <property name="targetMethod" value="add"/> <property name="arguments"> <bean class="org.example.AuthenticatedBasedComponentFilter"/> </property> </bean> </beans>
This is all, now you have your custom component filtering enabled.