Customizing the Share Header Menu (Part 2)

cancel
Showing results for 
Search instead for 
Did you mean: 

Customizing the Share Header Menu (Part 2)

Intermediate II
0 28 6,922

Introduction

In my last post I explained the basic differences between the old (4.1) and new (4.2) Share header bars. In this post I'm going to provide a slightly more complex example that demonstrates the following:

  • How to create a custom widget by extending an existing widget in the Alfresco library
  • How to configure additional widget library packages in Surf
  • How to insert that custom widget into the new header


The current header menu features a 'Sites' drop-down menu that allows you to quickly return to one of the last few sites that you previously visited. The example extension I'm doing to demonstrate (provided in full in this JAR file) will update the 'Sites' drop-down menu to convert each recent site link into a cascading menu that allows the user to go straight to a specific page within the site (e.g. the Document Library or Wiki).

Recent Sites

I've personally found the Recent Sites list an incredibly valuable addition in my day-to-day development and testing of Share. The recent sites are stored in each users' preferences and are updated as they visit different sites. As well as appearing in the header menu they also are available for selection in many of the file pickers (e.g. 'Copy' 'Move', etc). Only the last 5 sites are shown by default but this can be changed by updating the 'max-recent-sites' element found in the 'share-config.xml' file.

Screenshot showing the recent sites in the Sites drop-down menu

The Sites Menu

The 'Sites' drop-down is unlike the other items in the menu as it is generated asynchronously when the user clicks on it. This provides a very small performance enhancement as it is unlikely as a user will want to access the menu on every page they visit. Similarly the 'Favourites' cascade menu isn't populated until a user actually clicks on it.

This functionality is too complex to define in a basic JSON model so it is controlled by the widget 'alfresco/header/AlfSitesMenu'. It is important to note that the all of the items within the 'Sites' menu are still built in exactly the same way, using exactly the same widgets that are used in the JSON model.

To achieve our use case we will need to customize the 'alfresco/header/AlfSitesMenu' widget. This can be achieved in two different ways:

  1. By changing the default configuration (which I'll cover in a future post)
  2. By extending the default widget, overriding some of it's functions and then replacing the default widget with the customization.

Extending a Widget

We're going to create a new widget called 'blogs/BlogSiteMenu' and we're going to place this widget in the 'META-INF/js' folder of the JAR that we're going to build (as this is the only location that Surf is able to load resources from within JAR files). I've previously described custom widget creation in this blog post but here is a recap of the basic steps with a focus on extending an existing widget.

Create the following file in the JAR: 'META-INF/js/blogs/BlogSiteMenu.js' and add the following code to it:

// Define module requirements and callback...
define(['dojo/_base/declare','alfresco/header/AlfSitesMenu'],
       function(declare, AlfSitesMenu) {

  // Module returns a new widget that extends AlfSitesMenu
  return declare([AlfSitesMenu], {
   
// Override the '_addMenuItem' function of AlfSitesMenu...
    _addMenuItem: function(group, widget, index) {
       this.inherited(arguments);
    }
  });
});‍‍‍‍‍‍‍‍‍‍‍‍‍

This code is the absolute minimum required to define a new widget that extends an existing one. The 'define' statement declares the dependent modules and passes them as arguments to a callback function that will be executed when the module is required.

The callback function declares a new widget that extends the 'alfresco/header/AlfSitesMenu' widget (which has been assigned to the 'AlfSitesMenu' argument). This new widget overrides the '_addMenuItem' function.

The function override simply calls the inherited function (i.e. calls the version defined in 'alfresco/header/AlfSitesMenu') so doesn't actually achieve anything... but the point is to illustrate how to create the extension. The full source is contained in the JAR file which you should download and read through (even if you don't deploy it) - this isn't a tutorial on writing JavaScript !! ;-)

Declaring Packages in Surf

