The PatchworkAction

The PatchworkGenerator is the easiest and fastest way to merge your layouts, but there could be some reasons which prevent you from using this generator. In this case you could use the PatchworkAction instead for resolving the layout definition but the merging process itself must be done by the view engine like JXTemplate, Velocity, XSLT or whatever.

Similar to the PatchworkGenerator the PatchworkAction must be configured in the components section of your sitemap first:

<map:action name="patchwork" src="com.logabit.webtools.patchwork.acting.PatchworkAction">

Then you can use the action in your pipeline. Again, the simplest way to do so is by using it without any parameters:

<map:match pattern="news">
  <map:generate type="file" src="templates/index.jxt"/>
  <map:act type="patchwork/>
  <map:transform type="jx"/>
  <map:serialize type="html"/>
</map:match>

On the PatchworkAction you can also set the parameters call-layout, and request-name as described for the PatchworkGenerator.

Once called, the PatchworkAction will load the requested layout and stores it into the request scope under the name configured using the parameter request-name. The default request attribute name for this is patchwork. After the PatchworkAction was executed you can load the current layout definition from any place where you have access to the request scope. In a flowscript for example you can do:

var layout = cocoon.request.getAttribute("patchwork");

In a JXTemplate for example you can do the following in order to include the header definition:

<jx:set var="layout" value="${cocoon.request.getAttribute('patchwork')}" />
<jx:import uri="${layout.includes.header}"/>

Please take a look into the Javadocs for the PatchworkLayout class to find out all available methods which can be used within your template. Here are some examples for the most important use cases:

<jx:import uri="${layout.includes.main}"/>

Includes the include main.

<jx:forEach var="include" items="${layout.includes}">
  <jx:import uri="${include}"/>
</jx:forEach>

Allows you to iterate over all includes. Doing it this way you could easily dynamically import the includes of a layout.

<h1>Title: ${layout.properties.title}</h1>

Reads the property title from the current layout.

Note: Depending on the view render engine you're using, the ways of accesing the layout definition coming from the request scope could differ. For JXTemplate you could find everything you should know in the online docs of Apache Cocoon: http://cocoon.apache.org/2.1/userdocs/flow/jxtemplate.html

Resolving the master template

Looking back a bunch of lines you can see that in the intrduction example the master template templates/index.jxt was loaded explicitely using a generator:

<map:match pattern="news">
  <map:generate type="file" src="templates/index.jxt"/>
  ...
</map:match>

This was done, because we couldn't know the path of the master template. To resolve this path dynamically, the PatchworkAction will return the action parameter master-template. This parameter contains the path to the master template. Using this parameter you could then easily load the master template dynamically by passing it to the generator:

<map:match pattern="news">
  <map:act type="patchwork>
     <map:generate type="file" src="{master-template}"/>
  </map:act>
  <map:transform type="jx"/>
  <map:serialize type="html"/>
</map:match>

Easy, isn't it?! :-)