Customizing Share JavaScript Widget Instantiation (Part 1)

This post refers to code committed to the latest Alfresco Community source – it is not yet available in any official release but you can try it out against the nightly Community builds.

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

14 thoughts on “Customizing Share JavaScript Widget Instantiation (Part 1)

  1. Bertrand

    Hi,

    Do you know in which enterprise version those mechanisms will be available ? In a “major” version (4.1.0 for example) or in a minor version (4.0.3) ?

    Regards.

    Reply
  2. ddraper Post author

    Hi Bertrand,

    Because we need to make changes to almost every WebScript in Share then I’m fairly confident that these will not be available until the next “major” release – although I don’t actually make these decisions so I can’t be 100% sure. I do know that although the Surf changes are complete, we have only just started updating the Share WebScripts so it will be a long time before they’re all finished.

    Regards,
    Dave

    Reply
  3. Faizaan

    Hi,

    This is very interesting to us when we are soon to customise Share with JavaScript. But I have a doubt this is not better than the “dependencies” configuration we use in the share-config-custom.xml….? Could not you have used such thing for the extended filter in this blog post?

    Thanks

    Reply
  4. ddraper Post author

    Hi Faizaan,

    Striking the right balance between a sensible example and something concise and easily understandable is quite tricky… the objective of this first post in the series is to show how to instantiate a different JavaScript class than the default. I could have done anything else in my custom JavaScript class and it didn’t necessarily even have to extend the Alfresco.DocumentList widget – the main obstacle we’re trying to overcome is the ability to change the JavaScript object instantiated, what you do in your own JavaScript code is entirely up to you.

    It is entirely possible to write and include a separate JavaScript file that augments the existing definition – but this approach would almost certainly require some in-depth JavaScript knowledge and wouldn’t lend itself to extension on extension.

    There’s lots more to come in this series of blogs – hopefully the next post will be published in the next few days.

    Regards,
    Dave

    Reply
  5. Pingback: David Draper's Blog » Blog Archive » Customizing Share JavaScript Widget Instantiation (Part 3)

  6. Pingback: Kev’s blog » Blog Archive » Alfresco Community 4.2 – Shiny. Fast. Awesome.

  7. Zlatko

    Hello,

    Can you please show the file structure of the JAR?
    Also, the part – in what file does it go and where?
    And do you deploy the JAR in $TOMCAT/shared(ib or in share/WEB-INF/lib?

    Thanks

    Reply
  8. Vitali

    Hi Dave,

    we often only need to override a CSS or JS include defined in *.get.head.ftl.

    I tried to solve this using approach you’ve described in this topic, but it didn’t work.

    Here what I did:
    1. Added a customization for comment-list component:

    org.alfresco.components.comments
    my.comments

    2. Added a file: webscripts.my.comments.comments-list.get.head.ftl which includes my additional CSS: include:

    But my customization has been never called by Surf.

    Or should I add in the core file: org.alfresco.components.comments.comments-list.get.head.ftl

    and override it in my
    webscripts.my.comments.comments-list.get.head.ftl
    ?

    Ideally would be to have an easy-to-use extension point for every “head.ftl” of Alfresco Share presentation widgets (webscripts).

    Best regards
    Vitali

    Reply
  9. Dave Draper Post author

    Hi Vitali,

    Apologies, but I have to ask the obvious questions first…
    1) Are you running on 4.2 Community or from a nightly or personal build of the latest code?
    2) Are you creating and deploying an extension module? (and are you sure it’s deployed successfully?)
    3) Are you targeting the correct <@markup> elements and are you using the right actions?

    It is possible to effectively have an extension applied to *every* WebScript if you just make the target package “org.alfresco” since all Alfresco WebScripts are defined beneath that package.
    Also (it’s not recommended as they’re now deprecated) but you can still create extensions to the .head.ftl files (but this will just save you the effort of having <@markup> directives)

    If you could maybe post some code to the forums and reply with a link then I might be able to see what’s going wrong for you?

    Regards,
    Dave

    Reply
  10. Vitali

    Additionally to my previous post, following appears in the Alfresco log:
    2013-02-03 06:47:01,002 WARN [extensibility.impl.ExtensibilityModelImpl] [http-8080-exec-23] The ‘replace’ action was attempted to used when defining the base model by directive:ID: commnts-list-custom-includes-impl, ACTION:replace

    Reply
  11. Pingback: Customizing Share JavaScript Widget Instantiation (Part 3) | Alfresco Developer Blog

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>