Skip navigation
All Places > Alfresco Content Services (ECM) > Blog > 2013 > February
2013

Creating Custom Share Widgets

Posted by ddraper 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:

 



Simple Page Creation in Share

Posted by ddraper Feb 21, 2013

Introduction



The purpose of this post is to introduce the steps required to add a page to Share using some of the updates outlined in my previous post. Hopefully if you've had previous experience of adding pages to Share you'll see how this approach simplifies the process. As this series of posts continues the examples will get progressively more detailed - I just want to get the basic concepts nailed down first!



As with the last post, this information refers to the latest code in HEAD SVN and will appear in future 4.2 Community and Enterprise releases.

Create a New Page



To create a new page in Share using the new capabilities added by recent updates you can now do the following:



  1. Create a new WebScript (made up of the following files)



    • “new-page.get.desc.xml” (WebScript descriptor)


    • “new-page.get.js” (WebScript controller)


    • “new-page.get.html.ftl” (WebScript template)




  2. Place these files in any path under “<tomcat-home>/webapps/share/WEB-INF/classes/alfresco/site-webscripts”


The WebScript Descriptor should contain the following:



<webscript>

   <shortname>My New Page</shortname>

   <url>/my-new-page</url>

</webscript>


The WebScript Controller should contain the following:



model.jsonModel = {

   widgets: [{

      name: 'alfresco/logo/Logo”

   }]

};


The WebScript Template should contain the following:



<@processJsonModel group='share'/>


Now open the web browser of your choice and point it at http://<server>:<port>/share/page/dp/ws/my-new-page and you should see the following…



Screenshot of page with Alfresco logo



Not the most exciting page in the world granted, but implemented with fairly minimal effort.

Widget Creation Configuration



What if we don’t want to just display the Alfresco logo? What if we want to display a different logo? Fortunately the “alfresco/logo/Logo” widget declares a number of different CSS rules that allow us to easily change the logo that is rendered. Update the JavaScript controller to be the following:

model.jsonModel = {

   widgets: [{

      name: 'alfresco/logo/Logo',

      config: {

         logoClasses: 'surf-logo-large'

      }

   }]

};


