Web Quick Start Developer Guide

From alfrescowiki

Jump to: navigation, search

Contents

Overview

Architecture

Alfresco provides a scalable WCM platform for developing enterprise Web Content Management (WCM) solutions. An Alfresco WCM architecture can vary in design depending upon requirments regarding performance, scalability, security and redundancy. The below diagrams show simplified views of both a single and a two-tier approach.

Single tier WCM architecture: With a single tier architecture, Alfresco provides both content editorial services as well as content delivery services from a single Alfresco installation.

Single Tier WCM Architecture

Two-tier WCM Architecture: With a two-tier architecture, the editorial and delivery services are separated to use two Alfresco installations.

Two Tier WCM Architecture

What is Web Quick Start?

Web Quick Start is a sample application built on the Alfresco WCM platform. It provides an end-to-end WCM example including an authoring and publishing environment using Alfresco Share and a web application built using Spring MVC, Spring Surf and OpenCMIS. The web site is delivered dynamically using Alfresco as a CMIS runtime.

The primary design goals of the QS are to illustrate the power of the Alfresco WCM platform in an easy-to-install package and to provide developers with a strong starting point for their Alfresco implementations. Both of these goals are fundamentally aimed at getting both business people and developers up and running with the Alfresco WCM platform in as short a time as possible. The Alfresco core product has not been changed in any way, just extended by plugging in content model, behaviours, workflows, etc using the many standard hooks provided by the Alfresco product.

Web Quick Start consists of four parts:

  1. An [Alfresco Module Package] (AMP) that extends the Alfresco repository to support a generic website model
  2. An AMP that extends Alfresco Share for editiing content for the website, managing the structure of the website, and publishing content via workflow
  3. A JAR file that contains a Java API for accessing the website data held in the repository
  4. A web application (webapp) that, when deployed to a servlet container such as Tomcat, delivers a fictional financial news website. The webapp is a Spring MVC application, constructed using Spring Surf, and communicating with the Alfresco repository using the API mentioned above. As well as dynamically building the website from data held in the the Alfresco repository, the Quick Start webapp also provides examples of user generated content (UGC) whereby content is sent from the webapp back to the Alfresco repository.

Quick Start Components

The diagram below shows each of the QS components deployed in a single-tier architecture as described previously:

WQS Architecture

1. Alfresco Share Extensions

Alfresco Share is the primary editorial tool (along with the Web Editor for in-context changes) for managing the Web Quick Start website. Using Share, content editors and web producers alike have the ability to manage and control a wide variety of features including (but not limited to) content items, sections, navigation, templates, renditions, workflow, UGC and tagging. It is important to note that the core Alfresco Share code has not been modified to support Web Quick Start, only extended.

2. Alfresco Extensions

Site Structure and content - Web Quick Start provides a sample site structure and content populating each of the website sections. This allows for full site functionality to be evaluated once installed.

Publishing Mechanism - Web Quick Start includes a workflow driven publishing mechanism that allows for a clean separation between the editorial content and the approved live content. The Editorial and Live sections are implemented as part of the default Document Library site structure.

Content Model - The content model used by the system is designed to be generic enough to let you build many website solutions. So just by understanding the web application and altering it for your requirements you can get a head start in creating your own content managed enterprise website

Workflow - Web Quick Start provides example workflows for review and approval of content.

Renditions - Web Quick Start makes use of the Alfresco Rendition Service and provides multiple examples of content transformations and resizing.

3. Web Application

The Web Quick Start web application provides a richly-featured web application built using Spring MVC, Spring Surf and OpenCMIS. The web application can be used for both website preview (by the editorial team) and also for delivering the live website content.

4. Rendered Website

The rendered website within the diagram simply represents the delivered site via a browser, viewable by the editorial team and website visitors alike, depending upon configuration.

The Web Quick Start Model

The Web Quick Start (WQS) model is deliberately simple. A WQS website is made up of sections, assets, and asset collections. Sections are basically folders with additional metadata and behaviour, and assets are basically files with additional metadata and behaviour that are located inside sections. An asset collection is a named group of assets that is defined in a section. Each section may have zero or more asset collections. Each asset may have zero or more pieces of visitor feedback associated with it.


Wcmqs-basic-model.png

Website

A website contains precisely one section that is the root of a tree of sections. Each website is configured with a host name, port number, and root context. These attributes correspond to the URL used to address the home page of the website:

http://hostname:portno/context/

As an example, the QS website comes preconfigured with a website named "Quick Start Editorial" which has the following attributes when first loaded:

host name = localhost
port = 8080
context = wcmqs

which means that the site home page can be accessed at the URL "http://localhost:8080/wcmqs/". We'll use this URL in future examples throughout this guide.

It is possible configure websites so that one publishes to another. Each website may be associated with one other website that is its publishing target. Again, the QS comes with an example of this, with the Quick Start Editorial website pointing at the Quick Start Live website as its publishing target. The publishing mechanism is looked at in some detail later.

Finally, each website may have a list of configuration properties of the form "propertyname=propertyvalue". In the QS website there is just one property used: "isEditorial". If set to "true" then the templates used to render the QS site will include the markup needed to launch the Alfresco Web Editor.

Section

As mentioned above, a section is best thought of as a folder with additional metadata and behaviour. Each section is uniquely named within its parent section. Each website has one root section which is the only section whose parent is not another section. The name of a section is used to determine its URL at delivery time. So, for example, the "blog" section in the QS website is located at "http://localhost:8080/wcmqs/blog/". If you rename it so that it is named "blogs" instead, then its URL will change to be "http://localhost:8080/wcmqs/blogs/".

In addition to the normal metadata such as name, title, description, and so on, a section has a few other properties. One of these is used to configure template mappings. These determine which web template should be used to render a particular type of content in that section, and take the form of name/value pairs where the content type is the name and the template name is the value. So, for example, a setting of "ws:article=articlepage2" means that the template named "articlepage2" should be used to render content of type "ws:article" in that section. The way in which template mappings are processed is described later.

Another attribute of a section worthy of note is the rendition configuration. On each section it is possible specify what renditions you want to create for assets that are placed in that section. For example, you may want to create a thumbnail version of any asset that has a MIME type of "image/jpeg" or you may want to trigger that same thumbnail rendition for any asset of type "Image". Each rendition setting is, again, a name/value pair with the name being either a MIME type or a content type and the value being the name of the rendition definition to run. This mechanism is described fully in the rendition configuration section of this guide.

Asset

An asset is any piece of content that is placed in a section. An asset doesn't have very much "special" metadata, but does contain summary information about visitor feedback that has been received for that asset such as the number of comments received and the average rating. This is described further in the section about processing visitor feedback. An asset may optionally specify a presentation template that is to be used when rendering it. If set, this overrides the template mappings set on its parent section. You can read about this in the section about how templates are resolved.

Asset Collection

As the name suggests, an asset collection groups together a number of assets. Each asset collection is associated with a particular section and is uniquely named within that section. There are two general types of asset collection: "dynamic" and "static". A dynamic asset collection is configured with a search query that is run periodically. The assets found by the query are placed in the collection. A static asset collection has no search query defined, and contains assets that are placed in it either manually or through some other automated mechanism.

In the case of a dynamic collection it is possible to specify the maximum number of assets it should contain and the interval between re-runs of its query. Dynamic asset collections are described in more detail later.

Visitor Feedback

Visitor feedback is information that has been passed back through a rendered website into the repository. It may be used for numerous purposes, but in the Quick Start example it is used to record comments about an asset and requests from the "Contact us" page. A piece of visitor feedback has a number of generally useful properties such as the name and email address of the person who submitted it, a text property for the message or comment, and an integer property that can be used for ratings. It is also possible to have different categories of visitor feedback.

Received visitor feedback is added into a Share data list and can be processed in different ways depending on its category. For example, the Quick Start processes comments about assets and "contact us" requests differently. The way in which visitor feedback is processed is described in the section entitled "Processing Visitor Feedback".

Inside the core Web Quick Start module

The Web Quick Start content model

The Alfresco data dictionary is extended by a new content model named ws:webSiteModel. The types and aspects defined in this model are used by Web Quick Start to represent different elements of the website. Some of the types in the model correspond directly to parts of the website itself, others are used solely to provide structure to data in the repository. The model is designed to be as generic as possible, so, although it is used to render a financial web site in the Web Quick Start, it can be used for many different web sites with the necessary changes being limited to the actual content itself. The website model is defined in wcmquickstartmodule/config/alfresco/module/org_alfresco_module_wcmquickstart/model/webSiteModel.xml. The model is bootstrapped into the Alfresco data dictionary using the context model-context.xml in the same directory. The following subsections describe the new types:-

The ws:website type

The ws:website type is derived from the cm:folder type and represents a website. This type has properties that apply to an entire website, such as the host name, port, and context of the web application that delivers the website. In the Quick Start data in Alfresco Share, the folder at Documents/Alfresco Quick Start/Quick Start Editorial is of this type. You can see the host name, port and context in the folder's metadata.

