Pages, Components, Abstract Pages and Templates

Pages, Components, Abstract Pages and Templates combined contain the information for the HST to build all available HMVC pages. As explained in Sitemap Configuration, a matched sitemap item usually contains a reference through hst:componentconfigurationid to an  hst:component below hst:pages. An hst:component node can in turn contain other hst:component child nodes. In other words, a page is a composite structure. To get a good understanding of what the HMVC configuration looks like, we'll start with most basic example and slowly increase complexity to touch all the concepts. 

Example 1: simple (trimmed) configuration

/hst:hst:
  /hst:configurations:
    /example:
      /hst:sitemap:
        /about.html:
          hst:componentconfigurationid: hst:pages/textpage
          hst:relativecontentpath: common/about
          /hst:pages:
            jcr:primaryType: hst:pages
            /textpage:
              jcr:primaryType: hst:component
              /header:
                jcr:primaryType: hst:component
                /main:
                  jcr:primaryType: hst:component
                  /leftmenu:
                    jcr:primaryType: hst:component
                  /content:
                    jcr:primaryType: hst:component
                  /right:
                    jcr:primaryType: hst:component

Example 1 explanation:

The sitemap contains 1 single item, about.html. A request that matches this sitemap item, will be rendered as a textpage because the hst:componentconfigurationid points to hst:pages/textpage. The hierarchical structure below textpage defines its page composition. The model that will be used for rendering the page is retrieved via the property hst:relativecontentpath: it is the canonical content that is mapped to the request. Apart from that, the model has access to the entire repository content, including searching and - in case of the Relevance Module - the visitor data. See HstRequestContext#getContentBean() at  the HstRequestContext object for information on how to access the canonical content.

What we did not yet include in the example configuration above is how from this hierarchical structure the HST gets the views ( hst:template) and the controllers ( hst:componentclassname). For this, we extend the configuration example to:

Example 2: simple configuration including views and controllers

/example:
  /hst:pages:
    /textpage:
      hst:template: textpage.layout
      /header:
        hst:template: textpage.header
        hst:componentclassname: org.example.components.Header
        /main:
          hst:template: textpage.main
          /leftmenu:
            hst:template: textpage.main.leftmenu
            hst:componentclassname: org.example.components.LeftMenu
          /content:
            hst:template: textpage.main.content
            hst:componentclassname: org.example.components.Content
          /right:
            hst:template: textpage.main.right
   /hst:templates:
     /textpage.layout:
       hst:renderpath: jsp/textpage/layout.jsp
     /textpage.header:
       hst:renderpath: jsp/textpage/header.jsp
     /textpage.main:
       hst:renderpath: jsp/textpage/main.jsp
     /textpage.main.leftmenu:
       hst:renderpath: jsp/textpage/main/leftmenu.jsp
     /textpage.main.content:
       hst:renderpath: jsp/textpage/main/content.jsp
     /textpage.main.right:
       hst:renderpath: jsp/textpage/main/right.jsp

Example 2 explanation:

As you can see every hst:component node has

  1. a view ( hst:template), and
  2. optionally a controller ( hst:componentclassname).

Controllers: Those component nodes that are only there for structural page layout reasons frequently do not have an hst:componentclassname (explicit controller)  because they do not require any custom controller behavior. Note that the HST internally will then use a default controller (if not specified in explicitly in end project  org.hippoecm.hst.core.component.GenericHstComponent is used as default controller, although this default can be changed, see Configure a Default HstComponent classname). If a hst:componentclassname is configured, it needs to be a fully qualified class name, which points to a class on the webapp classpath that implements  org.hippoecm.hst.core.component.HstComponent.

See Component Development for information on how to develop these HST controllers.

Views: every hst:component configuration node has a property  hst:template that points to a node below hst:templates. The hst:template nodes below hst:templates have a property hst:renderpath that can point to a 

  1. Freemarker template in repository Web Files
  2. JSP in the webapp container resources,
  3. Freemarker template in the webapp container resources, 
  4. Freemarker script in the template node itself in hst:script property
  5. Freemarker template on the classpath.

The advantage of option (1) and (4) is that the view can be modified in a runtime production environment because when they change, they get reloaded. See HST Freemarker Support for more information. 

The configuration in example 2 above is for just a single page textpage. From configuration example 1 and example 2 one can see that:

  1. multiple sitemap items can reference the same page below hst:pages.
  2. multiple hst:component nodes can reference the same hst:template.

However, what if next to a textpage page, you now also include a newspage and a newsoverview. A newspage might be exactly the same as the textpage, except that its main content JSP also should display the date of the news content. Instead of cloning the textpage to newspage and make one small change, you can make use of referencing. So assume adding  newspage and newsoverview nodes to hst:pages could result in something like:

Example 3: Component referencing 

