Added by Dennis Dam, last edited by Dennis Dam on Oct 02, 2007  (view change)

Labels:

cms cms Delete
extend extend Delete
extensions extensions Delete
override override Delete
Enter labels to add to this page:
Wait Image 
Looking for a label? Just start typing.
Hippo CMS version v6.03.06
Audience CMS developers

How to override core functionality in Hippo CMS

This page explains how to override parts of the CMS core (Cocoon) code by using the extensions mechanism.

Table of contents

Goal

Sometimes you want to modify the default functionality or presentation of the CMS. Instead of implementing your own solution from scratch, it is also possible to override only the bare minimum of the CMS' core functionality to achieve the same goal. This page assumes you have set up your own (project specific) extensions folder

How to do it

Basically you can override any pipeline call to the subsitemaps of /src/site/sitemap.xmap. E.g., the calls to /explorer/** and editing/**. Let's take an example pipeline from /src/site/explorer/sitemap.xmap:

<map:match pattern="resource-searcher/*/**">
  <map:aggregate element="root" label="nodetree">
    <map:part element="nodetree" src="cocoon:/simplesearch/{1}/{2}"/>
    <map:part element="sitemodel" src="{site:model}"/>
  </map:aggregate>
  <map:transform src="transformers/resource-picker/daslsearch2html.xsl">
    <map:parameter name="prefix" value="{repository:filesPath}"/>          
    <map:parameter name="rootNode" value="/{1}"/>   
    <map:parameter name="targetNode" value="/{2}"/>
    <map:parameter name="page" value="{request-param:page}"/>
    <map:parameter name="pageSize" value="10"/>
    <map:parameter name="query" value="{request-param:query}"/> 
    <map:parameter name="searchInCaptions" value="{request-param:searchInCaptions}"/>  
    <map:parameter name="mode" value="{request-param:mode}"/>     
    <map:parameter name="locale" value="{session-attr:locale}"/>
    <map:parameter name="requiredType" value="{request-param:type}"/>
    <map:parameter name="selectedResource" value="{request-param:selectedResource}"/>          
  </map:transform>
  <map:transform type="i18n">
    <map:parameter name="locale" value="{session-attr:locale}"/>
    <map:parameter name="default-catalogue-id" value="messages"/>
  </map:transform>
  <map:serialize type="html"/>
</map:match>

which can be called by (for example) /explorer/resource-searcher/binaries/. This is a browser for selecting documents or binaries from the repository.

Now suppose you want to override this pipeline, to customize the default asset picker popup. Let's say you want to change the way it searches for binaries: you want to restrict the files to .jpg files. You only need to change the dasl search executed for that, which is this part of the pipeline:

<map:part element="nodetree" src="cocoon:/simplesearch/{1}/{2}"/>

Ok. So how do we let the CMS 'listen' to our overriding code? Follow these steps:

1) Create the overriding sitemap

This can be done by creating a folder explorer in your extensions folder, and creating a file called override.xmap in the explorer folder. Your folder structure now looks like this:

+ HippoCMS-customized-project-name
    |
    +-- editor
    |
    +-- extensions
    |    |
    |    +-- sitemap.xmap
    |    +-- explorer
    |         +- override.xmap
    |
    +-- project.properties
    +-- common-project.xml

So what's the difference between sitemap.xmap and override.xmap ? The pipelines declared in sitemap.xmap are extensions to Hippo CMS, which have the url prefix /extensions/... The core CMS code is not affected by these pipelines. The pipelines created in override.xmap however, override the pipelines of the sitemap.xmap from the subfolder in /src/site with the same name as the folder which holds the override.xmap, e.g. <extensions folder>/explorer/override.xmap overrides /src/site/explorer/sitemap.xmap.

2) pass all urls you don't want to change back to the original sitemap

Now every call to the explorer sitemap will go through your custom sitemap at <extensions>/explorer/override.xmap! The first thing you have to do, is pass all other pipelines you don't want to change back to the original explorer sitemap. Add the following bare minimum code to override.xmap:

<?xml version="1.0"?>
<map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0">

  <!-- pass all other urls back to the original sitemap -->
  <map:pipelines>
    <map:pipeline type="noncaching">
     <map:match pattern="**">
       <map:mount uri-prefix="" src="site://explorer/sitemap.xmap"/>
     </map:match>
    </map:pipeline>
  </map:pipelines>
</map:sitemap>

3) add matches for code you want to override

Now it's time to add code for pipelines you want to override. Add the following code before the "catch all" pipeline (**) :

<map:match pattern="resource-searcher/*/**">
  <map:aggregate element="root" label="nodetree">
    <map:part element="nodetree" src="cocoon:/simplesearch/{1}/{2}"/> <!-- line 3 -->
    <map:part element="sitemodel" src="{site:model}"/>
  </map:aggregate>
  <map:transform src="site://explorer/transformers/resource-picker/daslsearch2html.xsl"> <!-- line 6 -->
    <map:parameter name="prefix" value="{repository:filesPath}"/>          
    <map:parameter name="rootNode" value="/{1}"/>   
    <map:parameter name="targetNode" value="/{2}"/>
    <map:parameter name="page" value="{request-param:page}"/>
    <map:parameter name="pageSize" value="10"/>
    <map:parameter name="query" value="{request-param:query}"/> 
    <map:parameter name="searchInCaptions" value="{request-param:searchInCaptions}"/>  
    <map:parameter name="mode" value="{request-param:mode}"/>     
    <map:parameter name="locale" value="{session-attr:locale}"/>
    <map:parameter name="requiredType" value="{request-param:type}"/>
    <map:parameter name="selectedResource" value="{request-param:selectedResource}"/>          
  </map:transform>
  <map:transform type="i18n"> <!-- line 19 -->
    <map:parameter name="locale" value="{session-attr:locale}"/>
    <map:parameter name="default-catalogue-id" value="messages"/>
  </map:transform>
  <map:serialize type="html"/>
</map:match>

This code was copied from the original explorer sitemap.xmap. There are three important things going on here:

  1. we are overriding the pipeline cocoon:/simplesearch/{1}/{2 (line 3)
  2. we are re-using the CMS' original code, by using an existing XSL (line 6)
  3. the result of this pipeline is not properly internationalized (i18n) yet, so we need to add some more code. (line 19)

Ad 1. overriding the pipeline cocoon:/simplesearch/{1}/{2}

We have to add code for the pipeline "cocoon:/simplesearch/{1}/{2}", for overriding the default search method used by the asset picker. Copy the original code from /src/site/explorer/sitemap.xmap and make some changes:

<map:match pattern="simplesearch/*/**">
  <map:generate src="resources/xml/simplesearch.xml" type="jx"> <!-- line 2 -->
    <map:parameter name="target" value="{repository:files}"/>
    <map:parameter name="path" value="{1}/{2}"/>
    <map:parameter name="query" value="{request-param:query}"/>
    <map:parameter name="searchInCaptions" value="{request-param:searchInCaptions}"/>
    <map:parameter name="nrOfResults" value="10"/>
    <map:parameter name="mode" value="{request-param:mode}"/>
    <map:parameter name="type" value="{request-param:type}"/>
    <map:parameter name="page" value="{request-param:page}"/>
  </map:generate>
  <map:transform type="webdav"/>
  <map:transform src="site://explorer/transformers/util/propfind2tree.xsl" label="nodetree"> <!-- line 13 -->
    <map:parameter name="prefix" value="{repository:rootPath}{repository:filesPath}"/>
    <map:parameter name="rootNode" value="/{1}"/>
    <map:parameter name="targetNode" value="/{2}"/>
    <map:parameter name="href" value="{2}"/>
    <map:parameter name="mode" value="list"/>
  </map:transform>
  <map:serialize type="xml"/>
</map:match>

On line 13, we re-used the original XSL for formatting a DASL result. The reference to the file containing the dasl (line 2) now points to a non-existing document. Copy the original dasl from /src/site/explorer/resources/xml/simplesearch.xml to <extensions folder>/explorer/resources/xml/simplesearch.xml and make the changes to the DASL you want. For example, to search for files of type .jpg, add a condition like this:

<jx:if test="${cocoon.parameters.mode=='jpeg'}"> 
  <d:eq>
    <d:prop><d:getcontenttype/></d:prop>
    <d:literal>image/jpeg</d:literal>
  </d:eq>
</jx:if>

This will search for files with mime-type image/jpeg whenever the asset picker is called with a request parameter mode=jpeg.

ad 3. Fixing the internationalization of the pipeline output

To fix the broken labels in the pipeline's output, you need to copy the original i18n transformer declaration from /src/site/explorer/sitemap.xmap to <extensions folder>/explorer/override.xmap, and remove any code we don't need. In our case, the i18n labels for the overridden pipeline are in /src/site/explorer/translations/... We simply re-use the original i18n files, add the following configuration in the top of override.xmap :

<map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0">
  <map:components>
    <map:transformers>
      <map:transformer name="i18n" src="org.apache.cocoon.transformation.I18nTransformer">
        <catalogues default="messages">
          <catalogue id="messages" name="messages">
            <location>site://explorer/translations</location>
          </catalogue>
        </catalogues>
        <cache-at-startup>false</cache-at-startup>
      </map:transformer>
    </map:transformers>
  </map:components>

  <map:pipelines>
   ...
  </map:pipelines>
</map:sitemap>

That's it! Check whether the customized pipeline works by going to the url

/explorer/resource-searcher/binaries/

and click around a bit to see if nothing's broken.

Conclusion

One important thing to remember is that the CMS code is always under development. If you decide the update the CMS to a newer version, it can affect your customizations as well. That's why you need to ask yourself a couple of questions:

  1. Do I really need to override code from the CMS ? There are possibly more roads leading to Rome.
  2. When I do really need to override the CMS code, how can I minimize the risk with relation to future updates of the CMS?

Regarding the last point, it is advisable to have at least code duplication as possible, so when you update your CMS version, you have to update as least code as possible.