The ws:section type

The ws:section type is derived from the cm:folder type and represents a section of a website. The website is modelled as a tree of sections. A section defines an element in the website navigation and can contain child sections and web assets, such as the section's landing page, articles, collections of articles, and images. In the Web Quick Start data in Alfresco Share, the Documents/Alfresco Quick Start/Quick Start Editorial/root/blog folder is of this type.

The ws:webroot type

The ws:webroot type is derived from the ws:section type and represents the root of a website's tree of sections. This type extends the section type so therefore a webroot folder *is* also a section. In the Web Quick Start data in Alfresco Share, the Documents/Alfresco Quick Start/Quick Start Editorial/root folder is of this type.

The ws:webassetCollectionFolder type

The ws:webassetCollectionFolder type is derived from the cm:folder type and is used to hold asset collections. Each section folder has a webassetCollectionFolder below it named "collections" in which all of that section's asset collections are placed. In the Web Quick Start data in Alfresco Share, the Documents/Alfresco Quick Start/Quick Start Editorial/root/blog/collections folder is of this type.

The ws:webassetCollection type

The ws:webassetCollection type is derived from the cm:folder type and represents an asset collection. In the Web Quick Start data in Alfresco Share, the Documents/Alfresco Quick Start/Quick Start Editorial/root/blog/collections/latest.articles folder is of this type.

The ws:indexPage type

