ddraper

Spring Surf Presets

Blog Post created by ddraper on Nov 1, 2011

Introduction

I started writing this post back in November last year but never got around to finishing it. Since some questions came up about presets at DevCon in San Diego last week I thought it would make sense to revisit and finally post it. It really just provides some general information on how to configure an alternative Presets Manager for Alfresco Share and provides an example of how to switch from using the repository to the local file system for preset generated Spring Surf objects. In the future I'll try and post some more information specifically relating to customizing the Alfresco Share default presets, but hopefully this will be useful in the meantime!

Background

When any user logs into Share they are presented with their own unique dashboard that is a Spring Surf Page object in its own right. This page does not exist until the user logs in for the first time at which point it generated and persisted in the repository for re-use. This generation is achieved through the use of 'presets'.

 

Presets are essentially parameterised abstract Spring Surf artefact definitions (Pages, Template-Instances, Components, etc) defined in XML files that can a token map can be applied to generate concrete org.springframework.extensions.surf.ModelObject instances. They are managed and can be used through the org.springframework.extensions.surf.PresetsManager that is registered in the 'spring-surf-presets-context.xml' Spring configuration file as the 'webframework.presets.manager' bean. This configuration file is used to define what type of files contain Preset configuration and where to look for them.

 

By default Presets can be defined in 'presets.xml' files located in either 'classes/alfresco/site-data/presets' or 'classes/alfresco/web-extension/site-data/presets' (the use of 'alfresco' is a remnant of the morphing of Alfresco Surf to Spring Surf) but can be customized by overriding the Spring bean configuration (or by defining a new PresetsManager bean) in consuming applications.

 

If you do amend the search paths you must keep them somewhere under the 'classes' directory as they are located using a ClassPathStore (alternatively you can configure an alternative store and search path in the application context to use to locate the files). A correctly authored preset file contains a single 'presets' element containing zero or more 'preset' elements where each preset element can contain definitions for zero or more 'pages', 'template-instances' and 'components'. A basic skeleton structure could look something like this:

 

<?xml version='1.0' encoding='UTF-8'?>
<presets>
   <preset id='MyPreset'>
      <components>
         <component id='${componentId}'>
            ...
         </component>
      </components>
      <template-instances>
         <template-instance id='${templateId}'>
            ...
         </template-instance>
      </template-instances>
      <pages>
         <page id='${pageId}'>
            ...
         </page>
      </pages>
   </preset>
</presets>


The Share application contains a single Presets configuration file (WEB-INF/classes/alfresco/site-data/presets/presets.xml) that define a variety of different presets (including the default user dashboard configuration).

 

Instantiating Presets

You simply need to obtain your PresetsManager bean and call its 'constructPresets' method to make use of the configured Presets. This method requires the ID of the preset to use and a java.util.Map of key/value pair tokens to apply to the parameters. The method will search through the list of configured Preset XML files looking for a <preset> element with a matching 'id' attribute. The token map is applied to a matching definition to substitute the configurable parameters with actual values. Each defined ModelObject is then instantiated and persisted using the ModelObjectService bean. The resulting Pages, Template-Instances and Components are then available for use as though they were statically configured Spring Surf artefacts.

 

An alternative method for constructing presets is to use the custom Spring Surf JSP tags 'constructPreset' and 'presetToken'. These can be used as follows:

<%@ taglib prefix='surf' uri='http://www.springframework.org/tags/surf' %>
<surf:constructPreset preset='myPreset'>
   <surf:presetToken key='pageId' value='MyPage'/>
   <surf:presetToken key='templateId' value='MyTemplate'/>
   <surf:presetToken key='componentId' value='MyComponent'/>
</surf:constructPreset>


A preset is only used to provide an initial definition for objects that you wish to create multiple instances of. There is little point in having a preset if you only wish to use it once (you may as well just create a normal configuration file) and the parameterisation only allows simple String substitution rather than any kind of complex logical processing (that you could achieve with FreeMarker or JSP processing of model properties).

 

Since the purpose is to create multiple instances of the same combinations of ModelObject it is vitally important that page and template instance 'id' attributes are parameterised and that any scoped component definitions have their 'source-id' attribute configured to match the page or template instance id. For example, if you define a template and a page then their IDs should be parameterised and the pages 'template-type' attribute should used the same parameter as the template instances  'template-type' attribute.

 

Alternative Presets Manager

If you wish to use presets as part of a Share customization or are writing a Spring Surf application that has access to an Alfresco installation then you can make use of the default application context and your constructed preset ModelObjects will be stored in the repository store. However, if you are using Spring Surf for an application independently of Alfresco you will need to override the default application context.

 

The following configuration should work as it is currently used in the Spring Surf FVT application (the purpose of each bean definition is explained in-line):

 

The PresetManager requires a ModelObjectService to create and save ModelObjects. The default PresetManager uses a MultiObjectService that does NOT include access to the local file system. A ModelObjectService requires a PersisterService which requires a Persister which requires a Store. It is the Store that controls where the preset constructed ModelObjects will ultimately be persisted and to write to the local file system we need to use the LocalFileSystemStore.

<bean id='presets.model.object.service'>
   <property name='objectPersistenceService' ref='presets.object.persistence.service'/>
</bean>

<bean id='presets.object.persistence.service' >
   <property name='persisterService' ref='presets.persister.service'/>
</bean>

<bean id='presets.persister.service' >
   <property name='persister' ref='presets.persister'/>
   <property name='webFrameworkConfig' ref='webframework.config.element'/>
</bean>

<bean id='presets.persister' parent='webframework.sitedata.persister.abstract'>
   <property name='store' ref='presets.store'/>
   <property name='pathPrefix'><value>/</value></property>
</bean>

<bean id='presets.store' >
   <property name='root'><value>./WEB-INF</value></property>
   <property name='path'><value>/presetConstructs</value></property>
</bean>


We need to override the default PresetsManager (or we could define a new bean) and ensure that it references our ModelObjectService.

<bean id='webframework.presets.manager' >
   <property name='modelObjectService' ref='presets.model.object.service'/>
   <property name='searchPath' ref='webframework.presets.searchpath'/>
   <property name='files'>
      <list>
         <value>presets.xml</value>
      </list>
   </property>
</bean>


Finally we need to override the default Autowire service to add make our new persisters available (otherwise it won't be able to use any of the ModelObjects created from the PresetsManager).

<bean id='webframework.service.autowire' parent='webframework.service.autowire.abstract'>
   <property>
      <list>
         <ref bean='webframework.sitedata.persister.classpath'/>
         <ref bean='presets.persister'/>
      </list>
   </property>
</bean>

The end result of this configuration should be that when an object is instantiated from a preset it is stored on the local file system rather than in the Alfresco repository.

Outcomes