Our custom widget is defined in the 'blogs' package (the part of the path that we're going to consider as the package root) and we need to add this to the Surf configuration so that the Dojo AMD loader can locate it.

Edit the '<tomcat-home>webapps/share/WEB-INF/surf.xml' configuration file and look for the 'dojo-pages' element. Within this element you'll find a list of packages and its here that you will need to add the location of the 'blogs' package as shown in the example below:

<dojo-pages>
   <bootstrap-file>/res/js/lib/dojo-1.9.0/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.9.0/dojo'/>
      <package name='dijit' location='js/lib/dojo-1.9.0/dijit'/>
      <package name='dojox' location='js/lib/dojo-1.9.0/dojox'/>
      <package name='alfresco' location='js/alfresco'/>
      <package name='blogs' location='js/blogs'/>
   </packages>
</dojo-pages>‍‍‍‍‍‍‍‍‍‍‍‍

Swapping in the Custom Widget

The previous post provided detailed instructions on how to create the extension module so I'm going to jump straight into working with the 'share-header.get.js' JavaScript controller extension. To replace the default 'Sites' menu widget with our own we simply need to add the following code:

// Find the 'Sites' menu...
var sitesMenu = widgetUtils.findObject(model.jsonModel, 'id', 'HEADER_SITES_MENU');
if (sitesMenu != null)
{
  // Change the widget to our custom menu...
  sitesMenu.name = 'blogs/BlogSitesMenu';
}

The controller extension finds the default sites menu (identified as 'HEADER_SITES_MENU') and then changes the name that defines the module to use to that of our custom widget. When we deploy our module and refresh the page we'll see that the 'Sites' menu has been updated:

Screenshot of updated sites menu 1

Screenshot of updated sites menu 2

Screenshot showing updated menu 3

Additional Notes

Hopefully the comments in the JavaScript source should be sufficient for you to understand exactly how the new widget creates the new menus. Extending a widget obviously requires an understanding of the widget being extended and a good knowledge of JavaScript.

Although I've used other Dojo modules in the code it's not mandatory to do so. It's important to remember that Dojo is merely the tool used for resolving the AMD modules and that you're free to use whatever JavaScript libraries that you choose.

You might also note that the JAR file contains a new WebScript for retrieving the site page information. The majority of the work is actually done in the 'share-header.lib.js' file that is imported at the beginning of the 'site-pages.get.js' file and provides another example of how we're trying to make it easy for 3rd parties to re-use Alfresco code.

Summary

This tutorial should have demonstrated that you are not limited to any configuration schema when customizing the new Share header. The Surf framework now allows you to easily plug-in completely custom JavaScript code into Share which allows you to make fine-grained changes to existing code or to add entirely new functionality.

28 Comments
Active Member
Hi Dave,



Thanks for this series, I think it's really useful to see practical examples.  Also, I know this is just an example/tutorial, but I think the idea to allow the site pages to be picked from the recent sites is an excellent one and would prove very useful if incorporated into the core product!



One question though, the modularity that's achieved by the new framework is great, but I still notice that there is a requirement to modify the core surf.xml file any time you need to declare a new package.  What's the most practical approach for someone developing a new module to be able to do this? 



E.g. an addon built to be included in alfresco-addons wouldn't want to include a modified surf.xml as that would break any other modules the end user had deployed.  Is it going to have to be done through installation instructions (e.g. add this line to your surf.xml file...) or is there a better practice here?



Regards



Steven
Intermediate II
Hi Steven, thanks for the feedback - it's much appreciated!



Technically, you don't *need* to declare a new module - you could just place your files in the 'alfresco' package structure as that would avoid needing to add the package (however, that's obviously a workaround rather than a solution).  I'll investigate ways to improve this so that it can be done when deploying modules, but it's unlikely to get in for 4.2 Enterprise now unfortunately - I've raised and issue to address this: https://issues.alfresco.com/jira/browse/ALF-20012



