WebScript JavaScript Controller Extensions Recap

Introduction

Following discussions with various Alfresco Community members over recent weeks I've realised that there is possibly a lack of understanding about the relationship between the controller and template in a WebScript related to when JavaScript controller extensions are processed. This post will attempt to clarify the relationship and explain how and why to use a JavaScript controller extension as well as providing another concrete example of how to extend the new header bar in Alfresco Share 4.2

Extensibility Recap

The JavaScript controller (or the Java bean controller if you're writing a Java backed WebScript) has one primary purpose; to create a model object that is then passed to the template renderer (typically FreeMarker).

Even if your WebScript has an additional configuration file (e.g. one that ends '*.config.xml') then it the controller is still responsible for processing that configuration and adding it to the model ... a configuration file achieves nothing by itself!

In Alfresco 4.0 we introduced the ability to apply extension modules that could dynamically manipulate the controller, localization properties and FreeMarker templates. Customizing the FreeMarker templates was by far the most limited of these approaches as it relied on additional <@markup> directives being included in the templates. Unfortunately most of WebScript logic and client-side JavaScript widget instantiation code was defined in the FreeMarker templates which often made it difficult to achieve the desired results through pure extensions.

In Alfresco 4.2.e Community and Alfresco 4.2 Enterprise we have introduced a new approach to page creation where a JSON model of widgets to render is defined entirely in the JavaScript controller and the FreeMarker template is reduced to a single custom FreeMarker directive. This allows extensions much finer control over the page that make it possible to achieve a wider range of customizations through model manipulation.

The JavaScript Controller Extension

When writing a JavaScript controller you have access to a variable called 'model'. Any attributes added to this object are available in the associated FreeMarker template, e.g.

model.myVariable='This is a variable'

...in a controller can be referenced in a FreeMarker template by:

${myVariable}

The controller can add any variables it likes to the 'model' object but the refactored WebScripts make use of the 'widgets' attribute (e.g. 'model.widgets') and the new page creation approach relies on the 'jsonModel' attribute (e.g. 'model.jsonModel').

Surf will check for the existence of any extension controllers mapped to the WebScript before the FreeMarker template is renderer is called.

This means that one or more controller extensions can change the contents of the 'model.widgets' or 'model.jsonModel' attributes before they are processed by the custom directives in the template. The core controller will always be run to setup the default model and all applicable extension controllers will have their turn at updating the model before it is passed to the template.

If you're writing an extension module that you want to distribute around the Alfresco Community then you need to be aware that other extensions might have changed the model in an unexpected way before your extension has it's turn.

Real World Example

Let's see this in action by adding a new menu item to the header in Alfresco 4.2 Enterprise. Information on how to create and deploy your extension module have been covered in detail before so I'm not going to go over those now, instead I'll just focus on the controller extension itself.

If we want to add a new menu item (or indeed any new widget) we need to identify a parent widget to add it to. At the moment this still requires some knowledge of the Alfresco source code (although it is easy enough to identify the source files using SurfBug).

In this case we're customizing the 'share-header.get' WebScript whose controller imports the 'share-header.lib.js' file which provides functions that return a model fragment of the header menu. It's important to note that you don't want to (and indeed can't) extend the lib file directly - but instead much extend the controller that imports the lib file.

All of the widgets are given unique IDs in the JSON model so that they can be easily referenced. In this case we want to add a new 'alfresco/menus/AlfMenuBarItem' widget to the 'alfresco/header/AlfMenuBar' widget identified as 'HEADER_APP_MENU_BAR'. For the purposes of this example it doesn't really matter where the menu item takes the user, but in this case we're going to use it to navigate to the user's trashcan.

...and when the module is deployed you will see the following added to the Share menu:

Summary

The key thing to take away from this post is how JavaScript controller extensions fit into the the processing order of WebScript files and that the sole purpose of a JavaScript controller is to set up a default model that extending controllers can then manipulate.