WebScript Extensibility on the Alfresco Repository

cancel
Showing results for 
Search instead for 
Did you mean: 

WebScript Extensibility on the Alfresco Repository

Intermediate II
0 11 2,972

This no longer works in the most recent releases of Alfresco but an issue has been raised to restore it

Introduction

The WebScript libraries can be used independently of Surf and this is what the Alfresco Repository uses to provide its REST API. The Surf libraries build on the WebScript libraries and although some of extensibility classes and interfaces have been defined in the WebScripts libraries the actual extensibility container is defined as part of Surf. This meant that it was not possible to customize the Repository WebScripts which forced you to use the old approach of copying the relevant folders and files into the “web-extension” path and make the changes on the copies (which then introduced maintenance issues).

In an attempt to address this issue we’ve created a simple Extensibility Container for the WebScripts library which gives you the ability to perform some of the same customizations. Essentially you can perform the same types of customization described in my earlier blogs for extending i18n properties files, JavaScript controllers and the FreeMarker templates of a WebScript.

There are some important differences to be aware of though…

    • The new container isn’t the default container – if you want to make use of it then you’ll have to explicit update your Spring application context configuration.
    • There is no module deployment process – if the configuration for a module is found on the extensions search path then it will be automatically deployed.  The Surf layer provides all sorts of additional persistence options which aren’t available in the base WebScripts layer so it wasn’t possible to provide that level of flexibility.
    • Currently there are no evaluations although it may be possible to add this capability at a later date.
    • There are no <@markup> directives anywhere in the Repository code. This means that you cannot extend the FreeMarker templates as they are out-of-the-box. The only way to make use of this type of customization would be to manually update the original files yourself. You could argue that this defeats the purpose of the exercise but we’re not really sure how useful a feature this might be (we’re relying on feedback from the Community with in this regard!) and secondly, the use-case being considered is where the JSON response has additional content added to it. This can be achieved by simply adding a basic <@markup> directive to the end of the '.json.ftl' file which would require a negligible amount of maintenance.

Configuring the Container

To configure your Repository to use the ExtensibilityContainer you need to edit the 'webapps/tomcat/alfresco/WEB-INF/classes/alfresco/web-scripts-application-context.xml' file and update the definition for the 'webscripts.container' bean so that this line:

 

<bean id='webscripts.container' parent='webscripts.abstractcontainer'>

...becomes...

<bean id='webscripts.container' class='org.alfresco.repo.web.scripts.ExtensibilityContainer' parent='webscripts.abstractcontainer'>
  <property name='extensibilityModuleHandler' ref='extensibility.handler'/>‍‍

The  'extensibility.handler' bean is defined within the WebScript library JAR.

Example