The ws:indexPage type is derived from the cm:content type and represents an index page of a section (also known as the section's landing page). Each section has an index page. In the Web Quick Start data in Alfresco share, the Documents/Alfresco Quick Start/Quick Start Editorial/root/blog/index.html asset is of this type.

The ws:article type

The ws:article type is derived from the cm:content type and represents any piece of text-based content such as a news article or a blog post. The article type defines a few associations that allow an article to be linked with related articles and a couple of images. In the Web Quick Start data in Alfresco Share, the Documents/Alfresco Quick Start/Quick Start Editorial/root/blog/blog1.html file is of this type.

The ws:image type

The ws:image type is derived from the cm:content type and is used for general image assets. In the Web Quick Start data in Alfresco share, the Documents/Alfresco Quick Start/Quick Start Editorial/root/news/detail_img1.jpg file is of this type. The images that can be associated with articles must be of this type.

The ws:visitorFeedback type

The ws:visitorFeedback type is derived from the dl:dataListItem type and represents feedback that has been submitted by visitors to a website. The intention is for this type to be sufficiently generic to be useful for a number of different types of feedback including comments, reviews, ratings, and questions. Each website has a Share data list created for it into which items of visitor feedback are placed. When first installed, Quick Start does not have any visitor feedback. Submitting a comment to a blog post or a "contact us" request from the website will create a node of this type in the repository.

The ws:visitorFeedbackSummary type

The ws:visitorFeedbackSummary type is derived from the cm:cmobject type and is used to record summary information about visitor feedback received for a given asset such as the number of comments received and the average rating given.

The ws:publishqueueentry type

The ws:publishqueueentry type is derived from the cm:cmobject type and is used to record nodes that have been queued up for publishing.

The ws:webasset aspect

The ws:webasset aspect is derived from the cm:titled aspect and is used to mark any piece of content that is addressable through a website.

Among other things, a web asset (a node with the ws:webasset aspect) has two multi-valued NodeRef properties (ws:parentSections and ws:ancestorSections) that contain the identifier(s) of the section(s) in which the asset is placed and its ancestor sections. When an asset is created in, moved to, or removed from a section, these properties are updated to reflect the asset's new location. This is done to make certain kinds of common queries very fast.

Behaviours

Numerous custom behaviours are provided in the core Quick Start module. This section looks at each of them in turn, breaking them into groupings based on the type or aspect to which they apply.

Behaviours related to the ws:section type

The behaviours that have been defined for the ws:section type can be found in the class org.alfresco.module.org_alfresco_module_wcmquickstart.model.SectionType. They relate to three primary events: when a section is created; when a child node is added into or removed from a section; and when the roll-up of tag usage in a section is updated.

The creation of a new section causes the automatic creation of its index page (of type ws:indexPage), its asset collection folder (of type ws:assetCollectionFolder), and a few default asset collections. When a new folder is created inside a section then that folder is automatically converted into a section. The set of default asset collections can be altered by overriding a bean with identifier "wqsmodule_sectionAssetCollections". By default, this bean looks like this:

  <bean id="wqsmodule_sectionAssetCollections" class="java.util.ArrayList" >
     <constructor-arg>
        <list>
           <bean class="org.alfresco.module.org_alfresco_module_wcmquickstart.model.AssetCollectionDefinition">
               <property name="name" value="latest.articles" />
               <property name="title" value="Latest Articles" />
               <property name="maxResults" value="3" />
               <property name="queryType" value="alfrescoCmis" />
               <property name="query"><value>SELECT d.* FROM cmis:document AS d JOIN ws:webasset AS wa ON d.cmis:objectId = wa.cmis:objectId WHERE d.cmis:objectTypeId='D:ws:article' AND in_tree(d, '%{section:.}') ORDER BY wa.ws:publishedTime DESC</value></property>
           </bean>
           <bean class="org.alfresco.module.org_alfresco_module_wcmquickstart.model.AssetCollectionDefinition">
               <property name="name" value="section.articles" />
               <property name="title" value="Articles" />
               <property name="queryType" value="alfrescoCmis" />
               <property name="query"><value>SELECT d.* FROM cmis:document AS d JOIN ws:webasset AS wa ON d.cmis:objectId = wa.cmis:objectId WHERE d.cmis:objectTypeId='D:ws:article' AND in_tree(d, '%{section:.}') ORDER BY wa.ws:publishedTime DESC</value></property>
           </bean>
           <bean class="org.alfresco.module.org_alfresco_module_wcmquickstart.model.AssetCollectionDefinition">
               <property name="name" value="featured.articles" />
               <property name="title" value="Featured Articles" />
           </bean>
        </list>
     </constructor-arg>
  </bean>

This means that each new section is automatically endowed with two dynamic asset collections ("latest.articles" and "section.articles") and one static asset collection ("featured.articles"). Note that in this configuration the "queryType" of a dynamic asset collection may be either "alfrescoCmis" (the default) or "lucene".

When a file is created in a section then one of four things happen. If the file is an image (as determined by its MIME type) then the file is given the ws:image type and marked as a web asset. If the file is text-based (again, as determined by its MIME type) then it is given the ws:article type and marked as a web asset. If the file is neither an image nor an article then it is simply marked as a web asset. It is possible to configure a set of content types that are ignored by this processing by overriding a Spring bean with the id "wqsmodule_sectionTypesToIgnore". By default, this bean looks like this:

  <bean id="wqsmodule_sectionTypesToIgnore" class="java.util.TreeSet">
     <constructor-arg>
        <set>
           <value>ws:visitorFeedback</value>
           <value>ws:indexPage</value>
        </set>
      </constructor-arg>
  </bean>

You may replace it with another bean with the same identifier and add the types you'd like to be ignored. Please keep the types from the default configuration. Note that the ignoring mechanism only ignores the specific types listed in this bean - it does not cascade down the type hierarchy.

Behaviours related to the ws:webasset aspect

The behaviours that have been defined for the ws:webasset aspect can be found in the class org.alfresco.module.org_alfresco_module_wcmquickstart.model.WebAssetAspect. There are four main behaviours associated with this aspect: onCopyNode; onContentUpdate; and beforeDeleteNode.

As mentioned earlier, an asset keeps a record of which sections it's in. When an asset is copied, however, we want to ensure that the copy recalculates its parent sections. Therefore a behaviour is hooked into the onCopyNode policy that ensures the ws:parentSections property of the copied node is not taken across to the copy.

Assets often have renditions associated with them, and Quick Start provides a hierarchical configuration mechanism to determine what renditions should be generated for certain assets in given sections. This configuration is processed whenever an asset is created or has its content updated via behaviours hooked into the onContentUpdate and onAddAspect policies.

Finally, when an asset is deleted, a behaviour is invoked that causes the deletion to be queued up for publishing. This means that, in Quick Start, a deletion of an asset in the editorial system is quickly reproduced on the live system - a deletion is not subject to workflow.

Behaviours related to the ws:visitorFeedback type

When visitor feedback is received it may contain the identifier of an asset to which it relates. This is stored as a NodeRef property initially, but a behaviour is used to translate this property into an association. This behaviour is defined in the class org.alfresco.module.org_alfresco_module_wcmquickstart.model.VisitorFeedbackType

Behaviours related to the ws:visitorFeedbackSummary type

When the properties on a visitorFeedbackSummary node change then a behaviour that is hooked into the onUpdateProperties policy cause the summarised comment count and average rating to be copied onto the asset to which the summary is related. This behaviour is defined in the class org.alfresco.module.org_alfresco_module_wcmquickstart.model.VisitorFeedbackSummaryType

Behaviours related to the cm:taggable aspect

All assets are decorated with the cm:taggable aspect (cm:taggable is a mandatory aspect of the ws:webasset aspect). The tags set on a taggable node are normally stored as a multi-value NodeRef property, but a behaviour (in the class org.alfresco.module.org_alfresco_module_wcmquickstart.model.TaggableAspect) copies the tag values into a multi-value text property (ws:tags) which is then used for tag-based queries. This behaviour is hooked into the onUpdateProperties policy.

Behaviours related to the cm:tagscope aspect

All sections are decorated with the cm:tagscope aspect which is used to automatically "roll up" a summary of which tags have been applied to assets in that section and its subsections. The tagscope aspect uses a content property to hold the summary information which makes it difficult to get hold of through CMIS. A behaviour hooked onto the onContentPropertyUpdate policy on this aspect copies the information into a couple of multi-value properties (one for the tag value and another for the tag count). This behaviour can be found in the class org.alfresco.module.org_alfresco_module_wcmquickstart.model.TagScopeAspect

Dynamic Asset Collections

Any asset collection that has a search query defined on it is referred to as a dynamic asset collection. Periodically (as defined by the refresh interval on the asset collection) this search query is executed in the background and the resulting assets are bound into the asset collection.

The search query may use either Lucene or CMIS query languages, and may contain a special placeholder that is automatically replaced by a section identifier prior to executing. This placeholder takes the form ${section:<arg>} where <arg> may be any of the following:

  • . refers to the section that "owns" the asset collection being processed. ${section:.}
  • .. refers to the section above the one that owns the asset collection being processed. ${section:..}
  • / refers to the root section of the website that contains the asset collection being processed. ${section:/}
  • abcdef refers to the section named "abcdef" below the section that owns the asset collection being processed ${section:abcdef}

It is also possible to chain these together to form a complete path. For example ${section:/news/./global/../companies} would resolve to the identifier of the section "/news/companies".

Scheduled Jobs

Several classes are provided that do work at scheduled times. These classes can all be found in the org.alfresco.module.org_alfresco_module_wcmquickstart.jobs package, and provide processing of publishing queues, visitor feedback, and dynamic asset collections. Each of the classes has a scheduled time at which it will run, specified as a cron expression in the WQS properties file. The Spring context file for all the scheduled job classes is config/alfresco/module/org_alfresco_module_wcmquickstart/job-context.xml.

Processing the Publish Queue

Articles are placed on a website's publishing queue when they have gone through the approval workflow, or on deletion. The PublishQueueProcessor class is responsible for processing all websites' publishing queues. It runs periodically, by default every minute, and publishes any nodes found in each publishing queue to the target website. The property used to control this schedule is "wcmqs.publishQueueProcessor.schedule". Its default value is "0 0/1 * * * ?".

Processing Visitor Feedback

Each item of visitor feedback may be related to a particular web asset. Each web asset for which there is any visitor feedback is "stamped" with summary information such as the total number of comments received and the average rating given. The FeedbackProcessor class finds any visitor feedback that has not been processed yet, and incorporates it into the summary information for each asset. It runs periodically, by default every 30 seconds. The property used to control this schedule is "wcmqs.feedbackProcessor.schedule". Its default value is "0/30 * * * * ?".

The summary information for an asset is not held directly on the asset, instead it is held in a separate summary node. This is because an asset is typically published from the editorial site to the live site. On each site visitors will create feedback items. There may be 2 or 3 test feedback comments for an asset on the editorial site, but several thousand feedback comments for the same asset on the live site. When the asset is re-published from the editorial site to the live site, the whole asset is replaced. If the summary statistics were part of the asset, the live summary statistics would be incorrectly replaced by the test summary statistics. Holding the statistics in a separate, unpublished node removes the problem by making the summary statistics for an asset site-specific.

Each item of visitor feedback is typed. The type is simply recorded in a text property (ws:feedbackType), and can have any value that you wish. Out of the box, the Web Quick Start uses two types of visitor feedback: "Comment" and "Contact Request". Each different type of visitor feedback may have a handler configured that knows what to do with it. When the feedback processor finds an unprocessed item, it looks at its type and passes processing off to the appropriate handler. Adding a handler for a new type of feedback can be done by creating a class that extends the abstract class "org.alfresco.module.org_alfresco_module_wcmquickstart.jobs.feedback.FeedbackProcessorHandlerBase" and overriding the processorCallback method. An example of a handler is as follows:

  <bean id="commentFeedbackProcessorHandler" parent="feedbackProcessorHandler"
     class="org.alfresco.module.org_alfresco_module_wcmquickstart.jobs.feedback.CommentFeedbackProcessorHandler">
     <property name="feedbackType" value="Comment" />
  </bean>

The important parts of this are specifying the parent "feedbackProcessorHandler" and setting the feedbackType property to match the type of feedback that the handler knows how to handle.

Processing Dynamic Asset Collections

Each dynamic asset collection has a "Minutes To Query Refresh" property that is set in the asset collection's metadata. The DynamicCollectionProcessor class is responsible for refreshing dynamic collections. It runs periodically, by default every minute, and iterates over all the dynamic asset collections. For each one that is due to be refreshed it executes the configured query, refreshes the asset collection with the results, and updates the time at which that asset collection should next be refreshed using the configured "minutes to refresh" property. The property used to control the schedule for this processing is "wcmqs.dynamicCollectionProcessor.schedule". Its default value is "0 0/1 * * * ?".

Template Maps

Each section in a Web Quick Start site may optionally specify which presentation template should be used for which type of content in that section. This information is stored as a multi-value text property on a section ("ws:sectionConfig") and each value takes the form of "my:typename=MyTemplateName". In the Web Quick Start webapp (which uses Spring Surf) the template name is the name of the Surf page, but it could be something else if you've built your webapp using a different framework (the name of a JSP, for example).

To take an example, at the time of writing (8/Nov/2010) the blog section has the following template mappings defined: ws:indexPage=sectionpage2,ws:article=articlepage2

This means that an asset of type ws:indexPage in this section (the landing page) will use the template called "sectionpage2" and an asset of type "ws:article" will use the template called "articlepage2". Naturally, you will almost certainly have different templates and types in your implementation - just change the mappings to suit your needs.

Renditions

The Alfresco Rendition Service is used throughout Web Quick Start to reformat content dynamically for the web. It is used for resizing images, producing flash previews of pdf documents, and producing PDF renditions of business documents.

Rendition Definitions

The set of renditions that Web Quick Start uses is configured in the Spring file config/alfresco/module/org_alfresco_module_wcmquickstart/bootstrap/rendition-context.xml. Each definition specifies the rendering engine, and the parameters and values to be used when creating the rendition definitions. You can find a table of the default Web Quick Start rendition definitions in the Reference section below. You are likely to want to change the rendition definitions available for your website, and this can be done by overriding the Spring bean with the identifier "wqsmodule_renditionDefinitions". This is simply an ArrayList of rendition definitions. A truncated version of the default configuration is as follows:

   <bean id="wqsmodule_renditionDefinitions" class="java.util.ArrayList">
       <constructor-arg>
           <list>
               <bean class="org.alfresco.module.org_alfresco_module_wcmquickstart.rendition.BootstrapCompositeRenditionDefinition">
                   <property name="name" value="smallThumbnail"/>
                   <property name="renderingEngineName" value="compositeRenderingEngine"/>
                   <property name="definitions">
                       <list>                            
                           <bean class="org.alfresco.module.org_alfresco_module_wcmquickstart.rendition.BootstrapRenditionDefinition">
                               <property name="renderingEngineName" value="reformat"/>
                               <property name="parameters">
                                   <map>
                                       <entry key="mime-type" value="image/jpeg"/>          
                                       <entry key="runAs" value="System"/>   
                                   </map>
                               </property>
                           </bean>
                           <bean class="org.alfresco.module.org_alfresco_module_wcmquickstart.rendition.BootstrapRenditionDefinition">
                               <property name="renderingEngineName" value="imageRenderingEngine"/>
                               <property name="parameters">
                                   <map>
                                       <entry key="xsize" value="72"/>                            
                                       <entry key="ysize" value="72"/>                                                        
                                       <entry key="maintainAspectRatio" value="false"/>
                                       <entry key="runAs" value="System"/>
                                   </map>
                               </property>
                           </bean>
                       </list>                    
                   </property>
               </bean>
               
               <!-- ....other rendition definitions are configured here....  -->
                 
           </list>
       </constructor-arg>
   </bean> 

The example shown above creates a rendition definition named "smallThumbnail" (which will be placed in the "ws" namespace). This is a composite rendition that first transforms the image to the JPEG format and then resizes it to be 72x72 pixels.

Rendition Mappings

Web Quick Start maps a content type or mime type to one or more rendition definitions. The mapping is specified in a section's Rendition Config metadata. A section can have its own list of mappings, or can inherit the parent section's mappings, or have no rendition mappings. For example, when the Web Quick Start web site data is loaded, the root section has the following mappings:

ws:image=ws:smallThumbnail,ws:image=ws:imagePreview,application/pdf=ws:swfPreview

So in the root section, or a descendant section that inherits its rendition config from the root section, any asset of type "ws:image" will have two renditions generated ("ws:smallThumbnail" and "ws:imagePreview"), and any asset with the "application/pdf" MIME type will have a Flash preview rendition generated (the "ws:swfPreview" rendition).

Rendition Processing

As described in Behaviours, the processing of renditions is governed by Alfresco behaviours. Whenever a web asset is created or has its content updated the appropriate renditions for that type of asset are recreated by behaviours hooked into the onContentUpdate and onAddAspect policies (in the WebAssetAspect class).

Publishing

  • Workflow
    • Review and Publish
    • Publish Site Structure
  • Transfer Service
    • Creates/edit
    • Deletes
  • Publishing Job


Quick Start Editorial : This is where you manage your site's structure and content. Content can be previewed in the webapp and edited in-context using the Alfresco Web Editor (AWE). Once an article is ready it is submitted for approval and the publishing mechanism will copy it to Quick Start Live.
Quick Start Live : This is the content that the web application serves as your production website.

The Quick Start API

The Quick Start API provides a client application with a means of interacting with the Quick Start model held in an Alfresco repository. It is used by the Quick Start webapp.

Building the API

If you obtain the source code from Alfresco's Subversion repository, the Quick Start API project is located in the folder code/root/modules/wcmquickstart/wcmquickstartclientapi. It can be built from the code/root folder using the package-wcmquickstart-clientapi ant target:

ant package-wcmquickstart-clientapi

The result of this is a JAR file named "alfresco-wcmqs-clientapi-x.y.z.jar" in the project's "build/dist" folder.

This JAR file contains everything necessary to expose the Quick Start model to client applications such as the Quick Start webapp. At the time of writing (22/Sep/2010), this JAR file has the following dependencies:

chemistry-opencmis-client-api-0.1.0-incubating-SNAPSHOT.jar
chemistry-opencmis-client-bindings-0.1.0-incubating-SNAPSHOT.jar
chemistry-opencmis-client-impl-0.1.0-incubating-SNAPSHOT.jar
chemistry-opencmis-commons-api-0.1.0-incubating-SNAPSHOT.jar
chemistry-opencmis-commons-impl-0.1.0-incubating-SNAPSHOT.jar
commons-codec-1.3.jar
commons-collections-3.1.jar
commons-httpclient-3.1.jar
commons-logging-1.1.jar
commons-pool-1.4.jar
ehcache-core-2.0.0.jar
json.jar
org.springframework.aop-3.0.0.jar
org.springframework.asm-3.0.0.jar
org.springframework.beans-3.0.0.jar
org.springframework.context-3.0.0.jar
org.springframework.context.support-3.0.0.jar
org.springframework.core-3.0.0.jar
org.springframework.expression-3.0.0.jar
org.springframework.jdbc-3.0.0.jar
org.springframework.orm-3.0.0.jar
org.springframework.transaction-3.0.0.jar
slf4j-api-1.5.11.jar
slf4j-log4j12-1.5.11.jar
log4j-1.2.15.jar

All of these dependencies can be found in the Alfresco "3rd Party" project, or, in the case of the OpenCMIS libraries, in the Quick Start Client API project itself.

Configuring the API

There is a single properties file that contains a few properties that are used to configure the API. This file is named "wcmqs-api.properties". The default file is located in the clientapi JAR file (in the "alfresco" folder), but can be overridden by placing a file with the same name on the classpath before the clientapi JAR. For example, if you're using the WQS API from within a JEE webapp then place a file named "wcmqs-api.properties" in the folder "WEB-INF/classes/alfresco/".

The properties that are contained by this file are:

  • wcmqs.api.alfresco is the base URL for the Alfresco repository. The default value is "http://localhost:8080/alfresco"
  • wcmqs.api.user is the username that is used to authenticate the WQS API to Alfresco. Clearly it is recommended that this is changed.
  • wcmqs.api.password is the password used to authenticate the WQS API to Alfresco. Clearly it is recommended that this is changed.
  • wcmqs.api.alfresco.cmis is the URL that the API will use to reach the CMIS interface. The default value is "%{wcmqs.api.alfresco}/service/cmis"
  • wcmqs.api.alfresco.webscript is the base URL that the API will use to invoke webscripts running on the Alfresco repository. The default value is "%{wcmqs.api.alfresco}/service/api/"

Changes in 3.4.b Community: Note that, from 3.4.b onwards, it is possible to override individual properties by placing them in a file named "wqsapi-custom.properties" located on the classpath in a directory "/alfresco/extension/" (under "/shared/classes/" in a Tomcat installation, for example). There are also three additional properties available:

  • wcmqs.api.repositoryPollMilliseconds is the time that the API will wait between checks for the repository being available. This mechanism ensures that the webapp can be started before the repository. The webapp will connect when the repository becomes available. By default, this is set to 2000 milliseconds.
  • wcmqs.api.sectionCacheSeconds is the time that the API will cache section objects before reloading them from the repository. By default, this is set to 60 seconds.
  • wcmqs.api.websiteCacheSeconds is the time that the API will cache website objects before reloading them from the repository. By default, this is set to 300 seconds.

Accessing the API

The API is wired together with a Spring application context that is embedded in the JAR file at the location "/alfresco/wcmqs-api-context.xml". If the API is being used inside a Surf application then this context file will be automatically bootstrapped on start up, otherwise you will have to load it up yourself.

In the following subsections we'll take a look at the main interfaces that you'll interact with as a user of the API. Later we'll dive under the covers and look at some of the classes that do the actual work.

The Website Service

The starting point for most applications that use the Quick Start API will be the Website Service. This is represented by the abstract class org.alfresco.wcm.client.WebSiteService, an instance of which is defined in the Spring context file with the id "webSiteService". The website service has only a few operations defined, one returning a collection of all the websites that have been configured in the repository and another returning a specific website based on host name and port number.

A website is represented by an object with the interface org.alfresco.wcm.client.WebSite. This gives read access to the properties of a website as well as providing operations to retrieve assets and sections given a path. For example:

WebSite website = websiteService.getWebSite("localhost", 8080);
Section blogSection = website.getSectionByPath("/blog/blog1.html");
Asset blogPost1 = website.getAssetByPath("/blog/blog1.html");

The website service also gives access to another service which is described a little later: the UGC (user-generated content) Service.

The Section interface

As you would expect, an object that implements the Section interface represents a section of a website. It provides a number of operations that give access to child sections (getSections, getSection) and assets (getAsset, getIndexPage), as well as configuration settings on the section (getTemplateMappings, getExcludeFromNav). It also offers access to search functionality with a few flavours of "search" operations:

SearchResults search(Query query);
SearchResults search(String phrase, int maxResults, int resultsToSkip);
SearchResults searchByTag(String tag, int maxResults, int resultsToSkip);
Query createQuery();

Results from these searches are returned wrapped in a SearchResults object that exposes a list of the actual results, the Query that was executed to obtain the results, and the total number of results. Each result is represented as an object of type SearchResult. This is an interface that extends the Asset interface (each search result represents an asset) and adds the search score as a readable property.

In 3.4.b and beyond it is also possible to access the asset collections that are defined on a section through the getAssetCollection operation on the Section interface.

The Asset interface

Objects with the Asset interface are used to represent Quick Start assets. This interface exposes operations to read the asset's properties and to discover any related assets and renditions that it may have. It's worth pointing out that both relationships and renditions are named, so the operations that provide access to them allow the caller to select by name.

Another operation provided on any asset is "getTemplate". This does a calculation based on the asset's type and the template mappings defined on the section it's in to work out which template should be used to render it.

The Asset Collection Factory

Currently, asset collections are accessed through the asset collection factory which is represented by an object exposing the org.alfresco.wcm.client.CollectionFactory interface. There is an asset collection factory bean defined in the Spring application context with the id "collectionFactory". In 3.4.b and beyond, asset collections can be accessed from the Section interface - "Section.getAssetCollection(String)"

The CollectionFactory interface is a very simple one with just two operations currently (23/Sep/2010). One operation returns an entire asset collection given the section identifier and asset collection name, and the other provides the ability to retrieve just a portion of the identified asset collection by specifying the maximum number of assets to return and the number of assets to skip over. Both return an object with the AssetCollection interface.

The AssetCollection interface

The AssetCollection interface provides access to the underlying list of assets as well as the properties of the asset collection itself such as its name, title, and description.

Local data caches in the API

The Web Quick Start API provides a balance of editorial control against speed of content delivery by keeping the data needed to render web pages local to the web app. This massively reduces the load placed on the repository and enables very fast dynamic page rendering.

There are four different caches provided by the WQS API which are described here.

Website cache

Each website held in the Alfresco repository has its own hierarchy of sections and assets. A single repository may hold many different websites, and a single WQS web application can be used to deliver any number of those websites. The website cache enables the fast lookup of which website hierarchy is relevant for a given HTTP request along with configuration settings for that website (whether it is an editorial site or not, for example). Generally, the websites don't change very frequently, so this cache is configured to refresh every five minutes. This interval can be adjusted with the configuration property "wcmqs.api.websiteCacheSeconds".

Section cache

Once the website has been looked up in the website cache the next job is to resolve the section that is being addressed. This also needs to be extremely fast, so the section hierarchy is also held in memory by the API. As well as providing very fast resolution of the requested path to the appropriate section and asset in that section, this cache is also used to quickly determine which template is to be used to render the page. The section hierarchy doesn't change very often in a website either - this cache is configured to refresh every 60 seconds by default. This setting can be changed with the property "wcmqs.api.sectionCacheSeconds".

Asset Collection cache

Asset collections are intended to provide very fast delivery of arbitrary lists of assets - a very common feature of any website. Most coupled or "fried" web delivery systems carry out queries against the repository at request time to provide this kind of capability, and this seriously impacts on their ability to perform well under load. With asset collections, Web Quick Start removes this overhead and enables these user-defined asset lists to be efficiently cached in the web tier. The asset collection cache is implemented as a caching proxy over the asset collection factory in the API.

For a dynamic asset collection the cache uses knowledge about when the asset collection was last refreshed and the frequency at which it is refreshed to make intelligent decisions about when to check the repository for any updates. For example, if a dynamic asset collection is configured to be refreshed every 15 minutes and the copy that was last loaded from the repository was refreshed at 10:15 then the API will not ask the repository for that asset collection again until 10:30.

For a static asset collection the cache periodically checks the modified time in the repository and only reloads the asset collection when it changes.

Asset cache

The asset cache is similar to the asset collection cache and holds properties and associations for each asset that is accessed through the API. An asset in the cache is refreshed only when the asset is found to have changed in the repository (by checking its modified time periodically).

When the content of an asset is read for the first time the content stream is also cached in a temporary file store in the web tier. This, again, helps reduce the load on the repository and means that a web app built using the WQS API typically delivers most of its content either from memory or from local disk.

Template Map Processing by the API

As mentioned before, each section may define mappings between content types and presentation templates. For example, if you want content of type "ws:article" in a given section to be rendered using a template named "articlepage1" then you would specify a template mapping on that section like this: ws:article=articlepage1. Additionally, each asset may have a template explicitly specified for it. In this case, just the template name is specified: "articlepage1". If a template is specified on the asset itself then it will be used in preference to any template mappings that are set on its parent sections, and the rest of the description here is not relevant.

It's important to understand how the section template mappings are processed by the WQS API. For the sake of this example we shall assume that there is a section structure "root/news/global" and a type hierarchy "cm:content" -> "ws:article" -> "myapp:pressrelease". We shall also assume the following template mappings have been defined:

root: cm:content=generalpage, myapp:pressrelease=pressreleasepage news: ws:article=articlepage global: myapp:blogpost=blogpage

Now let's take an example of a press release being requested from the "global" section. First we look up the type hierarchy to see if we can find a match on the current section. Since myapp:pressrelease is not derived from myapp:blogpost, there is no suitable match found. We therefore move up to the news section and try looking up the type hierarchy again. myapp:pressrelease is derived from ws:article, so we find a match and return the template name "articlepage". So the process is: look up the type hierarchy on the current section first; if no match is found move to the parent section and then look up the type hierarchy again there; repeat until the root section has been processed.

Introducing the Web Application

The Quick Start web application is a Spring Surf application, and as such uses the extremely popular Spring MVC framework. In this section we'll take a look at how the webapp works and how it's wired together.

Having checked out the source code from the Alfresco Subversion repository, you can find the source code for the Quick Start webapp in the folder "root/modules/wcmquickstart/wcmquickstartwebsite". Any references to source file locations in this section assume this as the starting point.

The wiring

Often when examining a Spring-based application the hardest part is working out where and how all the wiring takes place. This section takes a look at the Quick Start webapp from this point of view.

The starting point is the web.xml file ("source/web/WEB-INF/web.xml" in the source). This is a fairly short file, and the part that we'll concentrate on right now is the part that kicks off all the Spring magic: the dispatcher servlet.

  <servlet>
    <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/classes/web-application-config.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

This DispatcherServlet object loads up a Spring application context using the referenced configuration file: "/WEB-INF/classes/web-application-config.xml". This file can be found in the config folder in the source, and just causes two other Spring configuration files to be loaded: "surf-config.xml" first and then "wcmqs-webapp-context.xml"

The first of these ("config/surf-config.xml") loads up the Spring Surf and Spring Webscripts extensions, and then configures a default request handler:

    <bean id="handlerMappings" parent="webframeworkHandlerMappings">
        <property name="order" value="2" />
        
        <!-- Override list of interceptors defined in webframeworkHandlerMappings so that we can add our own. -->
        <property name="interceptors">
            <list>
                <ref bean="requestContextInterceptor"/>
                <ref bean="cmisSessionInterceptor"/>
                <ref bean="applicationDataInterceptor"/>
            </list>
        </property>    

        <property name="defaultHandler">
    	    <bean class="org.alfresco.wcm.client.controller.GenericTemplateAssetController">
    	        <!--  List of pages which we don't want to look up in the repository. These will be handled by Surf instead. -->     	    
    	        <property name="staticPages">
    	           <set>
                       <value>\/messages\.js</value>
    	           </set>
    	        </property>
    	    </bean>     		
    	</property>
    </bean>

This creates an instance of the class "org.alfresco.wcm.client.controller.GenericTemplateAssetController", which is a Spring MVC controller, and also puts in place three interceptors that Spring will invoke prior to passing a request to the controller. The first of these, "requestContextInterceptor" is a Spring Surf object that is necessary when using Surf as a Spring view resolver. The next two, "cmisSessionInterceptor" and "applicationDataInterceptor" are beans that are defined by Quick Start in the file "wcmqs-webapp-context.xml" which we'll look at in greater detail later. The cmisSessionInterceptor ensures that there is a CMIS session available throughout the processing of the request (accessible through the CmisSessionHelper class), and the applicationDataInterceptor uses the requested host name, port number, and path to obtain the appropriate website, section, and asset from the Quick Start API. It then places these resolved objects onto the request context with the names "webSite", "section", and "asset" so that they can be used while processing the request. (Note: from 3.4.b onwards, the resolved WebSite object is placed on the request context with two names: "webSite" (as before) and "website")

The other Spring context file ("wcmqs-webapp-context.xml") is used to wire together everything else. There are numerous objects defined in this file, but the most important ones are the three other controllers (asset, search, and rss), the dynamic page view resolver (used to work out which template to use for a given request), the interceptors mentioned earlier, and a "model decorator" which is used to inject Freemarker directives into the model that are then available to FreeMarker templates.

The Spring controllers

There are four Spring controllers configured in the Web Quick Start webapp. In this section we'll look at each in turn.

org.alfresco.wcm.client.controller.GenericTemplateAssetController

This is the default controller. It actually doesn't do very much - the only important thing is that it redirects a request for a resource named "index.html" to its parent section (so "/news/global/index.html" is redirected to "/news/global/"). More important are the interceptors that are configured to sit in front of this controller. These have been described in the previous section.

org.alfresco.wcm.client.controller.StreamedAssetController

The streamed asset controller is configured to handle all requests that are addressed at a URI with the pattern "/asset/**". The structure of the expected URI is "/asset/uuid/filename" where the UUID is the id of the asset being requested and the (optional) filename is the name of the asset being requested. This controller is useful if you want to generate "perma-links" for assets, since the URLs will not change even if the asset moves (assuming you omit the filename). It's also useful if you have the identifier for an asset, but you don't know where it is located. Search results may be one example of this.

If the addressed asset can be rendered through a template then this controller will make that happen, otherwise it will stream the asset content back "raw" to the browser. The controller will look for a request parameter named "rendition" and, if present, will resolve and stream back the named rendition of the specified asset.

org.alfresco.wcm.client.controller.SearchFormController

This controller is used to process the search form, execute the query against the repository, and place the results on the model ready to be rendered. This controller is an example that makes use of Spring annotations, and is configured to respond to requests for "/search.html".

org.alfresco.wcm.client.controller.RssController

The RSS controller is used to handle requests for RSS format information. It is also mapped using annotations, and handles requests for any URI that ends "/rss.xml". This controller is particularly simple in that all it does is specify which page view should be used ("rssfeed").

The Application Data Interceptor

This interceptor has been mentioned earlier, but it's worth describing in a little more detail. It is configured to sit in front of all of the controllers apart from the StreamedAssetController, and is responsible for resolving the requested URL into the WebSite, Section, and Asset objects that are being addressed. It does this simply by making calls into the WQS API (WebSiteService and WebSite), and it places the objects that it finds onto the servlet request context with the names "webSite", "section", and "asset". You can find more information about the process of resolving these objects in the Request Handling section.

Once it's done that it also takes responsibility for injecting those three objects in addition to some others onto the model that is returned from the underlying controller. It does this by delegating to a utility class named "ModelDecorator". The other objects that are placed in the model are:

  • A few Freemarker directives that are described later
  • A Boolean named "editorialSite" which is set to true if the resolved website has been configured as an editorial instance
  • A Boolean named "wef_enabled" that is used by the AWE-related Freemarker directives to decide whether to show the Web Editor
  • A Spring request context named "springMacroRequestContext" which is needed in order to allow the Spring Freemarker macros to be used
  • The original requested URI as a String named "uri"

Freemarker directives

There are six Freemarker directives included in the WQS webapp currently (11/Nov/2010). These are configured and injected into the ModelDecorator from the wcmqs-webapp-context.xml file, and are described in this section.

streamasset

This directive is an instance of the class "org.alfresco.wcm.client.directive.AssetDirective". It takes a single parameter ("asset" of type Asset), and streams the content of that asset to the servlet writer. The content comes from the asset's cm:content property. A simple example of its use can be seen in the "content/current" component (file: "current.get.html.ftl"):

 <@streamasset asset=asset/>

makeurl

This directive is an instance of the class "org.alfresco.wcm.client.directive.UrlDirective". It takes a few parameters: "asset" of type Asset; "section" of type Section; "rendition" of type String; "force" of type String. One or other of "asset" and "section" must be supplied (asset wins if both are supplied), and both "force" and "rendition" are optional and are ignored if "asset" is not supplied. "force" must have the value of either "long" or "short", if it's specified at all.

The directive is used to output the URL of the specified asset or section. There are two kinds of URLs that can be generated for an asset: "long" and "short". A long URL gives the full path to the asset whereas as the short URL uses the asset's UUID. Effectively, short URLs are less friendly (both to humans and search engines) but are less prone to change when the asset changes (if it's moved, for example). The "rendition" parameter allows the caller to specify the name of a rendition that is to be addressed. For example, the wide list component ("list/wide") uses the makeurl directive to address the asset's small thumbnail and to link to the relevant article:

  <a href="<@makeurl asset=article/>"><img src="<@makeurl asset=image rendition='smallThumbnail'/>" alt="${image.title!article.title!article.name}" class="news-img" /></a>

