ddraper

Creating Custom Share Widgets

Blog Post created by ddraper on Feb 26, 2013

Introduction



In my last post I explained the basics of creating a new page by a defining a JSON model of existing Alfresco widgets. The library of widgets that Alfresco provides is currently small but is constantly growing. Ideally it would be nice if you could build the page you want just using out-of-the-box widgets but at some point you might need to write your own. This post will describe how to create a simple widget that defines its own template, CSS and localization resources.



If you have an understanding of Dojo (1.7+) or the AMD pattern then the JavaScript code should all be very obvious, but if you've no experience of these then hopefully this post will be sufficient for you to make progress. At the end of the day, it’s all just JavaScript!

The Basics



The AMD (Asynchronous Module Definition) pattern is a method of defining small JavaScript resources that you can easily declare dependencies of. Your AMD framework (e.g. Dojo or require.js) will be able to dynamically request those dependencies as they’re identified. This can result in a large number of HTTP requests from the browser but this can be avoided by building resource “layers” containing multiple modules. Surf avoids this problem by dynamically performing dependency analysis to avoid both excessive HTTP requests and the necessity for a build. As well as the benefits of a well-defined dependency network AMD also provides enhanced security by not polluting the global context so that the module code cannot easily be manipulated by malicious attacks.

Modules and Packages



Each widget is considered a module and modules are located relative to the package in which they live. The package is defined as the first element in the module identifier (or MID) so the “alfresco/logo/Logo” widget can be found in the “alfresco” package. Package locations are defined in the configuration used to bootstrap Dojo but Surf will take care of that for you. The Surf “web-framework” configuration element defines the core package of “dojo”, “dijit”, “dojox” and “surf” and Share augments that with the “alfresco” package. Look in the “<webapps>/share/WEB-INF/surf.xml” file and you’ll see the following:

<dojo-pages>

   <bootstrap-file>/res/js/lib/dojo-1.8.3/dojo/dojo.js</bootstrap-file>

   <page-widget>alfresco/core/Page</page-widget>

   <base-url>/res/</base-url>

   <packages>

      <package name='dojo' location='js/lib/dojo-1.8.3/dojo'/>

      <package name='dijit' location='js/lib/dojo-1.8.3/dijit'/>

      <package name='dojox' location='js/lib/dojo-1.8.3/dojox'/>

      <package name='alfresco' location='js/alfresco'/>

   </packages>

</dojo-pages>




  • “bootstrap-file” defines the version of Dojo to be used


  • “base-url” defines where all packages are relative to


  • “page-widget” it the name of the widget to use to construct the page


  • “packages” defines the map of packages


If you want to use your own package then this is where you’ll need to define it.

Creating Your First Widget



For simplicity let’s just add a new widget to the “alfresco” package. These can be found under “<webapps>/share/js/alfresco”. Add a new folder called “blog” in that location and then add the following files:



A screenshot of the file structure and the files to add



“MyWidget.js”

define(['dojo/_base/declare',

        'dijit/_WidgetBase',

        'dijit/_TemplatedMixin',

        'dojo/text!./MyWidget.html',

        'alfresco/core/Core'], function(declare, _Widget, _Templated, template, Core) {

   return declare([_Widget, _Templated, Core], {

      cssRequirements: [{cssFile:'./MyWidget.css',mediaType:'screen'}],

      i18nScope: 'BlogExamples',

      i18nRequirements: [{i18nFile: './MyWidget.properties'}],

      templateString: template,

      buildRendering: function alfresco_blog_MyWidget__buildRendering() {

         this.greeting = this.message('greeting.label');

         this.inherited(arguments);

      }

   });

});


“MyWidget.html”



<div class='blog-example-widget'>${greeting}</div>


“MyWidget.css”



.blog-example-widget {

   padding: 10px;

   color: orange;

   font-weight: bold;

}


“MyWidget.properties”



greeting.label=Hello


“MyWidget_fr.properties”



greeting.label=Bonjour


Update the JavaScript controller of the WebScript described in my last post to the following:

model.jsonModel = {

   widgets: [{

      name: 'alfresco/blog/MyWidget'

   }

]};


Refresh the WebScripts and reload the page and you should see the following:



Screenshot of browser showing custom widget in English



Switch your browser into the French locale and refresh and you'll then see:



Screenshot of browser showing custom widget in French



Although your widget is made up of multiple files all of them are linked together through the main JavaScript module that gets included in the JSON model of the page. The CSS and template is automatically pulled into the page and the correct i18n properties file is used for the requested locale (and only the i18n properties of the browser locale are loaded into the page).



With just a single widget on the page this might not seem particularly impressive but expand this to a page containing hundreds of widgets from multiple sources and you should begin to appreciate how much simpler this will make development. You just need to know how to write JSON to be able to create your own page from 3rd party widgets without worrying about coding, localization and styling.

Understanding the Code



The most important thing to understand in the JavaScript file is the mapping between each declared dependency (in the array argument of the “define” function) and the variable that it maps to in the callback function (that is the second argument of the “define” function). See the screenshot below for a colour-coded examples (apologies to any colour-blind readers, hopefully you get the gist).



Screenshot showing JavaScript file with colour matched dependencies



The “define” function returns a module which is instantiated by the “declare” function. The declare function takes an array of modules to inherit from and an object that defines the module. Even if you have no experience of Dojo or AMD that’s really all you need to know – everything else is just JavaScript. Obviously it helps to understand the standard Dojo widget lifecyle so I would advise at least reading some of the excellent Dojo documentation (I’ve included a few pages that I’d suggest are worth reading at the end of this post).



You don’t need to use any of the Dojo modules if you don’t want to, but there’s lots of great utilities that and widgets that you can extend or include in your own code. In a future post I’ll explain how we’ve made it easy to include non-AMD dependencies in your widgets – as I said in my last post, our intention is not to limit Share to using just Dojo, but whatever your previous opinion of it may be – I can assure you that it’s an excellent place to start.



The breakdown of the example widgets attributes are:



  • 'cssRequirements' - an array of CSS files to include in any page that uses the widget. You can specify multiple files and can specify different files for different media types. There is no need to include CSS files required by extended or mixed in widgets as Surf will automatically include them.


  • 'i18nRequirements' - an array of the i18n properties files that the widget should have access to. I'll explain localization in more detail in a future post


  • 'i18nScope' - a identifier for where to store the properties in the page. This can be used to prevent widgets overriding each others properties


  • 'templateString' - this should be set to the variable assigned with the HTML dependency (as has been done in the example). See the Dojo documentation for a more detailed explanation


  • 'buildRendering' - this is one of the Dojo widget lifecyle functions. We're extending the default implementation to get the localized message to set in the template


 'alresco/core/Core'



One of the strengths of Dojo is it's inheritance model. This allows your widget to directly extend another module and then 'mixin' the capabilities of others. When you create a custom widget you should always mixin the 'alfresco/core/Core' module. This module provides a number of useful functions and the one used in our example widget is the 'message' function which is used to set the 'greeting' attribute. This function will search through all the appropriate i18n scopes to try and map the supplied key to the translated message. Again, this will be covered in greater detail in a future post but its important to remember that if you don't mixin in the 'alfresco/core/Core' module then you will struggle to access the widgets i18n properties correctly.

Summary



The purpose of this post is simply to familiarise you with the basics of how to create a widget that can be included in the JSON model of a Share page. We're still just scratching the surface of widget creation but hopefully by following the example you'll be able to begin creating your own widgets. In future posts I'll describe:



  • more of the function provided by 'alfresco/core/Core'


  • how to extend and wrap both Dojo and other JavaScript library widgets (including existing Share widgets)


  • how the dependency analysis works and how you can configure it


  • the i18n process in more detail


  • how widgets should fire and handle events to work with the Share services


Suggested Further Reading



In addition to the linked articles, I'd recommend reading the following:

For those of you that love JQuery, I'd recommend taking a look at the following:

 



Outcomes