Obsolete Pages{{Obsolete}}
The official documentation is at: http://docs.alfresco.com
JavaScript API
IMPORTANT: This document details the JavaScript API and Script Services for Alfresco 2.0 or 1.4. For more recent versions then please see this document: JavaScript API. Note that this page will no longer be maintained or updated except to correct any mistakes.
From Alfresco 1.3, the Alfresco JavaScript API allows script writers to develop JavaScript1.5 compatible files that access, modify and create Alfresco repository Node objects. It provides a simple, clean and object-oriented API to well known Alfresco concepts such as Nodes, Properties, Associations and Aspects. The API is similar to the Alfresco Template API - with a significant difference that it allows modification and creation of nodes, aspects and properties.
Note that JavaScript API was significantly enhanced in Alfresco 1.4 - this document details the 1.4 API. Enhancements specific to Alfresco 2.0 are noted.
Using the API script writers can find nodes (via XPath), walk node hierarchies, perform searches (including Lucene full-text searches), examine and modify the value of node properties and modify the aspects applied to a node. In addition scripts can create new files and folder objects and copy/move/delete nodes. All the usual Alfresco security and ACL permissions apply.
As JavaScript is a standard language (also known as ECMA Script) there are many web resources, online tutorials and books to help developers in writing JavaScript files. It is suggested that potential script writers read up on JavaScript resources before diving in to scripting the Alfresco repository.
A good general JavaScript 1.5 reference can be found on the Mozilla site.
The integrated JavaScript engine in Alfresco utilises the Mozilla Rhino JavaScript Engine for Java. This allows JavaScript1.5 (ECMA Script) files to access Java objects using a 'bean' style notation and interpreted mode execution without compiling to external class files.
Scripts can currently be executed in two different ways.
The first is to create a Content Rule and select 'Execute a Script' from the list of available actions for the rule. The scripts shown in the list are pre-loaded into the /Company Home/Data Dictionary/Scripts space - users with appropriate permissions can add create/modify the available scripts.
The second way is to use direct URL addressing for a REST-style of execution. The Alfresco web-client features a generic 'command' servlet that allows the execution of scripts directly via URLs. This feature allows direct access to scripts and the result of the script is returned as the HTML response stream. See Command Servlet docs for more info on this servlet.
The API provides a set of scriptable 'Node' objects that wrap the Alfresco repository concepts such as nodes, aspects, properties and provide access to common services through a rich object-orientated API. If you are accessing the script engine either through a rule/action in the Web-Client or through the Script Command Processor then the following objects are available by default in the root scripting scope:
The default model objects can be accessed directly from the root of a script scope and follows the standard JavaScript pattern of property dot notation style syntax, for example:
var name = userhome.properties.name
Here the variable name is populated by accessing the properties property and the name value within it.
The node children/association/aspect etc. model is built dynamically as required. E.g. you can write statements that walk multiple node collections such as:
userhome.children[1].children[0].children[2].name
The companyhome, userhome and person default objects represent Alfresco Node objects, the JavaScript integration provides the following Node API available to use within scripts.
var name1 = userhome.properties.name
var name2 = userhome.properties['name'];
var name3 = userhome.properties['cm:name'];
var name4 = userhome.properties['{http://www.alfresco.org/model/content/1.0}name'];
One special feature is all properties of type NodeRef are automatically converted into another Node object. This means the script writer can continue to walk the object hierarchy for that node e.g. If a document node has a NodeRef property called 'locale' you could execute the following to retrieve the name of the node the property references:
var locale = document.properties.locale.properties.name;
In addition, any properties of type ContentData are converted to wrapper objects that feature an API to retrieve and set the content, mimetype, size and url on the content property. For example:
var mimetype = document.properties.content.mimetype;
Properties can be modified and new properties added, see the Modifying and Creating API section below.
The following Node API functions are provided to help locate child nodes using an XPath or Name based path:
var testingFolder = userhome.childByNamePath('QA/Performance/Testing');.
var nodes = userhome.childrenByXPath('*[@cm:name='Finance Documents']/*');.
Most of the available Node API returns read-only values, however the real power of the scripting API can be found in the support for writable Node objects and access to Alfresco services through this API. The Node object allows modification of existing properties, creating new file and folder nodes and of course updating and setting the text content for a node. It addition it is possible to perform powerful features such as deleting nodes and adding a new aspect to a node. In the future even more services will be made available through the scripting API.
// change the name of this document
document.properties.name = 'Backup of ' + document.properties.name;
// add a new property string
document.properties['cm:locale'] = mylocalenode;
// save the property modifications
document.save();
It should be noted that the node.save()
API call is needed to persist the property modifications - all other modifications made using the API (e.g. content or adding aspects) take immediate effect.
It is important to note that JavaScript objects are different to native Java objects. Property values in the repository must be of the correct object type as defined in the Data Dictionary and exposed by the content model. So a String property value expects a Java String and a multi-valued property expects a List. The Alfresco JavaScript API will perform the conversion of most object types between JavaScript and Java and visa-versa for you, e.g. Array (for a multi-value property), numbers, boolean and strings. But some properties may need to be explicity of the correct type. Note that in Alfresco 1.3 Date properties and some multi-value properties are not handled correctly, see JIRA bug AR-777 for work-around - this was fixed in Alfresco 1.4.
var props = new Array(1);
props['cm:template'] = myTemplateNode.nodeRef;
document.addAspect('cm:templatable', props);
org.alfresco.service.cmr.security.PermissionService
. Most commonly used permission checks are 'Read', 'Write', 'Delete' and 'AddChildren'.The Search API provides direct access to repository level Lucene search results and Saved Search results. It is accessable via the 'search' root scope object. Note that local searches can be performed using the Node APIs childByNamePath and childByXPath as detailed above.
Like the various node objects, the search object is part of the root scope available to script writers. The API provides the following functions:
Note: This API is only available in Alfresco 1.4 Enterprise (for now).
The People API provides access to Alfresco people and groups. The API provides the following functions:
Note: This part of the API is from Alfresco 2.0.
The checkin/checkout Node API features methods for checkout, checkin and cancelling checkout of working copies. Note that you may wish to first add the cm:versionable aspect to a Node if you wish version history to be recorded when using these methods.
The following Node functions make use of the document transformation services available in Alfresco. The OpenOffice server is required for some document transformations.
The following functions make use of the image transformation services available in Alfresco. The ImageMagick components will need to be installed correctly and working. Note that detailed information on the ImageMagick options mentioned in the methods below can be found on the ImageMagick website.
The following functions make use of the FreeMarker template processing services available in Alfresco. The result of the template execution is returned as a string from each function. Note that the node is used as the context to the template. If this node is a document, it will be setup as the 'document' context object for the template, with the parent space setup as the 'space' context object for the template, if it is a folder then it will be setup only as the 'space' context object for the template. An argument list can also be passed to the template - and will be available as the 'args' object, see Template Default Model page for more information on template models and the 'args' object.
Details of the API to workflow can be found in the Workflow documentation.
A root level object 'actions' is provided that allows invocation of Alfresco Actions registered with the Repository.
A ScriptAction represents an Alfresco Action registered within the Repository.
// create mail action
var mail = actions.create('mail');
mail.parameters.to = 'davidc@alfresco.com';
mail.parameters.subject = 'Hello from JavaScript';
mail.parameters.from = 'davidc@alfresco.com';
mail.parameters.template = root.childByNamePath('Company Home/Data Dictionary/Email Templates/notify_user_email.ftl');
mail.parameters.text = 'some text, in case template is not found';
// execute action against a document
mail.execute(document);
A root level object 'logger' is provided that provides functions to aid debugging of scripts.
log4j.logger.org.alfresco.repo.jscript
should be set to DEBUG
. This can be applied in the log4j.properties file (TomCat) or log4j.xml file (JBoss) on the Alfresco server.A root level object 'session' is provided to access to the current logged in user session ticket as a string value.
There are two parts to the API: manipulating classifications and manipultaing the categories that they contain. A root level object 'classification' is provided to return category nodes. The nodes returned from the functions are extended from the standard JavaScript Node model to include category manipulation.
The CategoryNode object API.
A root level object 'utils' is provided as a library of helper functions that are missing from generic JavaScript.
Example scripts can be found in the Company Home/Data Dictionary/Scripts folder in a default Alfresco 1.3 installation.
Creates a backup of a document as it is added to a space:
// find the backup folder - create if not already exists
var backupFolder = space.childByNamePath('Backup');
if (backupFolder == null && space.hasPermission('CreateChildren'))
{
// create the folder for the first time
backupFolder = space.createFolder('Backup');
}
if (backupFolder != null && backupFolder.hasPermission('CreateChildren'))
{
// copy the doc into the backup folder
var copy = document.copy(backupFolder);
if (copy != null)
{
// change the name so we know it's a backup
copy.name = 'Backup of ' + copy.name;
copy.save();
}
}
Creates a backup of a document and logs the doc properties to a log text file:
// find the backup folder - create if not already exists
var backupFolder = space.childByNamePath('Backup');
if (backupFolder == null && space.hasPermission('CreateChildren'))
{
// create the folder for the first time
backupFolder = space.createFolder('Backup');
}
if (backupFolder != null && backupFolder.hasPermission('CreateChildren'))
{
// copy the doc into the backup folder
var copy = document.copy(backupFolder);
if (copy != null)
{
// change the name so we know it's a backup
var backupName = 'Backup of ' + copy.name;
copy.name = backupName;
copy.save();
}
// record the time of the backup to a log file
var logFile = backupFolder.childByNamePath('backuplog.txt');
if (logFile == null)
{
logFile = backupFolder.createFile('backuplog.txt');
}
if (logFile != null)
{
logFile.content += 'File: ' + backupName +
'\tDate: ' + new Date().toGMTString() +
'\tSize: ' + copy.size + '\r\n';
}
}
Appends a copyright line of content to plain text and HTML files:
if (document.hasPermission('Write'))
{
if (document.mimetype == 'text/plain')
{
document.content += '\r\n\r\nCopyright (C) 2006';
}
else if (document.mimetype == 'text/html')
{
document.content += '
Copyright © 2006';
}
}
Adding several aspects to a document:
var props = new Array(1);
props['cm:template'] = document.nodeRef;
document.addAspect('cm:templatable', props);
props = new Array(1);
props['cm:lockIsDeep'] = true;
document.addAspect('cm:lockable', props);
props = new Array(1);
props['cm:hits'] = 1;
document.addAspect('cm:countable', props);
Finds all the documents containing the text 'Alfresco' using a Lucene search and records the results to a log file:
// log the docs that currently contain the word 'Alfresco' to a log file
var logFile = userhome.childByNamePath('alf docs.txt');
if (logFile == null)
{
logFile = userhome.createFile('alf docs.txt');
}
if (logFile != null)
{
// execute a lucene search across the repo for the text 'alfresco'
var docs = search.luceneSearch('TEXT:alfresco');
var log = '';
for (var i=0; i
Returning a result value. This is useful for scripts that are processed using URLs via the Script Command Servlet, as the results are returned as the HTML response from the servlet:
function result()
{
return 'The name of my home space is: ' + userhome.name;
}
result();
The following solution will also return a value if placed at the end of a script:
// script here
// ...
var result = 'some results...';
result;
Creates a document, makes it versionable, checks it out, modifies the content of the working copy, checks it in again and then repeats the process but checks in the document with a version history note and as a major version increment:
// create file, make it versionable
var doc = userhome.createFile('checkmeout.txt');
doc.addAspect('cm:versionable');
doc.content = 'original text';
// check it out and update content on the working copy
var workingCopy = doc.checkout();
workingCopy.content = 'updated text 1';
// check it in
doc = workingCopy.checkin();
// check it out again
workingCopy = doc.checkout();
workingCopy.content = 'updated text 2';
// check it in again, but with a version history note and as major version increment
doc = workingCopy.checkin('a history note', true);
Changes the mimetype of a document after setting the content:
var file = userhome.createFile('testfile.html');
file.content = 'some HTML here';
file.mimetype = 'text/html';
Creates document content and converts it to new formats using the transformation API:
// create a plain text doc and convert to PDF
var doc1 = userhome.createFile('transform_me1.txt');
doc1.mimetype = 'text/plain';
doc1.content = 'This is plain text';
var trans1 = doc1.transformDocument('application/pdf');
// create an HTML doc and convert to plain text
var doc2 = userhome.createFile('transform_me2.html');
doc2.mimetype = 'text/html';
doc2.content = 'This is an HTML document!';
var trans2 = doc2.transformDocument('text/plain', companyhome);
Converts an image document to other formats:
// convert an image document to GIF format and place it in company home
var gifImage = document.transformImage('image/gif', companyhome);
// convert an image to JPG format and resize to thumbnail image
var thumbImage = document.transformImage('image/jpeg', '-resize 120');
Executes a template from the repository against the current Document node:
var template = companyhome.childByNamePath('/Data Dictionary/Presentation Templates/doc_info.ftl');
if (template != null)
{
var result = document.processTemplate(template);
// output result to the console - could just as easily save the content into a new node...
logger.log(result);
}
Builds a FreeMarker template directly in the script, also builds up an argument list for the template. The result of the template is saved to a new node as the content:
var template = 'Document name is ${document.name}
' +
'The ID argument: ${args['id']}';
var args = new Array()
args['id'] = '01234-56789';
var result = document.processTemplate(template, args);
// save the template result content to a new node in my home space
var outputFile = userhome.createFile('output.txt');
outputFile.content = result;
The Script Service is a typical Alfresco repository service accessed via a Spring managed bean with the name of ScriptService.
Only repository developers will be interested in accessing the ScriptService directly, those more interested in simply writing scripts themselves should skip this section and jump to Scripting API.
The ScriptService features the following interface:
/**
* Script Service.
*
* Provides an interface to services for executing a JavaScript engine script file against a
* Java object based scripting data-model.
* <p>
* The script file can either be in the repository (passed as NodeRef string) or on the classpath.
* Also a script String can be passed directly to the service via the executeScriptString() methods.
* Java objects are passed into the scripting engine and methods can be accessed directly from the script.
* <p>
* A script is executed within a single transaction, any modifications to nodes or properties that fail
* and cause a rollback which will rollback all repoistory modifications made by the script.
*
* @author Kevin Roast
*/
public interface ScriptService
{
/**
* Process a script against the supplied data model.
*
* @param scriptClasspath Script location as qualified classpath name
* @param model Object model to process script against
*
* @return output of the script (may be null or any valid wrapped JavaScript object)
*
* @throws ScriptException
*/
public Object executeScript(String scriptClasspath, Map<String, Object> model)
throws ScriptException;
/**
* Process a script against the supplied data model.
*
* @param scriptRef Script NodeRef location
* @param contentProp QName of the property on the node that contains the content, null can
* be passed to indicate the default property of 'cm:content'
* @param model Object model to process script against
*
* @return output of the script (may be null or any valid wrapped JavaScript object)
*
* @throws ScriptException
*/
public Object executeScript(NodeRef scriptRef, QName contentProp, Map<String, Object> model)
throws ScriptException;
/**
* Process a script against the supplied data model.
*
* @param script Script content as a String.
* @param model Object model to process script against
*
* @return output of the script (may be null or any valid wrapped JavaScript object)
*
* @throws ScriptException
*/
public Object executeScriptString(String script, Map<String, Object> model)
throws ScriptException;
}
The Service API provides three similar looking methods, they each take a script (either as a Alfresco NodeRef location, Java ClassPath location or the script content itself as a String) and a data-model to make available to the script. The data-model objects are made available to the script by placing them into the global root variable scope. For example, if an String object with the map key name of 'mystring' is provided in the data-model then the JavaScript being executed can access the object simply by name:var s = mystring;
The default data-model API is described in Scripting API below.
Note: this feature is available in the 2.0 release of Alfresco
It is possible to create and add custom script API's implemented in Java and accessable in JavaScript. This provides an integration point for Alfresco extensions to provide custom API's where appropraite.
In order to implement a custom JavaScript API it is recommended that you develop a POJO (Plain Old Java Object) that extends the base class org.alfresco.repo.jscript.BaseScriptImplementation. The public methods of your class will be those that will be accessable from JavaScript. (For example see org.alfresco.repo.jscript.ScriptLogger)
Once complete, you must then configure your bean in Spring. Make use of the baseScriptImplementation parent bean in order to ensure your script is automatically registered with the JavaScript framework. The name of the script as it will appear in the JavaScript API must also be specified in the bean definition.
The following example shows the bean defintion for the ScriptLogger custom API.
<bean id='loggerScript' parent='baseScriptImplementation' class='org.alfresco.repo.jscript.ScriptLogger'>
<property name='scriptName'>
<value>logger</value>
</property>
</bean>
Since this is a standard bean defintion any additional services that are required can be injected by extending the bean defintion in the usual way.
Once this bean have been loaded the custom API will be accessable directly in JavaScript by calling the methods on the named Java Script object. For example the following shows how the log method can be called on the logger API we have defined above.
...
logger.log('This is a log message.');
...
Return to Developer Guide