truncate

This directive is an instance of the class "org.alfresco.wcm.client.directive.TruncateDirective". It requires two parameters: "value" of type String; "chars" of type Integer. It writes the supplied String value out to the servlet writer, truncating it to be no longer than the number of characters specified by the "chars" parameter. It appends an ellipsis ("...") to the output. An example of this directive being used can be seen in the "list/narrow" component:

  <p><@truncate value=article.description chars=100/></p>

startTemplate

This directive is an instance of the class "org.alfresco.wcm.client.directive.WebEditorStartTemplateDirective". It accepts one parameter named "toolbarLocation" of type String that may have one of the following values: "top", "left", or "right". "top" is the default.

The purpose of this directive is to inject a reference to the Alfresco Web Editor bootstrap script. It's used in templates that render content that is to be editable with the Web Editor. In the Web Quick Start we enable this functionality in any page that uses either the "article/style1" or the "article/style2" components. For example, the file "style2.get.head.ftl" (note the head) contains this single line:

  <@startTemplate toolbarLocation="top"/>

markContent

This directive is an instance of the class "org.alfresco.wcm.client.directive.WebEditorStartTemplateDirective". It accepts up to four paramaters: "id" of type String; "title" of type String; "formId" of type String; and "nestedMarker" of type String. Its purpose is to mark the location of a piece of content so that it can be edited using the Alfresco Web Editor.

