Customizing the Share Header Menu (Part 1)

cancel
Showing results for 
Search instead for 
Did you mean: 

Customizing the Share Header Menu (Part 1)

Intermediate II
2 23 8,352

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
    Active Member
    [...] my last post I explained the basic differences between the old (4.1) and new (4.2) Share header bars. In this [...]
    Active Member
    [...] the Dave Draper posts that describes the new header bar using the updated widget processing framework provided by Surf of [...]
    Active Member
    [...] 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 [...]
    Active Member
    [...] on the version of Share – in share-config-custom.xml for the old style header – see  here for the new style [...]
    Active Member
    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
    Intermediate II
    @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.
    Active Member
    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
    Intermediate II
    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?
    Active Member
    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 :-(
    Intermediate II
    @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)
    Active Member
    With server restart it works fine. All modifications are applied. It's not the optimal solution, but it works.



    Thanks Dave.
    Active Member
    […] header in Alfresco 4.2 Enterprise. Information on how to create and deploy your extension module have been covered in detail before so I’m not going to go over those now, instead I’ll just focus on the controller […]
    Active Member
    Excellent, thanks. What file I need to modify to move the menu position, below the banner?
    Intermediate II
    @Albert - both the banner and the menu are defined in the share-header.get WebScript so you just need to work with that file. If you want to move the banner above the menu then you just need to re-order those elements in the JSON model.
    Active Member
    Can this idea be used to remove certain menu items from the view of certain user groups? I want to prevent a user group from seeing sites or the repository.



    Thank for any help anyone can offer.
    Active Member
    When I do this, my extension doesn't appear in the Module Deployment list.  I must be missing something somewhere.  What are the requirements for Alfresco Share to recognize an extension as being valid?
    Intermediate II
    @Brian the extension configuration file just needs to be located in a valid path and for the XML to contain all the required attributes. This post was written a while ago but should still be valid for recent releases. What version of Alfresco are you using and have you reproduced the steps accurately?
    Active Member
    Hi Dave,



    Firstly I thank you for writing such a good entry in your blog explaining this new approach on Share developments.



    Secondly, I would like to know if there are available more documentation about widgets and other examples. In particular I would like to know if it is possible to do the same example you show here but using Share AMP archetype to package the project rather than the Share module. I could not get it working, though.



    Thanks,

    David
    Intermediate II
    @David - yes, there is some more (albeit limited) information available. You can build JSDoc for all the new widgets and I've also done more blog posts recently. We're hopefully going to be providing more documentation and tutorials in the near future.



    I'm not sure I'll have time to update the blog posts to include AMP information... my priority is to make information available on Aikau and AMP'ing modules is a separate topic which can be covered/discussed elsewhere, I'd recommend trying the IRC channel or the Alfresco forums.
    Active Member
    Hi Pls any one help!

    How to remove the Upload button in alfresco 4.2.f



    Thanks in advance
    Active Member
    Hi Dav,



    We have requirement of implementing the global search.

    i.e We need to retrieve the search results from multiple repositories and display the results in global search page.



    Could you suggest some way of implementing this requirement.



    Thanks Regards

    Venkat
    Intermediate II
    @Venkat - that sounds like a federated search request (e.g. across multiple repositories). That sounds like more of a back-end problem that an client-side issue. If you want those search results in the live search widget in the header then you'll just need to override the search API that is used... as I said though, it sounds more like a back-end problem than a client-side issue.
    Visitor

    OMG. I've been looking for a similar blog-post for so long. Dave now I'm your fan