Ultimately Surf is creating configuration for the Dojo AMD loader, and given that this is done via JavaScript it is possible that it can be done via some client-side JavaScript loaded onto the page (but I'd need to check).



Regards,

Dave
Active Member
Thanks for the response Dave,



It would great long term if packages could be declared dynamically via client side Javascript, but as you say not for 4.2!



In the meantime though, rather than adding in to the alfresco package, possibly a new entry could be added in the out of the box surf.xml that defined a package for extensions.  Still a workaround, but it would avoid the above problems without polluting the alfresco namespace, e.g.







Regards



Steven
Intermediate II
Hi Steven,



I think WordPress swallowed your code suggestion there Smiley Happy  Can you update again, using lt and gt brackets?



Regards,

Dave
Active Member
[...] comment on a previous post questioned why it was necessary to update the “surf.xml” [...]
Intermediate II
Active Member
Hi David,



I have been playing with the new widgets and think its a great step foreward in the share customisation framework.   I have been trying to create a page with with a list of documents of a specific type and have assumed I can use the DocumentLibraryList widget for this.   I have been getting my head around dojo a bit to keep up with these changes but I am not really a js developer so am struggling with the document library widgets.  Is there any documentation or examples on how to include a document list on a page using the new framework. 



Thanks



Brian
Intermediate II
Hi Brian,



I saw that you have also asked this question on the forums and have posted an answer to it there: https://forums.alfresco.com/forum/developer-discussions/alfresco-share-development/how-use-alfdocume...



Regards,

Dave
Active Member
[…] the series as the first provides detailed information on creating a JAR file for your extension and the second provides some useful information on the “Sites” drop-down […]
Active Member
Hello,



I tried the first part out. But there is no page. Is this only for enterprice? Or should there be some extra jars installed?



I have 4.2.f community.



Yours,



Jyri
Intermediate II
@Jyri - I don't quite understand what you mean... which page is missing?
Active Member
Thank you for answering all though not much info on the question. I read on and I am now heading forward with these tutorials. Very good material thank you!!
Active Member
I added the jar file. But it says:



2014-05-07 08:52:26,015  ERROR [extensions.surf.DependencyAggregator] [http-apr-8080-exec-2] Could not find compressed file: js/lib/blogs/BlogSitesMenu.js



Must it be compressed in order to work?
Intermediate II
@Jyri - no, it's not that it needs to be compressed... when running in production mode Surf will automatically compress the files for you, they don't need to be compressed. I would expect that you've missed a step somewhere along the way in the tutorial - are you sure that the file is available on your web server?
Active Member
Hi Dave,



If i want to change literals, for example word Site by 'Whatever' (all occurrences) How should I process? I tried to replace the i18nRequirements: [{i18nFile: './i18n/MyCustomFile.properties'}], with my custom but not success



Thanks in advance
Intermediate II
@Ceeliro You need to actually update the model (e.g. extend the WebScript JavaScript controller) and change the label of the widget you want to update.
Active Member
thanks dave
Active Member
Hi Dave,



Thanks for the great post.



It worked  fine with Alfresco 5.0.1 which uses aikau-1.0.8.1 but does not seem to work with Alfresco 5.0.2 which uses aikau-1.0.25.2.

Can you please advice?



Regards,

Kavi
Active Member
I also get like that error in your first sample.

ERROR [extensions.surf.DependencyAggregator] [http-bio-8080-exec-5] Could not find compressed file: js/tutorials/sites-menu.js



How did you fix that?

Please explain me.

Thanks
Intermediate II
@Kavi - could you provide some information on how exactly its not working please?
Intermediate II
@Jyri - No, it doesn't need to be compressed - the compression will be done by Surf - it basically means that it can't find the file. Have you created the BlogsSiteMenu file? What path is it in the JAR? Are you absolutely sure that you have no typos when you're referencing it? Have you defined the 'blogs' AMD package?
Active Member
Hi, Dave! Thanks for the post. It is really helpful.

But unfortunately I can't find answer how to customize Share Header when we use widgets which are constructed in postCreate method, e.g. when I need to add additional menu item into 'advanced search menu'. Menu Items are widgets and they are added in postCreate method of widget 'alfresco/header/SearchBox.js'. Could you please give some advice?
Intermediate II
@Anton - unfortunately at the moment the SearchBox widget doesn't provide adequate support for easily adding additional items into the search menu (where you see the advanced search link).  You could raise a feature request in JIRA for us to improve this situation by making that menu configurable - that would be the ideal solution (but you'd need to wait for it to be implemented and included in an Aikau release). Alternatively you will need to extend the SearchBox widget and then extend the postCreate function - you should be able to call this.inherited(arguments) from in your widget and then update the menu programmatically.
Active Member
Thanks a lot. I've come up with the same idea. It may mean I understood this part of Aikau properly)))
Active Member
Hi Dave,



With the aikau 1.0.25.2 in Alfresco-5.0.2.5, these are the behaviour observed in different browsers,

In Firefox, it just shows 'Loading......', then it automatically brings you to the site dashboard.

In IE and Chrome, the cascading submenus are populated but you don't get the chance to click on them as it automatically brings you to the site board.



Regards,

Kavi
Intermediate II
@Kavi have you tried later versions of Aikau?
Active Member
Yes Dave.

Indeed I have tried aikau versions 1.0.32 and also 1.0.40. Same behaviour in both cases. I also tried version 1.0.24. Same behaviour.



Cheers,

Kavi
Occasional Visitor

Hi Dave,

Am trying to hide Create Site for users other than Admin and also add a new field Site Request for non-admin users. Am using alfresco 5.2.2 version.

Tried to access the jar which you shared earlier @ https://dl.dropboxusercontent.com/u/919772/BlogCustomiseHeader2.jar 

Seems the file is not available there.

Could you please suggest any better options(in alfresco 5.2.2) to add my required customization.

Thanks in advance.

Regards,

Naresh