"id" is mandatory and is the identifier of the asset that is being marked as editable. "title" is optional, and is used to override the content title that is displayed by the Web Editor. "formId" is optional and is used to specify which Alfresco form should be used by the Web Editor when editing the marked content. "nestedMarker" is optional and, if set to "true", indicates that the content being marked is contained by the "parent" tag of the marker.

An example of this being used can be found in the "article/style1" component:

  <@markContent id=asset.id title="${asset.title!asset.name}" nestedMarker="true" />    

endTemplate

This directive is an instance of the class "org.alfresco.wcm.client.directive.WebEditorEndTemplateDirective". It accepts no parameters. This directive is used together with the startTemplate and markContent directives to provide the information needed by the Alfresco Web Editor. It is typically used immediately before closing the body tag on the rendered HTML page. Its usage is very simple:

  <@endTemplate />

Request URL handling

A request to a URL such as "http://my.example.com:8080/wcmqs/blogs/brian/a_great_blog_post.html" is broken down and used as follows:

  1. The application data interceptor looks for a node of type "ws:website" that has values "my.example.com" and "8080" for the properties "ws:hostName" and "ws:hostPort" respectively. If it can't find such a node then an error page is returned to the browser.
  2. "wcmqs" in this example is the context of the webapp, and currently plays no part in the resolution process. (Note: it probably should, in fact, and this will be addressed in a later release)
  3. "/blogs/brian/" is used to lookup the section of the website that is being addressed. In this case it's the section named "brian" below the section named "blogs" below the root of the website
  4. "a_great_blog_post.html" is used to find the asset. Basically this is a query to the repo such as "give me the asset named a_great_blog_post.html that is in the section /blogs/brian/"

