1

Since some days ago I'm struggling to find out the best way to add a custom action in every dashlet's title bar by default.

At this stage, I know the actions are set up as a widget in every dashlet's webscript. For example, in docsummary.get.js:

   var dashletTitleBarActions = {
      id : "DashletTitleBarActions",
      name : "Alfresco.widget.DashletTitleBarActions",
      useMessages : false,
      options : {
         actions: [
             {
                cssClass: "help",
                bubbleOnClick:
                {
                   message: msg.get("dashlet.help")
                },
                tooltip: msg.get("dashlet.help.tooltip")
             }
         ]
      }
   };
   model.widgets = [docSummary, dashletResizer, dashletTitleBarActions];

Then, the widget is instanciated in when the page is rendered.

I have figured out the next approaches:

  1. Augment the "actions" array of every widget instance with the action I need in a custom JS snippet, every time the page is rendered. I have performed some tests without success using techniques like this: http://acidmartin.wordpress.com/2012/03/19/getting-instance-names-of-a-javascript-object/

  2. Modify the prototype of Alfresco.widget.DashletTitleBarActions by adding my custom action. I believe it doesn't work either as the "actions" object is always overridden when the widget is instantiated, as you can see in the code above pasted.

  3. Create an extension module for every dashlet's webscript which adds the custom action for every Alfresco.widget.DashletTitleBarActions widget definition, similarly as Dave Drapper explains in his post http://blogs.alfresco.com/wp/ddraper/2012/05/22/customizing-share-javascript-widget-instantiation-part-1/

  4. Get every dashlet div container and add the action required directly manipulating the DOM once the page is ready. It should work but is something I consider slightly dirty and inconsistent, hence I would like to avoid it.

Could anyone imagine a better and feasible solution??

  • What kind of action would you like to add? I believe that 2 is the correct way any way you put it,regardles of your answer. – Zlatko Jan 29 '13 at 17:14
  • An action like help action for every dashlet. Nevertheless, I believe option 2 can be discarded as even if I modified the original share.js (where objet Alfresco.widget.DashletTitleBarActions is defined) it wouldn't work as the webscript of every dashlet is overriding the "actions" object as you can see in the code snippet above. That means any possible "preloaded" action would be deleted... – Alejandro García Seco Jan 29 '13 at 17:58
  • Option (2) is not appropriate because you should never modify the prototype of a core Alfresco JS class. – Will Abson Jan 29 '13 at 18:16
  • I think option 2 would be appropriate if I could override the onReady method as @zladuric explains in his answer, but without changing the original share.js file. I strongly consider a bad practice and an inappropriate solution the fact of changing Alfresco's source code, whatever the resource is.. that would be my very last resource. – Alejandro García Seco Jan 30 '13 at 10:50
  • 1
    I disagree. You should never modify Alfresco classes, even if the modifications are in another file. Instead you should _extend_ off those classes with your own implementation (which should be in your own namespace) using `YAHOO.lang.extend()` and then inject in the name of your custom class using an extensibility module. – Will Abson Jan 30 '13 at 12:10
  • Alright, I will follow your advice. Thanks. – Alejandro García Seco Jan 30 '13 at 13:05

2 Answers2

4

Let's start with adding an action to a single existing dashlet. As you suggest in (3) you can define an extensibility module to change the behaviour of the dashlet by intercepting and modifying its model.

Creating an extensibility module is well covered on the blog article and follow-up posts, but the trick here is to provide a controller JavaScript extension which locates the DashletTitleBarActions widget and adds your action to it, e.g.

if (model.widgets)
{
    for (var i = 0; i < model.widgets.length; i++)
    {
        var widget = model.widgets[i];
        if (widget.id == "DashletTitleBarActions")
        {
            widget.actions.push({...})
        }
    }
}

What you put in the object literal depends on how your action is implemented. If you require some client-side behaviour (rather than say, a static link) then you will also need to bind that in using a CustomEvent - see the RSS Feed dashlet org/alfresco/components/dashlets/rssfeed.get.html.ftl for an example.

The down-side of an extensibility module is that you will need to define an explicit extension JS file for each dashlet. You could easily put the code above in a central file and then include it in each dashlet extension where it is needed, e.g.

<import resource="classpath:alfresco/site-webscripts/org/myco/utils/dashlet.utils.js">
Will Abson
  • 1,562
  • 7
  • 13
  • One possible issue, if you import this file from a central JS, that particular JS file could overwrite model.widgets, right? – Zlatko Jan 29 '13 at 21:12
  • This approach is proved that works but I don't like at all the idea of creating a webscript.get.js for each dashlet as you have mentioned, even isolating the common code as suggested and as Alfresco originally does usually. Would be perfect if there was any way of dealing with a common model or manage all models from a single point, for example within an evaluator as I was trying without success. Unfortunately as per of my lack of knowledge cannot guess whether that would be doable or not... – Alejandro García Seco Jan 30 '13 at 10:56
  • @AlejandroGarcíaSeco Yes it seems both the source and target of an extension must be packages, so you cannot apply a single .get.js file to all scripts below a particular location (or to a family). – Will Abson Jan 30 '13 at 12:05
1

I would take the road number 2 here.

The definition of this widget is in share.js file ({share.context}/js/share.js). For Alfresco 4.2, DashletTitleBarActions is defined at around line 1700. In the onReady handler of the widget, there's a loop that processes the actions.

 // Reverse the order of the arrays so that the first entry is furthest to the left...
        this.options.actions.reverse();
        // Iterate through the array of actions creating a node for each one...
        for (var i = 0; i < this.options.actions.length; i++)
        {

As you can see, it reverses the order of actions parameter, and then adds starts to loop the actions. So depending if you want your action to be the first or last, you could probably edit this file and add your custom action:

myAction = {
"cssClass": "customCSS"
, "tooltip": this.msg("slingshot.messages.generic.tooltip")
, "eventOnClick": ...
...
}
this.options.actions.push(myAction);
// now move on with the rest of the loop
for (...)

Of course, that requires overwriting shares' own js file, and not extending it. If you can't do that, you would then have to include a custom JS file on each page where share.js is also included and make sure it's executed after share.js but before any of the widgets are ready, and overwrite the onReady method of the widget itself so that it does this.

Zlatko
  • 18,936
  • 14
  • 70
  • 123
  • It is my priority to avoid modifying original Alfresco source code (JS files, Java classes or whatever) and perform customization as less intrusive as possible. If there is a way to effectively override the onReady would be quite interesting to know, I have been trying it without success. Thanks for your answer. – Alejandro García Seco Jan 30 '13 at 10:46