ddraper

Customizing Share JavaScript Widget Instantiation (Part 1)

Blog Post created by ddraper on May 22, 2012

 

Introduction

The engineering team at Alfresco has been working hard over the past year or so to make it easier to customize the Share user-interface.  We've now started work on what I’d consider to be the last major piece of the extensibility puzzle – customizing the instantiation of client-side JavaScript widgets for each WebScript rendered Component.

The only way it was previously possible to change the widget(s) instantiated for a WebScript rendered Component was to copy and paste the supporting code to the “web-extension” path and then make the necessary changes to that copy. The major downside of this approach was that it generated an additional maintenance task to keep the copied code up-to-date with any changes that resulted from upgrades, service packs and hot-fixes.

The approach that we’re taking is to move all the logic and metadata about widget instantiation out of the FreeMarker template and to move it into the JavaScript controller as it is inherently easier to customize.  The metadata will be stored as a standardized object structure in the model which will then be processed by a new custom directive in the FreeMarker template to output the JavaScript code necessary to instantiate the specified widgets.

This allows us to utilize the existing JavaScript controller extension capabilities so that extension modules can modify the default metadata object(s) to change the following:

    • The name of the JavaScript widget to be instantiated
    • The arguments passed when instantiating the widget
    • The variable that the instantiated widget is assigned to
    • Whether or not i18n messages are set for the widget
    • Whether or not additional options are applied to the widget
    • The additional options that should be applied to the widget


As part of these changes we will also be updating the FreeMarker template to use a common “boiler-plate” structure to ensure consistency across WebScript rendered Components. We will utilize updated resource handling features in Surf to move all the CSS and JavaScript dependency requests into the template and remove the associated *.head.ftl file.  A consistent pattern of <@markup> directives will be used throughout the template to further enhance customization options.


The first WebScripts converted are those on the Document Library page as these as this is the area most frequently customized – these changes have already been committed to the latest Community source code in SVN.

 

 

Example

Let’s consider a simple example of what can be done….

Say we want to pop-up a message every time that a Document Library filter is changed.  The obvious place to handle this would be at the end of the “onFilterChanged” function in the “documentlist.js” file. Up until now we had the following options:

    1. Edit the local copy of “documentlist.js” (and then have to maintain any changes to it that result from fixes and upgrades)
    2. Create a custom DocumentList JavaScript subclass and then copy and paste the “documentlist.get” WebScript to instantiate it instead of the default Alfresco implementation (and then have to maintain any changes to the WebScript that result from fixes and upgrades).


Now though we have a slightly better option – we can create our subclass as with option 1, but we can create an Extension Module to the WebScript to both import our new library file and to instantiate our custom widget.

 

Our module configuration looks like this:

 

<module>
  <id>Custom DocumentList Widget</id>
  <description>Instantiate a custom DocumentList widget</description>
  <customizations>
    <customization>
      <targetPackageRoot>org.alfresco.components.documentlibrary</targetPackageRoot>
      <sourcePackageRoot>blog.demo.customization</sourcePackageRoot>
    </customization>
  </customizations>
</module>

 

This identifies the WebScript package that we want to target – in this case it’s the Document Library related WebScripts. We then create our custom Document List widget in our Extension JAR (“META-INF/doclib/extension/custom-documentlist.js”):

 

// Declare namespace...
if (typeof Blog == undefined || !Blog) { var Blog = {}; }
if (!Blog.custom) { Blog.custom = {}; }
(function()
{
  // Define constructor...
  Blog.custom.DocumentList = function CustomDocumentList_constructor(htmlId)
  {
    Blog.custom.DocumentList.superclass.constructor.call(this, htmlId);
    return this;
  };

  // Extend default DocumentList...
  YAHOO.extend(Blog.custom.DocumentList, Alfresco.DocumentList,
  {
    onFilterChanged: function CustomDL_onFilterChanged(layer, args)
    {
      // Call super class method...
      Blog.custom.DocumentList.superclass.onFilterChanged.call(this, layer,args);

      // Pop-up a message...
      Alfresco.util.PopupManager.displayMessage({
        text: 'Filter Changed!'
      });
    }
  });
})();


 

Then we create an extension to the “documentlist.get.html.ftl” template to create a dependency to the file containing our custom widget (“webscripts/blog/demo/customization/documentlist.get.html.ftl” in our extension JAR):

 

<@markup id='custom-documentlist-dependencies' target='js' action='after' scope='global'>
  <@script src='${url.context}/res/doclib/extension/custom-documentlist.js' group='documentlibrary'/>
</@markup>


Finally we create an extension to the “documentlist.get.js” controller to change the widget that is instantiated ('webscripts/blog/demo/customization/documentlist.get.js'):

 

// Find the default DocumentList widget and replace it with the custom widget
for (var i=0; i<model.widgets.length; i++)
{
  if (model.widgets[i].id == 'DocumentList')
  {
    model.widgets[i].name = 'Blog.custom.DocumentList';
  }
}


Finally, deploy the module, restart Alfresco and each time you change a filter in the Document Library you should see the popup.

 

Extended Document List

Attachments

Outcomes