If either the section or the asset cannot be found then a 404 error page is returned to the browser.

The webapp was created as a spring surf application, but has changes to allow it to work with the URL handling described above.

A default Spring Surf project created using Spring Roo creates has the the following configuration files :-

  • surf-config.xml contains a Surf handler mapping bean ordered as 0 to process Surf components. It uses an extension to Spring's UrlFilenameViewController and defines Surf Interceptors.
  • web-application-config.xml contains a DefaultAnnotationHandlerMapping ordered as 1, and the same Surf Interceptors as surf-config.xml. This handles any custom Spring annotated controllers added to an application.

To support the required handling of URLs in wcmqs, the handler mappings have the following changes from the default :-

  • A SimpleUrlHandlerMapping is added to quickstart-request.xml, ordered as 0. This passes any requests prefixed with /asset/ to a StreamedAssetController. This controller uses annotations but was hard-wired using the SimpleUrlHandlerMapping so that it could operate with a different set of interceptors to any other annotated controllers. It only needs the CMIS session interceptor, so the others are omitted for performance.
  • The DefaultAnnotationHandlerMapping is changed to order 1 and is used for controllers which handle the search and comment posting. This handler mapping includes Surf and application interceptors, one to get a CMIS session and one to load application and page-wide data from the repository such as the website, section and asset objects relevant to the current URL.
  • The handler mapping in surf-config.xml is ordered to 2 so that it is processed last. The default controller is changed to GenericTemplateAssetController. If no asset is found by the interceptor matching the current URL then this controller sets the response status to 404 and forwards to a 404 page. This prevents Surf's PageViewResolver from taking URLs which happen to be valid template page names and processing them. The controller also causes the 301 redirects described above. Other URLs pass through to be resolved by a custom PageViewResolver called DynamicPageViewResolver. URLs of site resources that are not dynamically generated using the repository are handled by Surf's PageViewResover and are listed in GenericTemplateAssetController's definition to avoid issuing a 404 page for them. For example, messages.js and thankyou.html. DynamicPageViewResolver handles most of the site's pages and is described below.

Other features of the web application that are involved in URL handling are detailed below :-

  • The configuration file, urlrewrite.xml and the UrlRewriteFilter package allow static content to bypass Spring Surf for performance.
  • The DynamicPageViewResolver gets the template name for the asset descibed by the URL. A Surf PageView object is returned with this template name. Surf will then match this name to the name of one of the template pages.
  • The StreamAssetController will fetch the asset related to a "/asset/<repository asset id>/filename.ext" format URL. It uses the UrlUtilsImpl class to extract the asset Id from the URL. If the asset has a template defined for it then a 301 redirect is triggered to the full URL of the asset, for example /parent-section/section/article.html. For assets with no templates, such as images, the controller will get the content stream of the repository object and pass it to the StreamedAssetView class. This sets the content type and streams the data to the servlet output. If a rendition is required, which is indicated by rendition=<rendition-name> being included in the URL, the controller returns the stream of the rendition instead of the asset itself. Note that StreamedAssetController sets and checks date headers (Last-Modified, Expires and Etag) so that if a requested asset is unchanged since the browser's last request then a 304 Not Modified status can be returned rather than full content stream.
  • RepositoryExceptionResolver handles errors within the controllers. It does a look-up in the repository for a page with name <http status code>page.html. If found it uses this for the main body of the error page. If not found then it uses a default 404 or 500 page coded within the web application by delegating processing to it's super-class, SimpleMappingExceptionResolver.

Web Application Packages

The Web Quick Start Web application, wcmqs contains the following packages :-

