Creating Custom Share Widgets

cancel
Showing results for 
Search instead for 
Did you mean: 

Creating Custom Share Widgets

ddraper
Intermediate II
2 13 21.7K

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:

 



13 Comments
blog_commenter
Active Member
[...] my second post in this series I showed how to create a page from existing widgets and in my last post I showed how to create a custom widget. In custom widget showed how to specify i18n properties [...]
blog_commenter
Active Member
hi Will,



I am trying to get this example working using a project created from the new Alfresco SDK.  Where in the sdk project structure do you place the widget files.  I have tried creating js/alfresco folder under src/MAIN/amp and putting them in blogs folder there..  No go.



Keen to use SDK so any pointers appreciated.



Regards



Brian
blog_commenter
Active Member
Widget is not shoing on http://localhost:8080/share/page/dp/ws/my-new-page

Is the link really the same?
ddraper
Intermediate II
@Jyri - it's possible that you've not added the files correctly or the WebScript isn't configured properly... please re-trace through the tutorial steps to make sure you've not missed anything.
blog_commenter
Active Member
I've read your post, it's very good, and example works for you.



When you have the next post to covere these topics?



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



Thanks,

Stella
blog_commenter
Active Member
Hi Dave,



thanks for your great tutorial.



One question only: Imagine the situation that you want to pass some parameters to a constructor from the js controller script. How to do that?



Thanks for your help,

Amin
blog_commenter
Active Member
What I mean is calling from the js controller  a constructor with parameters of the widget.
ddraper
Intermediate II
@Amin - the constructor for all widgets takes a JavaScript object as the sole argument and the attributes of that object will be set as instance attributes on that widget instance. That's how configuration is passed from the JS controller into the model. We do this all the time.
blog_commenter
Active Member
[…] an HTML template file (although we could also include CSS and i18n files as well – see this previous post for details). The HTML template is just about as simple as possible containing nothing more than a […]
blog_commenter
Active Member
hi,

nice tutorial Dave,

i have problem with setting an aikau page as the home page after login with alfresco share ! is this even possible because i couldn't get it working ! especialy because all aikau pages have to be called after an [/dp/ws] in url so servlets can process them into aikau !



if i can find out how to import this custom freemarker macro  to be used in any surf webscript (under templates) so it will be great and that will solve the problem because then i will be able to make my root page .ftl processing an aikau json model.



but that processJsonModel macro can't be found if you open any webscript without putting (dp/ws) on url in order to activate the aikau processing !



how can i acheive what i want to do ? is it even possible to make an aikau page as your home page after login ! it should be simple to do if possible but i'm struggling to do it so i assume that's either hard to do or not possible !
ddraper
Intermediate II
@hani It is possible to configure the page you arrive at after login to be an Aikau page. General information on creating and configuring the landing page for Share can be found here: https://www.alfresco.com/blogs/developer/2011/11/01/advanced-share-customization-part-1/ . Also, if were to create a new Aikau client using the Maven archetype (https://github.com/Alfresco/Aikau/blob/master/tutorial/chapters/Tutorial1.md) you'd see that the exact same process is used there for configuring the post-login landing page.
blog_commenter
Active Member
If I look at these argument signatures:

function(declare, _Widget, _Templated, template, Core) {

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

I note that they differ from the example included in the SDK (2.2.0), where they read:

function(declare, _Widget, Core, _Templated, template) {

        return declare([_Widget, Core, _Templated]

I am guessing the the example is the right way since it works.
blog_commenter
Active Member
[…] The define function tells the AMD loader what modules to load, the declare function creates the JavaScript Class. The buildRendering and postCreate functions are called when an instance of the class is created. A more in-depth description of widget creation can be found here. […]