Tuesday, January 23, 2018

How to Change the Alfresco Version Label Format

Document versions in Alfresco are stored as a sequential history of snapshots of the state of the document made over the life of the document.  Each document version is a snapshot of the metadata and file content at the time when the version was created.

Only documents that have the versionable aspect are versioned.  New content uploaded to a document without the versionable aspect will simply be overwritten. In Alfresco Share, by default, all documents are automatically assigned the versionable aspect, so all content will be versioned.

Default version labels in Alfresco are numeric and consist of a major component, followed by the minor component, separated by a period, like "1.0" (major version is 1 and minor version is 0), and "2.5" (major version is 2 and minor version is 5). Also, by default in Share, newly uploaded content will start at version "1.0".

This format ([major].[minor]) for writing and referring to versions is somewhat hardwired into the Alfresco Share interface, but versions don't need to be stored this way in the Alfresco repository.  It is possible to format the version label in any way.

Alfresco provides a "behaviour" called calculateVersionLabel as an extension point where developers are able to wire in a new method for determining the version label.

But note that if you plan to use Alfresco Share as your main interface with the Alfresco repository, it may not be a good idea to change the format for version labels because doing that may cause problems with some assumptions that were made in the Share UI display.  Besides just changing the version label format, there would need to be additional customizations in Share to ensure that the new format for version labels are displayed and handled correctly.

To demonstrate how to change the method for calculating version labels, we will create two new files: a Javascript file that handles the calculation of the version label, and a file to wire in the configuration of this new behaviour.

Let's look first at the Javascript for the behaviour.  Alfresco behaviour code is often written in Java, but here we will just use server-side Javascript.

In this example, we will change the version format so that it still consists of a minor and major component, but instead, will be separated by an underscore.  So, instead of "1.0", the version will be "1_0", for example.

First create the Javascript file: <Tomcat>/shared/classes/alfresco/scripts/onCalculateVersionLabel.js. It contains the following content:

 // Calculate the Version Label  
 // Have a look at the behaviour object that should have been passed  
 if (behaviour == null)   
 {  
   logger.log("The behaviour object has not been set.");  
   scriptFailed = true;  
 }  
 // Check the name of the behaviour  
 if (behaviour.name == null && behaviour.name != "calculateVersionLabel")   
 {  
   logger.log("The behaviour name has not been set correctly.");  
   scriptFailed = true;  
 }   
 else   
 {  
   logger.log("Behaviour name: " + behaviour.name);  
 }  
 // Check the arguments  
 if (behaviour.args == null)   
 {  
   logger.log("The args have not been set.");  
   scriptFailed = true;  
 }   
 else   
 {  
   if (behaviour.args.length == 4)   
   {  
           // Set initial version to 0  
     var currentVersion = (behaviour.args[1]==null)? "0_0" : behaviour.args[1].getVersionLabel();  
           var versionType = behaviour.args[3].versionType;  
     var num = currentVersion.split('_');  
           if(num.length!=2)  
           {  
             scriptFailed = true;  
           }  
           else  
           {  
                var major = parseInt(num[0]);  
                var minor = parseInt(num[1]);  
                if(versionType.toString().equals("MINOR")) currentVersion = major + "_" + (minor + 1);  
                else if(versionType.toString().equals("MAJOR")) currentVersion = (major + 1) + "_0";  
           }  
           currentVersion;  
   }   
   else   
   {  
     logger.log("The number of arguments is incorrect.");  
     scriptFailed = true;  
   }    
 }  

A behaviour object is passed into the Javascript code that contains the parameters of the behaviour.  behaviour.args[1] contains the version object, and behaviour.args[3] is an object with parameters that specify how to construct the new version.

The current version label is stored as behaviour.args[1].getVersionLabel().  The version label will be null for new documents.

The type of versioning, for example, whether to create a new minor or major version, is specified by the value of behaviour.args[3].versionType.

Note that the last line executed in the Javascript code is the return value for the behaviour.  In this case, that value represents the new version label, which is stored in the variable currentVersion.

The Javascript code can then be wired into the behaviour with the following Spring context file.  Place this file in the following location: <Tomcat>/shared/classes/alfresco/extension/version-context.xml.  The configuration calls out the location of the Javascript file to be used for the Alfresco policy calculateVersionLabel.  Two beans are defined in the file.  The first overwrites and undefines the standard method for handling the calculation of version labels, and the second bean defines the new bean for version label calculation.

 <?xml version='1.0' encoding='UTF-8'?>  
 <!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>  
 <beans>  
   <bean id="registerContentWithVersionService" class="org.alfresco.repo.version.VersionServiceVersionLabelRegistrationBean"></bean>  
   <!-- Define a custom behavior on Standard Content -->  
   <bean id="customOnVersionLabelChange" class="org.alfresco.repo.policy.registration.ClassPolicyRegistration" parent="policyRegistration" >  
     <property name="policyName">  
       <value>{http://www.alfresco.org}calculateVersionLabel</value>  
     </property>  
     <property name="className">  
       <value>{http://www.alfresco.org/model/content/1.0}content</value>  
     </property>  
     <property name="behaviour">  
       <bean class="org.alfresco.repo.jscript.ScriptBehaviour" parent="scriptBehaviour">  
         <property name="location">  
           <bean class="org.alfresco.repo.jscript.ClasspathScriptLocation">  
             <constructor-arg>  
               <value>alfresco/scripts/onCalculateVersionLabel.js</value>  
             </constructor-arg>  
           </bean>  
         </property>  
       </bean>  
     </property>  
   </bean>  
 </beans>  

Now, when we create new versions, the new format for the version label will be used. Only newly created versions will use the new method for calculating the version label.

For example, here is the banner on the details page for version 1_3 of a document:








And here is how the version history for that document is displayed on the details page:



There are some problems in Share though.  There are places in Share that assume the version label format.  For example, the following code snippet from the file dnd-upload.js is used when the upload dialog pops up with checkboxes to select the major or minor version.  There is code here that tries to parse the current version label by using the split() method with a "." delimiter to break apart the minor and major components of the label.  This code will have problems when it encounters the new version label in a format like "1_3".



For example, we'll see the following:



Note the "(1.NaN)" which appears for the minor change.  There are likely other problems that will occur within Share because of the change of the version label format too.

But despite those problems, if your application has a need for reformatting version labels, you might consider redefining the method used to calculate the labels.