org.alfresco.wcm.client.controller
The Spring MVC controllers called by DispatcherServlet
org.alfresco.wcm.client.directive
Custom FreeMarker Directive implementations
org.alfresco.wcm.client.exception
Defines custom exceptions thrown by the WQS web application
org.alfresco.wcm.client.exceptionresolver
Classes to retrieve an error page from the Alfresco repository based on HTTP error code
org.alfresco.wcm.client.interceptor
Cross-cutting classes to load the spring model, and its data, and get the current CMIS session
org.alfresco.wcm.client.service
Implementation of the Web Quick Start API CollectionFactory as a service
org.alfresco.wcm.client.util
Interface for the CMIS session pool
org.alfresco.wcm.client.util.impl
Implementation of the CMIS session pool
org.alfresco.wcm.client.view
Image renderer
org.alfresco.wcm.client.viewresolver
A view resolver that uses the template defined for a page in the repository
org.alfresco.wcm.client.webscript
Java Webscripts to return JavaScript responses

Application and page-wide data

The webscripts that fetch web assets, such as the navigation menu, need application-wide data available on every page, for example the site structure data. The set of webscripts that fetch data for parts of one page share a common need for data which corresponds to the page as a whole, for example an article.html asset. To make this type of data available across the request, a Spring HandlerAdapterInterceptor has been used, ApplicationDataInterceptor. This loads the website, section and asset objects into the Surf RequestContext and the Spring Model.

Utility Classes

The UrlUtilsImpl handles getting a URL for an asset, extracting an asset id from a URL, and other URL specific functions. The mapping of a URL safe object Id to an Alfresco uuid is delegated to an Alfresco specific class called AlfrescoCmisUrlEncoderImpl. This removes the prefix workspace://SpacesStore/ to create a URI friendly uuid.

Logging

Since Roo was used to create the default Spring Surf project , two log4j configuration files are created, src/main/resources/META-INF/spring/log4j.properties and src/main/webapp/WEB-INF/classes/log4j.xml. Only the second of these is used by the application, so any changes you make need be applied there.

Spring Surf

Spring Surf a scriptable, content-oriented framework for defining pages, templates, components and layout of a web application. It takes care of a lot of the detail of creating a Spring MVC application for you. The Spring Surf Tutorial shows you how to get a web application built and running quickly, and explains the standard layout of a Spring Surf application. The wcmqs webapp conforms to the standard spring surf layout, so if you look at the project folder wcmquickstartwebapp/src/main/webapp/WEB-INF, you will see the following layout:-

500page.html
chrome/
classes/
common/
config/
dtd/
lib/
pages/
surf.xml
templates/
urlrewrite.xml
web.xml
webscripts/

You can see an outline of what these files and folders contain in the Spring Surf Tutorial, but in particular the folders pages, templates, webscripts, and config are useful to explore in this webapp. Surf provides tools to create the view composition part of a Spring MVC framework application. The Surf model lets you define pages, templates, components and more. Each of the definitions is a simple xml file.

Pages

The wcmqs webapp uses several page layout definitions, which can be seen, not surprisingly in the pages folder.

404page.xml
articlepage1.xml
articlepage2.xml
errorpage.xml
homepage.xml
publicationpage1.xml
search.xml
sectionpage1.xml
sectionpage2.xml
sectionpage3.xml
sectionpage4.xml

Spring Surf regards a page as a navigable html page in your web application. A page can have associations to other pages defined in your application. A page can have one or more formats defined by tempates. A page can have regions bound to zero or more components, which define the page layout. If you have installed and played with Web Quick Start and viewed the website, try opening some of these files. You will probably recognise some of of the page structures. For example, if you look at the sectionpage1.xml file :-

<page> 
   <id>sectionpage1</id>
   <title>sectionpage1</title>
   <template-instance>two-block</template-instance>
   <authentication>none</authentication>
   <components>
      <component>
         <region-id>left1</region-id>
         <url>/list/twocolumn</url>
	      <properties>
	         <collection>featured.articles</collection>
	      </properties>              
      </component>   
      <component>
         <region-id>right1</region-id>
         <url>/list/links</url>
          <properties>
             <collection>section.articles</collection>
          </properties>              
      </component>       
   </components>
</page>

In this example, the page sectionpage1 associates itself to a template managed by the template-instance two-block, and consists of two components, one bound to a region called left1, and one to the region right1.

A Spring Surf component associates a region with a webscript. The component right1 contains a url /list/links which is the path of the webscript that models the data for this region. The path is relative to the webscripts directory.

To understand the properties tag in the above xml file, it is necessary to know a little of how the Web Quick Start Model is constructed:-

The website is modelled by a Website object which contains the website's title, the hostname and port that the wcmqs website is listening on , and other data related to the website as a whole. The website content is modelled as a tree of Sections anchored by a root section, A section contains Assets. Assets can be child sections, or singular items of content, or items of content grouped in ordered or unordered Collections. This is not a complete list of the object types, but is enough to understand the above Page definition.

The xml file defines a new Surf property called collection to contain grouped content.

So as its name suggests, sectionpage1 describes a page displaying a section of news articles, which are organised in two Collections, a collection of featured articles in the left region of the page, and a collection of other news articles in the right region.

In the wcmqs webapp, pages are designed to be as generic as possible. Instead of a page for every named section, various styles of page such as "sectionpage1.xml" have been defined. Using standard collection names such as "section.articles" and "featured.articles" allows page to be re-used for various sections of the website, and allows pages to be the central place for defining what data should be displayed, and how it should be rendered.

Templates

Spring Surf templates are transformers that generate html for the browser to render. If you look in the templates folder, you can see there are two files that make up the template. There is an xml template instance configuration file, and the template itself, an ftl file which is a FreeMarker template. So the wcmqs webapp templates folder contains the following files:-

404page.ftl
404page.xml
errorpage.ftl
errorpage.xml
one-block.ftl
one-block.xml
searchpage.ftl
searchpage.xml
sprint1.ftl
sprint1.xml
three-block.ftl
three-block.xml
two-block.ftl
two-block.xml

Some templates are designed to be generic. For example :-

  • two-block which defines a wide left div and a narrow right div.
  • three-blockwhich defines a "belly-band" under the menu, a wide left div, and a narrow right div)

These templates have multiple regions in each div, for example "right1", "right2", "right3", so that pages can define one or more components for an area of the page.

The template instance is the configuration file for the template itself. The template receives all the properties of the template instance, and the template can uses them to influence its final rendering. A single template can render differently depending on its configuration file. In the wcmqs webapp there is just a single configuration file for each template file. So if you look at the template-instance file specified by the sectionpage1, which is two-block.xml it contains the following xml :-

<?xml version="1.0" encoding="UTF-8"?>
<template-instance>
	<title>two-block</title>  
  	<description>Template with two main areas</description>
   	<template-type>two-block</template-type>
    <components>
        <component>
            <region-id>top-right</region-id>
            <url>/search/box</url>     
        </component>
        <component>
            <region-id>footer</region-id>
            <url>/footer/footer</url>     
        </component>        
    </components>     	
</template-instance>

The template-type contains information common to all template-instances of the same type, again the wcmqs webapp has one template-type for each template-instance. The template-type can specify the template processor, but the wcmqs webapp uses FreeMarker as the processor, which is the default.

This template-instance binds two template scoped components to regions, in the same way as described at the page level.

Templates contain output markup and processing tags. When the tags execute they dynamically generate markup. Spring Surf has built-in tags, or in FreeMarker terms, directives. This is the corresponding FreeMarker template file, two-block.ftl:-

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <#include "/common/head.ftl"/> 
  <#include "/common/title.ftl"/>
</head>
<body>
<div id="body-wrapper">
  <div class="clearfix">
    <@region id="logo" scope="global"/>
    <@region id="top-right" scope="template"/>  
  </div>
  
  <div class="main-menu-wrapper">
	<@region id="menu" scope="global"/>
  </div>
  
  <div id="left">
    <@region id="left1" scope="page"/>    
    <@region id="left2" scope="page"/>    
    <@region id="left3" scope="page"/>    
  </div>
  
  <div id="right">
    <@region id="right1" scope="page"/>  
    <@region id="right2" scope="page"/>  
    <@region id="right3" scope="page"/>  
  </div>
  
  <div id="footer">
    <@region id="footer" scope="template"/>     
  </div>
</div>

<#include "/common/init.ftl"/> 
</body>
</html>

The most significant part of this file is the @region directive, which is a surf built-in directive. Note that the id properties with page scope correspond to regions in the page configuration file and those with template scope correspond to regions in the template-instance configuration file. The regions with global scope are from the website object.

  • Browser Support

Web Scripts

Web scripts are miniature, scriptable MVC applications. In the wcmqs webapp web scripts implement each component described in the page and template configuration files. Each component specifies the URL of its associated Web Script. Surf makes sure the Web Script executes with the right context and runtime environment so that it renders in the context of the whole page request. The output of each Web script is merged into the output of the page's associated template to construct the final html page.

Web scripts conform to the RESTFul architectural style, and accordingly use the HTTP methods for access (HTTP GET, POST, PUT, and DELETE). Web scripts can render output in a number of formats, for example HTML, Atom, XML, RSS. JSON. The wcmqs webapp responds to requests for HTML, so its web scripts all use the HTTP GET access method to render output in the HTML format.