If you make the changes to the source files in the deployed web application you can apply these changes simply by refreshing the WebScripts by clicking the “Refresh Web Scripts” button on the Web Scripts home page (http://<server>:<port>/share/service/index)



When you refresh the page you should now see:



Screenshot of page showing Surf logo



What we've done is simply add some instantiation arguments to the “alfresco/logo/Logo” widget to override the default “logoClasses” attribute with a different CSS class that a selector was defined in the CSS resource associated with it. In the JSON model the “name” attribute refers to the name of the widget that you want to instantiate (technically it refers to the Module Identifier or “MID”) and “config” attribute is an object passed during instantiation of that widget.

A Simple Menu Bar



Let’s build something a bit more interesting; replace the contents of the JavaScript controller with the following:

model.jsonModel = {

   widgets: [{

      name: 'alfresco/menus/AlfMenuBar',

      config: {

         widgets: [{

            name: 'alfresco/menus/AlfMenuBarItem',

            config: {

               label: 'One'

            }

         },{

            name: 'alfresco/menus/AlfMenuBarItem',

            config: {

               label: 'Two'

            }

         }]

      }

   }

]};


This now creates a basic menu bar with two menu items (don’t worry for the moment that these menu items don’t do anything yet, we’ll get onto that in a future post!).



Screenshot showing page with 2 item menu bar



The key thing to note here is the use of the “widgets” attribute in the “config” object of the “alfresco/menus/AlfMenuBar”. Where one widget can be the parent to child widgets it is always possible for the model for those children to be defined in an array assigned to the “widgets” attribute. This repeating pattern is one of the many ways in which Surf is able to establish all the dependencies to load onto the page.

Adding a Drop Down Menu



Let’s make the menu bar a bit more detailed, update the model to be the following:



model.jsonModel = {

   widgets: [{

      name: 'alfresco/menus/AlfMenuBar',

      config: {

         widgets: [{

            name: 'alfresco/menus/AlfMenuBarPopup',

            config: {

               label: 'One',

               widgets: [{

                  name: 'alfresco/menus/AlfMenuItem',

                  config: {

                     label: 'Popup item 1'

                  }

               },{

                  name: 'alfresco/menus/AlfMenuItem',

                  config: {

                     label: 'Popup item 2'

                  }

               }]

            }

         },{

            name: 'alfresco/menus/AlfMenuBarItem',

            config: {

               label: 'Two'

            }

         }]

      }

   }

]};


The result should be:



Screenshot showing page with menu bar where 1st item is a drop-down menu

Adding Cascading Menus and Icons



We've now converted the first menu item to be popup menu containing two more menu items (note again the repeating config/widgets/config/widgets pattern). Let’s now add some icons to the menu items, do some grouping and add a cascading menu… try this model in your JavaScript controller:

model.jsonModel = {

   widgets: [{

      name: 'alfresco/menus/AlfMenuBar',

      config: {

         widgets: [

         {

            name: 'alfresco/menus/AlfMenuBarPopup',

            config: {

               label: 'One',

               widgets: [

               {

                  name: 'alfresco/menus/AlfMenuGroup',

                  config: {

                     label: 'Group 1',

                     widgets: [{

                        name: 'alfresco/menus/AlfMenuItem',

                        config: {

                           label: 'Popup item 1',

                           iconClass: 'alf-edit-icon'

                        }

                     },{

                        name: 'alfresco/menus/AlfCascadingMenu',

                        config: {

                           label: 'Popup item 2',

                           iconClass: 'alf-cog-icon',

                           widgets: [

                           {

                              name: 'alfresco/menus/AlfMenuItem',

                              config: {

                                 label: 'Cascaded menu item 1',

                                 iconClass: 'alf-leave-icon'

                              }

                           },{

                              name: 'alfresco/menus/AlfMenuItem',

                              config: {

                                 label: 'Cascaded menu item 2',

                                 iconClass: 'alf-help-icon'

                              }

                           }]

                        }

                     }]

                  }

               },{

                  name: 'alfresco/menus/AlfMenuGroup',

                  config: {

                     label: 'Group 2',

                     iconClass: 'alf-logout-icon',

                     widgets: [{

                        name: 'alfresco/menus/AlfMenuItem',

                        config: {

                           label: 'Popup item 3',

                           iconClass: 'alf-profile-icon'

                        }

                     }]

                  }

               }]

            }

         },{

            name: 'alfresco/menus/AlfMenuBarItem',

            config: {

               label: 'Two'

            }

         }]

      }

   }

]};


Once the WebScripts are refreshed and the page is reloaded you should see:



Screenshot of page showing menu with dropdowns, cascaded and icons

Summary and Next Steps



Without writing a single line of (traditional) HTML, JavaScript or CSS it has been possible to construct a page containing a reasonably detailed menu bar. OK, so at the moment this menu bar doesn't actually do anything but we’re just getting started. The purpose of this post is simply to explain a new way of adding a page to Share and how to start building a JSON model for the contents of that page. In future posts I’ll be explaining how…



  • To create your own widget to include in a model


  • The various localization options for your pages and widgets


  • To define events in your model and introducing some of the out-of-the-box event handling services


  • The model is converted into dependencies and how to configure the dependency analysis


  • To wrap existing JavaScript widgets to work in the model


If you've previously done any JavaScript controller extensibility it should be fairly obvious how the existing mechanisms can be used to easily add, change and remove things from a default model. One of the main advantages of the new header bar in Share is not that you can easily change it without copying and pasting all of the XML that defined it.

The 'Hybrid Page'



The final thing I’d like to quickly point out is the availability of the “hybrid” view of page. By simply changing the URL to be http://<server>:<port>/share/page/hdp/ws/my-new-page (note the additional “h”) you will get the page content rendered between the standard Share header and footer.



Screenshot of menu bar between Share header and footer

Introduction

I’m pleased to announce the arrival of a number of significant improvements to the UI framework in Alfresco Share. It will be part of the next Alfresco 4.2 Community and Enterprise releases and is available now in HEAD SVN to try out. Rather than trying to explain all of the details in a single blog post I’m going to start by just going over the concepts of what we’re trying to achieve. Over the coming weeks I’ll be posting more information explaining how it all works and how you’ll be able to make use of it.

 

 

Goals

Our ultimate aim is to provide a library of widgets that can be easily assembled into a web application for accessing an Alfresco repository. We didn’t want to replace Share but we needed a way to migrate away from the original implementation (based around the Surf paradigms of Templates, Components and WebScripts) towards something faster to develop and easier to customize.

This is summary of what we wanted to achieve:

    • Provide a library of fine-grained, decoupled widgets that completely encapsulate all of their function, styling and localization behaviour
    • For those widgets to be easily unit testable across multiple browsers
    • To reduce the complexity of Surf rendered pages by removing Page, Template and Component configurations and replacing them with pages defined by an easily customisable JSON model in the JavaScript controller of a single WebScript
    • To be able to dynamically build pages both for and within the running UI and render them without restarting any servers
    • To provide a foundation for our business partners and customers to build their own solutions on

 

 

 

Building on Previous Work

Those of you that have been following the changes in Share from version 4.0 to 4.2 (Community) will have noticed significant enhancements to the Surf framework and the deliberate refactoring of logic from the FreeMarker template into the JavaScript controller of the Share WebScripts. We’ve also focused on improving page load performance by reducing HTTP requests by:

     

    All of these features are leveraged in the improvements and we believe it will make it easier to produce faster, more reliable pages and in less time for the Alfresco Share and the Alfresco in the Cloud web applications. The improvements build on top of Dojo and provide (with the greatest of respect to the Dojo developers) a couple of significant enhancements…

     

    Zero Build

    The first enhancement is a zero build approach. Surf is able to dynamically analyse the JSON model that defines the page being rendered and resolve all JavaScript dependencies that are then compressed and written into a single JavaScript resource that simulates a built Dojo layer. Surf caches all the dependency relationships as it finds them so it never traverses a dependency path a second time. It also caches the individual layers it generates so although the very first page rendered after server start up may take a few seconds to render, subsequent page rendering will be incredibly fast.

     

    Encapsulation

    When you render a web you are normally expected to take care of the styling via separately referenced CSS files (for example you might import a theme style sheet for the framework that you are using). We’re taking a different approach.

     

    Each widget can optionally define its own CSS resources (including the ability to specify different resources for different media types). If that widget is used on a page then Surf will automatically include those CSS resources within a single aggregated CSS resource loaded on the page. The same principle is applied to localization files so the use of a widget ensures that its NLS properties automatically built into a JavaScript resource loaded on the page. Widgets can scope their message bundles to ensure that there are no collisions and the core message handling function is smart enough to work through all of the message scopes in a widgets ancestry to ensure that the most specific message is displayed.

     

    Cross JavaScript Library Support

    At Alfresco we believe in using the right tool for the job. Although Share was originally built using YUI2 we have also introduced JQuery plugins and we have no intention of restricting Share to just Dojo. The framework is designed to easily support widgets provided by other libraries and by design we can easily swap out Dojo widgets for those provided by other libraries. We have also provided a mechanism for wrapping our existing YUI2 centric widgets to that they can be referenced in the JSON model for the page. If you go to the Calendar of any Site you’ll see YUI2, JQuery and Dojo all playing nicely together.

     

    Continued Extensibility

    One of Share’s greatest strengths is its ability to be easily customized. This is something that we’ve taken great efforts to enhance over the last few releases and is essential that we continue to support. The last significant improvement we made was to make it easier to customize the client-side widgets that are instantiated on each page by customizing the JavaScript controller of a Component WebScript. We’re continuing to support this paradigm but instead of customizing a single coarse-grained widget it is now possible to customize the numerous fine-grained widgets that comprise a page or Component. Widgets can be reconfigured, added or removed easily and because the widgets are decoupled (such that they don’t rely on the existence of other widgets) making changes won’t cause any errors.

     

    Mighty Oaks and Little Acorns

    At the moment we’ve only used these new capabilities to build a header bar to accompany the “Light” theme that arrived in 4.2 Community. However this is the first step along a longer path towards making Alfresco Share better than ever. Our library of widgets currently only consists of the handful required to construct our header (but already allow you to build your own header or customize the default one without writing a line of JavaScript, CSS or HTML) but as we add new capabilities to Share this widget library will continue to grow. As I promised at DevCon 2012 – we’re not going to discard all the hard work we’ve done over the last 5 years, hopefully this shows our commitment in respect of that goal.

    wabson

    Media Viewers 2.5 Released!

    Posted by wabson Employee Feb 1, 2013
    If you've been following the recent activity on Share Extras you'll notice that most of the work for the last few weeks has been going into the Media Viewers project and in particular the 'PdfJs' based viewer which it supplies.



    If you're not familiar with the underlying pdf.js project then you should know that it provides a means of direct rendering of PDF content items using HTML5 and JavaScript in the web browser, with no requirement from additional client-side plugins such as Flash.



    Combined with the ability of the Alfresco repository to transform just about any standard document into PDF format, we think this provides a compelling reason to try the Media Viewer add-on if you have not done so before.



    An initial version of the PdfJs viewer shipped in the Media Viewers 2.0 release for Alfresco 4, and version 2.5 which we're releasing today builds on that with support for Alfresco 4.2 while also maintaining backwards compatible with the 4.0 and 4.1 Alfresco product lines.



    There's so much in this release that it felt like much more than a v2.1 release and 2.5 seemed much more appropriate. You can read on for more details of these exciting new capabilities, or go straight to the download, installation and configuration instructions to try it out today.

    Easy Configuration



    The first thing you'll notice when enabling the add-on using Share's Module Deployment console is that additional modules have been added, which provide the configuration needed to bind the viewer into the document details page.



    Unless you want a specific custom configuration, you can just enable the new PdfJs Configuration module (in addition to the existing PdfJs Viewer module), click Apply Changes and you should see PDF documents will now use the new viewer. You no longer need to edit your web-preview.get.config.xml file although if you've made changes there they will continue to be respected.





    Persistent View Settings



    Can't see the text clearly? You can use the zoom controls in the viewer to zoom in, our out if you want to see more. Or pick a preset from the drop-down and the zoom will be auto-calculated.



    The next time you come back to the document, these settings - plus the page number you were on - will be remembered.

    Powerful Search



    Use the Search button in the toolbar or Ctrl+F (Cmd+F on MacOS) to search through the document contents for a particular item of text. You can easily step through the matches one by one, or highlight all matches found.







    When you've found what you're after, you can copy the relevant text to the clipboard in order to include it somewhere else, for example in an e-mail.



    The feature I like the most will make you wonder how you lived without it before, if you've ever searched for a document in Alfresco and then struggled to find the reference to that term in the document itself.



    Now, the viewer detects that you have clicked through from the search page, and automatically searches for and highlights the first match of that term. Just hit Enter to continue through any further matches, or Escape to close the search dialog.

    Link to Document



    The Link button in the toolbar generates a link to the document, or even to a specific page in the document, that you can send to co-workers. If you want them to review Slide 24 in your presentation then simply send them straight there.





    Improved Sidebar



    You can click the button on the left of the toolbar to toggle the side bar on and off. The side bar shows a thumbnail view of all pages in the document by default and can also show an outline of the document structure if available in the file.





    Presentation Mode



    At DevCon last November Jared neatly showed how you can use the the new Google Docs integration to deliver a presentation directly from your web browser using Google Drive's presentation mode, but with the PdfJs viewer you can now do the same thing directly from the Document Details page.



    Just click Ctrl+Enter (or Cmd+Enter on MacOS) to go full-screen, then use the left and right arrow keys to move between your slides (the arrow keys will also work in the normal Document Details view).

    Document Viewer Dashlet



    Just like Site Admins can use the Wiki dashlet to add a selected page to a site dashboard, the Document View dashlet supplied with the add-on allows you to display any chosen document on the dashboard.



    The dashlet works well with the PdfJs viewer, but will also work with the others supplied by the add-on or the base viewers supplied with Share (yes, that includes the Flash document viewer, if you really want).





    Password-protected PDFs



    Viewing password-protected PDFs is no longer a problem with the PdfJs viewer. You will be promoted for the password required to open the file and once supplied the viewer will work as normal.





    Lots More



    Of course there are many other improvements that you can't see directly, but that you will benefit from. Peter has been working tirelessly to keep the version of the pdf.js library up-to-date, ensuring that we stay up to date with the improvements in performance and compatibility that the latest versions now provide. We have also improved the performance of page rendering within the dashlet code itself, with improved lazy rendering of all page and thumbnail content.



    And of course PdfJs is not the only viewer in the add-on. The Flash-based audio and video players are still supplied, the Embed viewer offers a compelling Flash-free alternative for IE users not on IE 10 who have the Adobe Reader or another PDF plugin installed and the Prettify viewer is great at syntax highlighting any code stored in Alfresco.

    Feedback



    Like all Share Extras add-ons, we welcome constructive feedback to help us to deliver the next version of Media Viewers. Please use the Issues section on the project to report specific technical issues or ideas for enhancements, or general feedback can be left as comments below.

    Filter Blog

    By date: By tag: