Reference guide

WARNING This document is work in progress. Please report any mistakes or omissions to joost@open-t.nl

What is it?

The GroovyRunner allows you to run arbitrary Groovy scripts with full acces to the Alfresco Java API in the Alfresco repository. It is a great tool for testing, analysis, maintenance, setup and repair. We have been using it for years and it has become our Alfresco repository "swiss army knife".

Simply put alfresco-groovy-runner.jar in tomcat/webapps/alfresco/WEB-INF/lib or tomcat/shared/lib and restart Alfresco.

Since GroovyRunner is a very powerful tool and as such also can be dangerous, access is restricted to admins by default.  To change this, add the following to alfresco-global.properties:

groovyrunner.requireAdmin=false

Now, all users have access. Their transactions are still run as the user they are logged in as. So protected API services are restricted by what that user has access to.

 

Running a script

You can run a script from a webpage by opening http://[hostname]:[port]/alfresco/service/open_t/groovyrunner
you can also run a GroovyRunner script from the commandline using curl, or by using runscript.sh.

Either way, enter a simple script:
Date d = new Date()
println d
println d.getClass().getName()
This will result in the following output:
Tue Jul 14 15:10:14 CEST 2015
java.util.Date
That's just standard Groovy. if you haven't used Groovy before, see http://www.groovy-lang.org/

Now for the Alfresco API.

Let's get hold of a nodeRef:
NodeRef nodeRef=findNode("app:dictionary/app:email_templates/cm:invite/cm:invite-email.html.ftl")
println nodeRef
println nodeRef.getClass().getName()
Results in:
workspace://SpacesStore/c6a9d6ef-4d7b-46f5-9318-fcdd22b6dceb
org.alfresco.service.cmr.repository.NodeRef

Scripts have access to ALL Spring beans, this includes all API services, for example the nodeService:
 
println NodeService.getProperties(nodeRef)

Some caution is needed: nodeService is the unprotected service, NodeService the protected one.

The NodeRef has been extended using a Groovy MetaClass so it has extra virtual properties.
For example, all properties from the cm: namespace map to their short names:
 
println nodeRef.name
println nodeRef.title

Properties from other namespaces are mapped to prefix_name (as a colon is not allowed in a Groovy property name):
println nodeRef.sys_locale

Of course the setters are implemented too so you can write:

nodeRef.title="My title"


A few "special" properties have been implemented:

type
sets/gets the type in prefix:name format
aspects
gets the aspects list in prefix:name format
list
gets a list of child nodes of this node
text
sets/gets the text content of this node
content
sets/gets the binary content of this node
permissions
gets the permissions of this node
path
gets the human-readable path to this node as a String
assocPath
gets the association path to this node as a String
mimetype
gets the mimetype of this node as a String
parent
gets the primary parent of this node as a String





 

 

Aspect methods


Add an aspect:
nodeRef.addAspect("cm:effectivity")

Check if node has an aspect:
nodeRef.hasAspect("cm:effectivity")

Remove an aspect:
nodeRef.removeAspect("cm:effectivity")

 

Node creation/population methods

create a node as a child of another node:
nodeRef.createNode(name,type,properties,aspects)
Parameters:
name
The name of the new node
type
the type in prefix:name format
aspects
list of aspects in prefix:name format
properties
A map of property name/value pairs, with each name in prefix:name format

 

You can populate the content of a node with text simply by assigning to the 'text' property:

nodeRef.text="The quick brown fox ..."

If you want to specifiy a mimetype you'll need to use the putText method:

nodeRef.putText("1,2,3","text/csv")


You can upload content from a file. The file needs to be present on the server and readable by the user that runs Alfresco.

nodeRef.upload(path)

or

nodeRef.upload(path,mimeType)







 

Find a node based on it's path:
NodeRef nodeRef=findNode("app:dictionary/app:email_templates/cm:invite/cm:invite-email.html.ftl")
This will also work for a human-readable path, although care must be taken there as some folders are named differently per locale (Guest Home etc.)

Get or create a path:
nodeRef=createPath("app:dictionary/cm:test")
If the path already exists, this will simply return the node that the path points to. If it doesn't exist, all missing path elements will be created and the last node of the path will be returned.
 
We can wrap a closure in a transaction by using withTransaction:
// Let's find Guest home
guestHome=findNode("app:guest_home")

def testNode
withTransaction {
    testNode=guestHome.createNode("testNode")
    testNode.addAspect("cm:versionable")
    testNode.title="My test node"
}

If you need an afterCommit transaction listener, there is a helper for that as well:
 
withTransaction {
    ... transactional code goes here
    afterTransaction {
        ... listener code goes here
    }
}

You can run as another user:
 

runAs userName , {
    ... code goes here
}



 

An http helper object is available in the GroovyRunner script.
It allows easy acces to http services, which helps for instance in testing webscripts.
It uses the apache httpclient to do this.
For example, to read the site presets:
http.addCredentials(host:'localhost',port:8080,username:'admin',password:'admin')
def s = http.get(url:'http://localhost:8080/alfresco/service/api/sites/swsdp')
println s

You can use get, post, put and delete methods.
Each take a map as parameter, with the following keys:

text
set the body of post and put requests
mimeType
set the mimeType of the body of post and put requests.
url
the URL to use
headers
a map of key/value pairs to add as http headers. Default: ["Content-Type": "application/json","Accept":"application/json"]





 

 

The test helper helps to organize tests. It counts and names the individual tests, and stops processing the script if a test fails by default.
To change this, set test.stopOnError=false

Each test is wrapped in it's own transaction by default. To change this, set test.transactional=false

A script with tests looks like this:

test.run "Test name" , {
... test code goes here

}

test.run "Another test name", {
   ... test code goes here
}
You can create a person:
person=createPerson(["cm:userName":userName,email:"test@open-t.nl"])
or
person=createPerson(["cm:userName":userName,email:"test@open-t.nl"],"password")

Create a group:
createGroup(groupName)

Add a user to a group:
addToGroup(testGroup,userName)
Remove a user from a group:
 removeFromGroup(testGroup,userName)
Delete a group:
deleteGroup(groupName)

You can check if a node inherits permissions
nodeRef.inherit

And you can change that:
nodeRef.inheritPermissions(flag)


List of permissions:

println nodeRef.permissions


Add permission:

nodeRef.setPermission("GROUP_EVERYONE","Consumer")


delete permissions:

nodeRef.deletePermissions()



 

A content transformation can be performed by:
transform(sourceNode,targetNode,mimeType)