This is a simple example of something that can be done (there are other ways you could implement this, I'm just using this approach for the benefit of the tutorial)...

Let's say that you want to update the 'Sites' drop-down menu on the header bar of Share to show not only the users favourite sites but also all the sites that they are a manager of.  The Share WebScript used to generate that drop-down menu is 'org.alfresco.modules.header.sites.get' where the controller 'sites.get.js' makes a remote call to the '/api/people/{user_ id}/preferences' REST API on the Repository which returns a list of the users favourite sites.

This REST API is served by the 'org.alfresco.repository.preference.get' WebScript which we can now extend to provide the additional data we'll need to show the sites that the user manages.

As with the Share customizations we're going to create a module in a JAR which we drop into the 'WEB-INF/lib' folder of the application (in this case 'webapps/alfresco/WEB-INF/lib'.  The module configuration file needs to be placed in the package 'alfresco.webscripts.extension.config' (although this is a default location for module configuration files can be overridden in the Spring application context).

Create a file called 'preferences-extension.xml' containing the following code (this should be familiar from the earlier Share related customization tutorials):

<extension>
   <modules>
      <module>
         <id>User Sites Menu Customization</id>
         <description>Add additional data to the users sites menu</description>
         <customizations>
            <customization>
               <targetPackageRoot>org.alfresco.repository.preference</targetPackageRoot>
               <sourcePackageRoot>repository.module</sourcePackageRoot>
            </customization>
         </customizations>
      </module>
   </modules>
</extension>‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Now create a file called 'preferences.get.js' and place it in the JAR at the package 'webscripts.repository.module'. The file should contain the following code:

// Check that user details have been provided and that the user exists...
var userid = url.templateArgs.userid;
var person = people.getPerson(userid);
if (person != null){
   // Get all the sites that the use belongs to and find the ones that they are a manager for...
   var managedSites = [];
   var allSites = siteService.listUserSites(userid, 100);
   for (var i = 0; i < allSites.length; i++)
   {
      if (allSites[i].getMembersRole(userid) == 'SiteManager')
      {
         // Construct a new object with the site details we want...
         managedSites.push({
            shortName: allSites[i].getShortName(),
            title: allSites[i].getTitle()
         });
      }
   }     

   // Convert the original JSON data string back to an object...
   var preferences = jsonUtils.toObject(model.preferences);
   preferences.managedSites = managedSites;
   model.preferences = jsonUtils.toJSONString(preferences);
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

In the default 'preferences.get.js' file (the one that we're extending) the preferences data is stored in the model as a String representation of a JSON object. Our JavaScript file is run after the default one has finished processing and whilst it doesn't have access to any of the local variables used it does have access to the same model. We therefore need to convert the preferences object (stored at 'model.preferences') back into a JSON object so that we can add our additional data to it and then convert it back to a String.

PLEASE NOTE: In this particular example we are lucky because the associated FreeMarker template ('preferences.get.json.ftl') simply outputs the JSON String.  Ideally all JSON responses would be handled this way but the original WebScript authors could never have known that one day their code would be extended in this way and so some of them construct a JSON response directly. To work around this problem it will be necessary to update the original WebScript to include a <@markup> directive and then extend the template to reference the additional data.

Build your JAR file and copy it into the 'webapps/alfresco/WEB-INF/lib' directory and when you restart the server and view a users dashboard you can check the response in FireBug or other Web Developer Tools (see screenshot below):

Updating Share

To complete the customization we need to make some updates to the Share WebScripts as well - but this isn't a tutorial on customizing Share and there should be enough information in the other blog posts to help you do this. However, I did note when looking at this example that it's not as easy as it could be (a copy and paste to the Share 'web-extension' path would still be required) - fortunately the changes we're in the process of making to the Share WebScripts should mean that this won't be the case in the future!!



11 Comments
Active Member
Hey Dave,



where can I find the class 'ExtensibilityContainer' ?? ..I get following exception when the context file is loaded:



org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [org.alfresco.repo.web.scripts.ExtensibilityContainer] for bean with name 'webscripts.c

ntainer' defined in class path resource [alfresco/web-scripts-application-context.xml]; nested exception is java.lang.ClassNotFoundException: org.alfresco.repo.web.scrip

s.ExtensibilityContainer





In which JAR File is the class?
Intermediate II
@Amin ... You should find it in the alfresco-remote-api-4.2.0.jar (assuming you're on 4.2 Community... if you're not, or are not using a nightly or personal build then it's likely it won't be available)
Active Member
Hi Dave.. thanks very much for your fast answer.  But how is it possible that you wrote within this     'ALF-13460 Share extensibility: Custom Filters' that it is possible, although it is from '18-May-12' ? There was no 4.2 available.



Best regards

Amin
Intermediate II
@Amin 4.2 hadn't been released, but that didn't mean that we weren't working on it. Alfresco is open source, as soon as we make a commit that code is available for anyone to build themselves. Also, the issue you quoted was targeted at a future release - but because the code had been committed it would be available in any future release. We don't keep issues open until we make another release.
Active Member
Hi Dave,

all right, thanks for your response! God Bless You, Amin
Active Member
Excuse me when I still have a question after a half year: Is this repository extension feauture available for alfresco enterprise 4.1.5 ? Or from 4.2 Enterprise on, because you said it is for community 4.2.



I have executed exactly the steps you have written inside of alfresco 4.1.5 (but had to copy the 'ExtensibilityContainer' class into the 4.1.5 alfresco web application because it does not support this feauture).
Intermediate II
@Amin It's in the 4.2 Community nightly builds and will be in 4.2 Enterprise but it is not available in 4.1.x
Active Member
Thx a lot for your answer!
Active Member
Hi Dave,



I tried to initialize the 'webscript.container' bean as mentioned above, but I when I do this, Share cannot connect to my repository. There is no exception or error message in log files.

We use version 4.4.2. Is the way to setup webscript extensibility still the same in that version?
Intermediate II
@Juergen - I'm sorry to hear that. To be honest, I've not checked this against recent service pack releases - it's possible that something might have changed to prevent exactly what I've described above from working.  Is there a specific need you have to try to extend a Repository WebScript?
Active Member
I'm want to extend the doclist webscript (documentlibrary-v2) with additional information. However, the original script is very small, so I gave up finding out what went wrong and used the old approach. Maybe I did something wrong on my side.