Each Web script consists of three files :-

Description document
An xml file which describes the Web script's properties, for example its URI and HTTP method binding. The name of this file must comply with the Web Script Framework as follows:-
<web script id>.<http method>.desc.xml
For example: foo.get.desc.xml
Controller script
A JavaScript file which contains the logic of the Web script. Its output on execution is a model to render in the response template. The name of this file must comply with the Web script Framework as follows:-
<web script id>.<http method>.js
For example: foo.get.js
Response template
A FreeMarker template which renders the output response. The template has access to the model generated by the controller script, and the Web script's invocation context.The name of this file must comply with the Web script Framework as follows:-
<web script id>.<http method>.<format>.ftl
For example: foo.get.html.ftl

The location of the files is just a important as the name. If you look back at the sectionpage1.xml page definition file, the left1 region has a URL of /list/twocolumn. The corresponding webscript must be found in webscripts/list with a <tt><web script id> of twocolumn. So the URL is always a path relative to the webscripts directory, plus the Web script id.

The /list/twocolumn web script consists of three files :-

twocolumn.get.desc.xml
twocolumn.get.html.ftl
twocolumn.get.js

The description document twocolumn.get.desc.xml looks like this:-

<webscript>
  <shortname>Two col list</shortname>
  <description>Two column list</description>
  <url>/list/twocolumn</url>
</webscript>

This description just contains a name and the URI used to invoke the Web script. The description document name defines the HTTP method binding, so twocolumn.get.desc.xml specifies the HTTP GET method. The controller script twocolumn.get.js looks like this:-

model.articles = collectionFactory.getCollection(context.properties.section.id, args.collection);

The controller script's main job is to construct the model for rendering by the response template. The model is a map of names and values. The model root object is given to the controller script by the Web script Framework. The twocolumn.get.js adds a collection of articles to the model. The controller script uses the client API to get the Collection from the Alfresco repository. The client API javadoc is part of the wcmqs webapp and is provides the webapp's interface to the repository. The controller script just needs to provide the id of the section, and the name of the collection. The client API will generate a CMIS query to retrieve the requested collection and its contents.

The response template twocolumn.get.html.ftl looks like this:-

<div class="interior-content">
    <#if articles.assets?size == 0>
        ${msg('list.none')}
    <#else>
	  	<#list articles.assets as article>  
	  	    <#if article_index == 0>
	  	        <div class="featured-news">
                    <h2><a href="<@makeurl asset=article/>">${article.title!'no title'}</a></h2>
                    <span class="newslist-date">
                        From  <a href="<@makeurl section=article.containingSection/>">${article.containingSection.title}</a>
                       <#if article.properties['ws:publishedTime']??> - ${article.properties['ws:publishedTime']?string(msg('date.format'))}
                       </#if>
                    </span>
                    <#if article.relatedAssets['ws:primaryImage']??>             
                        <#assign image=article.relatedAssets['ws:primaryImage'][0]>             
                        <a title="Link to news article" href="<@makeurl asset=article/>">
                            <img src="<@makeurl asset=image/>" width="223" height="143" alt="${image.title!}" class="left-img" />
                        </a>
                    </#if>
                    <p>${article.description!'no preview'}</p>
                </div>		  	    
                <ul class="services-wrapper">
	  	    <#else>		  		
		        <li>
                    <div class="service-txt">
                        <span class="newslist-date">
                           From <a href="<@makeurl section=article.containingSection/>">${article.containingSection.title}</a>
                           <#if article.properties['ws:publishedTime']??> - ${article.properties['ws:publishedTime']?string(msg('date.format'))}
                           </#if>
                        </span>
    		            <h3><a href="<@makeurl asset=article/>">${article.title!'no title'}</a></h3>
    		            <p>${article.description!'no preview'}</p>        		            
    		        </div>
		        </li>
		    </#if>
	    </#list>
		</ul>
	</#if>
</div>

The response template renders an HTML div, using data retrieved from articles object. The articles object is the collection retrieved in the controller script. The template uses the #list FreeMarker directive to iterate over the assets in the collection. FreeMarker interpolations are used to address the properties of each asset. The response template can access any data in an object using the FreeMarker data model notation. Here is an example :-

${article.containingSection.title}

This adds a string, which is the title of the containing section of this asset to the response html. The dotted name convention addresses the class hierarchy in the wcmqs client api. So, article is of type Asset, and an Asset has a bean property, containingSection, which returns an object of type Section. A Section has a property title which is of type String. You can study the javadoc to see what is available in the FreeMarker data model. For example, the Asset interface in the org.alfresco.wcm.client package exposes tags, mimetype, size, contentAsInputStream, relatedAssets, template, and renditions as properties. In addition because Asset is a subclass of resource, in inherits the containingSection, description, id, name, properties, property, title, and type.

The response template can also access the Alfresco repository directly using property names of the type from the Alfresco Content Model. Note that in the wcmqs client api, one of the bean properties of Asset is itself called properties. This method retrieves properties for the type of the object directly from the Alfresco data dictionary. You can view the model properties available in the model extension in the file wcmquickstartmodule/config/alfresco/module/org_alfresco_module_wcmquickstart/model/webSiteModel.xml. For example, the type ws:webasset defines ws:parentSections, ws:tags, ws:publishedTime, ws:availableFromDate, ws:availableToDate, ws:available, ws:derivedCommentCount, and ws:derivedAverageRating as properties.

Here is an example :-

article.properties['ws:publishedTime']

This refers explicitly to the model property ws:publishedTime in the Web site Alfresco model extension.

Using CMIS to access the Alfresco Repository

The view composition webscripts' controllers the path to one of the factory interfaces in the client API to create Web asset model. For example in the twocolumn.get.js controller script the following JavaScript statement populates the model with a specified collection :-

model.articles = collectionFactory.getCollection(context.properties.section.id, args.collection);

Using the section id and the collection id, the getCollection method in implementation of the CollectionFactory, CollectionFactoryCmisImpl, builds a CMIS query language (cql) query string. The method then invokes the query method on the current CMIS session. CMIS will pass the query over to the repository and return a QueryResult. The CMIS query language is similar to that used for SQL queries. The session is held in ThreadLocal storage to allow it to be reused.

Understanding data flow in the Web application

The UML-like sequence diagram below illustrates how a request URL from the user of the Web Quick Start web application is handled. Most of the work is initiated by the Spring interceptors. RequestContextInterceptor gets a Spring request context for the application, CmisSessionInterceptor gets a CMIS session for this thread from a the session pool, and ApplicationDataInterceptor augments the Spring request context with WebSite, Section and web assets from the Alfresco repository.

The diagram shows round trips to the Alfresco Repository for WebSite, Sections, and Web Assets. The round trip is not usually made, since the application implements caches for both the WebSite objects and the Section data. Sequence Diagram

Making your own changes

TBC

Building Web Quick Start

The source code for the Web Quick Start atefacts is included in the Alfresco svn repository. Synchronise with HEAD and build alfresco.war and share.war before you attempt to build the Web Quick Start targets.

Building the Web Quick Start alfresco AMP.
ant package-wcmquickstart

This will build the alfresco-community-wcmqs-3.4.a.amp.

Building the Web Quick Start share AMP.
ant package-wcmquickstart-share

This will build the alfresco-community-wcmqs-share-3.4.a.amp.

Building the wcmqs web application
ant package-wcmquickstart-webapp-war

This will build the web application wcmqs.war.

Building the Alfresco Web Editor
ant package-webeditor


Once you have successfully built the above artefacts, you can install then using the process described in Web Quick Start Installation and Configuration.

Extending Web Quick Start for production

TBC

Reference

The Web Application Client API

TBC

JavaDoc

TBC

Running the Web Application API tests

TBC


Rendition Definitions Table

Definition Name Type Description Usage
smallThumbnail Composite Reformat to image/jpeg followed by resize to 72px X 72px Thumbnail image used by an Article
mediumPublicationThumbnail Composite Reformat to image/jpeg followed by resize to 110px X 145px Thumbnail image used in the Publications landing page
mediumNewsThumbnail Composite Reformat to image/jpeg followed by resize to 223px X 143px Thumbnail image used in an individual news article
largeNewsThumbnail Composite Reformat to image/jpeg followed by resize to 574px X 395px Enlarged image shown in the foreground when clicking on an image in a news article
featuredNewsThumbnail Composite Reformat to image/jpeg. Crop 80% height followed by resize to 641px X 281px. Homepage featured news image slider
imagePreview Composite Reformat to image/jpeg followed by resize to 625px width maintaining aspect ratio Display an image within the Publications section - full page details view
swfPreview Reformat Reformat to application/x-shockwave-flash Preview image of a pdf web asset
pdfWebasset Reformat Reformat to application/pdf Used internally to create a pdf web asset from a business document, for example from a Microsoft Word document

Back to Web Quick Start

Personal tools
Download and go
© 2014 Alfresco Software, Inc. All Rights Reserved. Legal | Privacy | Accessibility