/example:
  /hst:pages:
    /base:
      hst:template: base.layout
      /header:
        hst:template: base.header
        hst:componentclassname: org.example.components.Header
      /main:
        hst:template: base.main
        /leftmenu:
          hst:template: base.main.leftmenu
          hst:componentclassname: org.example.components.LeftMenu
        /right:
          hst:template: base.main.right
    /textpage:
      hst:referencecomponent: hst:pages/base
      /content:
        hst:template: textpage.main.content
        hst:componentclassname: org.example.components.Content
    /newspage:
      hst:referencecomponent: hst:pages/base
      /content:
        hst:template: newspage.main.content
        hst:componentclassname: org.example.components.Content
    /newsoverview:
      hst:referencecomponent: hst:pages/base
      /content:
        hst:template: newsoverview.main.content
        hst:componentclassname: org.example.components.NewsOverview

Example 3 explanation:

We have now a textpage, newspage and a newsoverview. They all share the same base page through

- hst:referencecomponent = hst:pages/base 

The base page provides the layout, i.e.  the  header, main/leftmenu and main/right parts. The concrete pages themselves define their content component. For a more elaborate explanation of merging rules of referenced components, see  HstComponent Configuration.

Example 4: Abstract pages

As can be seen from example 3, the hst:pages/base in there might be an abstract page, i.e. a (partial) page that is only meant to be referenced by other pages never referenced directly by a sitemap item. An abstract page can be better moved to hst:abstractpages for clarity. Making use of this the example configuration above becomes:

/example:
  /hst:abstractpages:
    jcr:primaryType: hst:pages
    /base:
      hst:template: base.layout
    /header:
      hst:template: base.header
      hst:componentclassname: org.example.components.Header
    /main:
      hst:template: base.main
      /leftmenu:
        hst:template: base.main.leftmenu
        hst:componentclassname: org.example.components.LeftMenu
      /right:
        hst:template: base.main.right
  /hst:pages:
    /textpage:
      hst:referencecomponent: hst:abstractpages/base
        /content:
          hst:template: textpage.main.content
          hst:componentclassname: org.example.components.Content
    /newspage:
      hst:referencecomponent: hst:abstractpages/base
      /content:
        hst:template: newspage.main.content
        hst:componentclassname: org.example.components.Content
    /newsoverview;
      hst:referencecomponent: hst:abstractpages/base
      /content:
        hst:template: newsoverview.main.content
        hst:componentclassname: org.example.components.NewsOverview 

Example 4 explanation:

The base page has been moved to hst:abstractpages and all pages that referenced hst:pages/base have their hst:referencecomponent changed:

hst:referencecomponent = hst:abstractpages/base

A sitemap item is not allowed to have a hst:componentconfigurationid that points to a page below hst:abstractpages. If that is the case, a warning is logged that this is not allowed.

A sitemap item should never reference an abstract page

We now covered Pages, Abstractpages and Templates. Last but not least are Components. From the HST engine point of view, there is no difference between whether a hst:component tree structure is configured below hst:pages or hst:components: a sitemap item can have a hst:componentconfigurationid that points to an  hst:component below hst:pages or below hst:components. The hst:components node is only there for developers, to be in a better position to make the configuration modular and reusable. As a rule of thumb, below hst:components the hst:component nodes are configured that have an explicit  controller, aka hst:componentclassname. From hst:pages, the component nodes then reference these components. Rewriting the example 4 configuration to make use of this rule of thumb results in the following configuration setup:

Example 5: Use hst:components 

/example:
  /hst:abstractpages:
    jcr:primaryType: hst:pages
    /base
      hst:template: base.layout
      /header
        hst:referencecomponent: hst:components/header
      /main
        hst:template: base.main
        /leftmenu
          hst:referencecomponent: hst:components/leftmenu
        /right
          hst:template: base.main.right
  /hst:pages:
    jcr:primaryType: hst:pages
    /textpage
      hst:referencecomponent: hst:abstractpages/base
      /content
        hst:referencecomponent: hst:components/content
    /newspage
      hst:referencecomponent: hst:abstractpages/base
      /content
        hst:referencecomponent: hst:components/content
        hst:template: newspage.main.content                !!!
    /newsoverview
      hst:referencecomponent: hst:abstractpages/base
      /content
        hst:referencecomponent: hst:components/newsoverview
  /hst:components:
    jcr:primaryType: hst:components
    /header
      hst:template: base.header
      hst:componentclassname: org.example.components.Header
    /leftmenu
      hst:template: base.main.leftmenu
      hst:componentclassname: org.example.components.LeftMenu
    /content
      hst:template: textpage.main.content
      hst:componentclassname: org.example.components.Content
    /newsoverview
      hst:template: newsoverview.main.content
      hst:componentclassname: org.example.components.NewsOverview

Example 5 explanation:

What changed with respect to example 4 is that 

  1. All hst:component nodes with a hst:componentclassname have been moved below hst:components.
  2. The hst:component nodes below hst:abstractpages and hst:pages use hst:referencecomponent to hst:components.

The configuration is pretty straight forward. A developer should realize that   

  1. Multiple inheritance is supported: hst:pages/textpage references hst:abstractpages/base, which in turn can reference other components or have descendants that reference other components.
  2. Properties of inherited components can be overridden by explicitly defining them. In the example 5 configuration above where this happens is marked with [!!!]. There we see that newspage/content references hst:components/content but explicitly defines to use the template newspage.main.content instead of the one from  hst:components/content.