cancel
Showing results for 
Search instead for 
Did you mean: 
ddraper
World-Class Innovator
World-Class Innovator

Introduction

Now that Alfresco Community 4.2d has been released I thought it would be useful to begin providing some examples of how to customize the new header bar. The new header bar is the first feature in Share to use the updated widget processing framework provided by Surf. In this post I will demonstrate how to update the default menu to convert the 'Admin Tools' link into a drop-down menu with links to all of the individual Admin Console pages. In later posts I'll show how to remove menu items and how to add custom widgets into the header.

Comparing the Headers

Let's first compare the old and new header menus...

Screenshot of the header from Alfresco Share 4.1

Screenshot of the Alfresco Share 4.2 header

As well as the obvious stylistic differences there are some major implementation differences. The original header was rendered by a combination of the 'header', 'title' and 'navigation' regions whereas these have been replaced by a single region called 'share-header'.

This new region is bound to the 'share-header.get' WebScript which uses the previously described approach to define the JSON model of widgets that comprise the header.

The structure of the old header was defined in the 'share-config.xml' configuration file whereas the new header is entirely defined by the 'share-header.get' WebScript's JavaScript controller. It is possible to render the header using the configuration from the 'share-config.xml' file by changing the 'legacy-mode-enabled' element value from 'false' to 'true'.

Creating the Extension Module

To customize the header you should follow the approach described in this blog post to extend the JavaScript controller of the 'share-header.get' WebScript. To briefly recap, follow these steps:

    1. Create a JAR containing the following folder structures:
      1. /alfresco/site-data/extensions
      2. /alfresco/site-webscripts
    2. In the '/alfresco/site-data/extensions' folder create a file called 'blog-extension.xml' (you can call it anything you like of course!)
    3. In the 'alfresco/site-webscripts' folder create a package path for the controller extension (e.g. 'blogs/admin-menu' - but again you can use any package you like)
    4. In the 'alfresco/site-webscripts/blogs/admin-menu' folder create a file called 'share-header.get.js'

Your 'blog-extension.xml' file should contain the following:

<extension>
  <modules>
    <module>
      <id>Update Admin Menu</id>
      <version>1.0</version>
      <customizations>
        <customization>
          <targetPackageRoot>org.alfresco.share.header</targetPackageRoot>
          <sourcePackageRoot>blogs.admin-menu</sourcePackageRoot>
        </customization>
      </customizations>
    </module>
  </modules>
</extension>

The 'targetPackageRoot' element identifies the location of the 'share-header.get' WebScript and the 'sourcePackageRoot' element identifies the location of your extension file. You now have the basic elements required to customize the header - everything else will be done in the '/alfresco/site-webscripts/blogs/admin-menu/share-header.get.js' file.

Customizing the Header

The purpose of a JavaScript controller is to construct a model object that the FreeMarker template uses when generating a fragment of DOM for the page. The model object created by the default JavaScript controller is made available to each extension controller before it is passed to the FreeMarker template and this provides them the opportunity to change the model before it is processed.

The model object is assigned an attribute called 'jsonModel' with a value that is a JSON object containing all the widgets to render in the header. Each widgets is given a unique 'id' attribute so that they can easily be found by extensions using the 'widgetUtils.findObject' helper function.

In this example we're going modify the 'alfresco/menus/AlfMenuBarItem' widget that creates the 'Admin Tools' menu item. The original definition of this can be found in the 'share-header.lib.js' library file (that is imported by 'share-header.get.js') and is:

{
  id: 'HEADER_ADMIN_CONSOLE',
  name: 'alfresco/menus/AlfMenuBarItem',
  config: {
    label: 'header.menu.admin.label',
    targetUrl: 'console/admin-console/application'
  }
}

‍‍‍‍‍‍‍‍‍

We can work with this widget by adding the following to our 'share-header.get.js' extension file:

var adminMenu = widgetUtils.findObject(model.jsonModel, 'id', 'HEADER_ADMIN_CONSOLE');

if (adminMenu != null)
{
  adminMenu.name = 'alfresco/header/AlfMenuBarPopup';
  delete adminMenu.config.targetUrl;
}

This will find the widget (which will only be present in the model if the currently logged in user is the Admin) and then change the widget definition's 'name' attribute to be 'alfresco/header/AlfMenuBarPopup' which will convert it into a drop-down menu. We then delete the 'targetUrl' attribute because it is not required.

The final step is to add in the contents of the drop-down menu (I'm only going to show the first sub-group defined but you can find a JAR file containing all the source here😞

var adminMenu = widgetUtils.findObject(model.jsonModel, 'id', 'HEADER_ADMIN_CONSOLE');
if (adminMenu != null)
{
  adminMenu.name = 'alfresco/header/AlfMenuBarPopup';
  delete adminMenu.config.targetUrl;
  adminMenu.config.widgets = [
    {
      name: 'alfresco/menus/AlfMenuGroup',
      config: {
        label: 'Tools',
        widgets: [
          {
            name: 'alfresco/header/AlfMenuItem',
            config:
            {
              label: 'Application',
              targetUrl: 'console/admin-console/application'
            }
          },
          {
            name: 'alfresco/header/AlfMenuItem',
            config:
            {
              label: 'Category Manager',
              targetUrl: 'console/admin-console/category-manager'
            }
          }
        ]
      }
    }
  ];
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Here we're adding a group with the label 'Tools' which contains two menu items labelled 'Application' and 'Category Manager' which each have a page relative (that is relative to '/share/page') URL for the page that they represent.

Copy the JAR to the '<tomcat-home>/webapps/share/WEB-INF/lib', restart your server and go to the 'share/page/modules/deploy' page and deploy your new module.

Screenshot showing the Module Deployment page

Login to Share as 'Admin' and you'll see that the 'Admin Tools' menu has now been converted into a drop-down menu (the screenshot shows the extension provided in my sample JAR):

Screenshot showing the updated "Admin Tools" menu

Summary

Although I haven't yet explained exactly what all the widget definitions mean, which widgets are available or how you can configure each of them... this example should at least show you how you can easily update the new header to suit your needs.

You may wonder how exactly this is more beneficial than updating the menu via configuration? The main advantages are as follows:

    1. It is possible to target specific parts of the menu without including the entire menu definition. This means that multiple definitions can be deployed together that independently alter the menu without overriding the each others changes
    2. It is possible to to leverage the existing extension module features to dynamically alter the menu based on a variety of criteria including user group, page context, user privileges, etc. by deploying modules with evaluators.
    3. It is possible to dynamically add and remove features without restarting the server.
    4. There is complete flexibility in what gets added to the header - you are not constrained to any particular widget set and can easily add custom widgets that are entirely new or extend existing widgets in the Alfresco library.
    5. It's very easy to re-use all or parts of the existing menu when creating new pages or applications.


    Hopefully this information is useful as a starting point. In my next post I'll demonstrate how you can extend an existing widget and use it in the menu.

    23 Comments
    blog_commenter
    Confirmed Champ
    Confirmed Champ
    [...] my last post I explained the basic differences between the old (4.1) and new (4.2) Share header bars. In this [...]
    blog_commenter
    Confirmed Champ
    Confirmed Champ
    [...] the Dave Draper posts that describes the new header bar using the updated widget processing framework provided by Surf of [...]
    blog_commenter
    Confirmed Champ
    Confirmed Champ
    [...] get the most out of this post you should ideally read the previous two entries in the series as the first provides detailed information on creating a JAR file for your extension and the second provides [...]
    blog_commenter
    Confirmed Champ
    Confirmed Champ
    [...] on the version of Share – in share-config-custom.xml for the old style header – see  here for the new style [...]
    blog_commenter
    Confirmed Champ
    Confirmed Champ
    Hi,



    This article where very usefull. It worked, and I needed to make my own and it worked. Thanks.

    However, I would like to know:

      - How to debug my extension: my script is not listed with other javascripts, javascript debuger didn't showed share-header code, so I cannot step into my extension.

      - How can I change my code without restarting the server. Apparently, 'Refresh Web Scripts' don't refresh my extension



    Thanks

    Sérgio
    ddraper
    World-Class Innovator
    World-Class Innovator
    @Sergio  Unfortunately the extension JavaScript controllers don't show up in the Rhino JavaScript debugger - it's something that I'd like to resolve, but haven't had the time to address yet.  As for refreshing your extension, it really depends upon how you've deployed it - I'm surprised that Refresh WebScripts doesn't work because they clear the WebScript cache - not to mention the fact that extensions are dynamically applied on a per request basis. It's possible that you may need to restart the server - particularly if you're deploying these as a JAR file. It could be a class loader issue.
    blog_commenter
    Confirmed Champ
    Confirmed Champ
    I deployed the module in an amp, so it is not inside a jar, enabled logging (log4j.logger.org.alfresco.repo.jscript.ScriptLogger=debug), confirmed that logger-log() is logged. Then went to alfresco/site-webscripts/package-name/share-header.get.js , changed the log, reloaded share and saw that the first message is the one being logged.



    I think it proved that the script is not refreshed.



    Like this I have no productive way to develop those scripts :-(.

    Any hint?



    Thanks
    ddraper
    World-Class Innovator
    World-Class Innovator
    Develop them locally within Tomcat (or whatever web server you're using) and only package them up into the AMP when you're happy with them?
    blog_commenter
    Confirmed Champ
    Confirmed Champ
    I indeed use tomcat. I tried to develop the script without package him, adding it to alfresco/site-webscripts by hand, deploy the module and then edit and refresh but it wont refresh either.



    I also tried to add it as a normal webscript, but then, the information loaded by the standard share-header.get.js is not loaded and the script is useless 😞
    ddraper
    World-Class Innovator
    World-Class Innovator
    @Sergio... it looks like you're just going to have to restart the server for the changes to take effect then unfortuntately (I'm assuming that works, if restarting doesn't capture the WebScript extension updates then it suggests you're somehow deploying your changes incorrectly)