Server

Use of MVEL in Automation Chains

Updated: November 15, 2024

This documentation focuses on the MVEL expression language, used in automation chains. For a broader look on that subject, have a look at the Understand Expression and Scripting Languages Used in Nuxeo page.

Also, it is very unlikely that you will need to use MVEL from inside an Automation Script (using an @{expression} value). Still, form Automation Scripting, you have access to very useful objects and helpers (Document , `CurrentUser,Fn`, etc., see below)

How to Use Scripting Features in Parameters Values

When using automation, values you put in operation parameters are provided with scripting capability (when using JavaScript Automation, parameters are a JavaScript expression, string, number, function returning a value, etc.). Basically, when the system evaluates the value, it detects whether it is a raw value or an expression to be evaluated/resolved. The syntax used to tell the system you are passing an expression to be evaluated is @{the_expression}.

So, for example, say we want to set the value parameter of the Document.SetProperty operation to set the dc:title field of a document:

  • If we pass The New Title, there is no interpretation (default behavior), the parameter is taken "as is", which means that the title will be "The New Title".

  • If we pass @{Title}, the system interprets Title (inside the brackets) as a scripting expression. In that case, it means the value of the title will be updated with the content of the variable Title (that will have been set previously)

  • The expression between brackets can be concatenated with a non-interpreted value. The New @{Title} will set the title to "The New " plus the content of the variable Title.

Notice it is also possible to use the expr: prefix: expr:Title is the same as @{Title} and expr:The New @{Title} is the same as The New @{Title}.

The expression between brackets (@{the_expression}) is evaluated/calculated and can be contextual objects or embedded helper functions:

  • You can put your own Context Variables (Using Context.SetVar previously in your chain)
  • Nuxeo provides objects for your convenience. We will discuss these objects below. For example, the Document, CurrentUser, CurrentDate objects provide accessors to facilitate getting values from the current context. @{Document.title}returns the title of the current document. @{CurrentUser.mail} the mail address of the current user (typically used with the SendMail operation). @{CurrentDate.days(7).calendar} returns a date "in 7 days" (for example, stored in dc:expired).

The expression can return any value, it is not limited to strings. For example, if we have an order:total_price floating point field, we can set its value (still using the Document.SetProperty operation) with @{Document["order:quantity"] * Document["order:price"].

MVEL

The scripting language used is MVEL. See the MVEL language guide. You can use all the features of the scripting language itself.

  • For instance, you can use the substring method, when dealing with paths: @{Document.path.substring(26)}.

  • This is especially useful if you manipulate strings inside your expression: No need to write a Java plugin for this, you can just use toLowerCase() or toUpperCase() for example. The following converts the order:label string field to all upper case: @{Document["order:label"].toUpperCase()} (This expression would be used as the value parameter of the Document.SetProperty operation.)

  • It is easy to format a string with padding zeros: Use String.format(). Notice the value to pass must be a number: @{String.format("%06d", 1234) => "001234".

  • Now, the following example looks more complex: It combines several objects and accessors and returns a unique Claim ID starting with "CLM-", using a sequence number (see below the Fn object). We need to convert the string returned by Fn.getNextId to a number:

    CLM-@{String.format("%06d", Integer.parseInt(Fn.getNextId("claim")))}
    

    => "CLM-000354", "CLM-000355", etc.

  • Testing if a variable is null:

    @{WorkflowVariables["mail"] == empty ? "VoidChain" : "MyChain"}
    
  • Usage of empty variable allows for evaluating the expression to empty string. For instance, to set a property of a document to "" (empty string), you can define the value with empty

    @{empty}
    

    (you also can use @{""}).

  • As MVEL will consider values to be string by default, you can set hard-coded values depending on the type of the field. To set an integer value to ten, write @{10}. To reset a field to null: @{null}. To set a string field to "" you can use @{""}

Scripting Context

The Expression Editor in Nuxeo Studio displays the Browse Context drop down and its related features, which contain all the features explained in this documentation.

  • Document: The Document object represents the input document, when the operation takes a document as input. You can use it to get a property value of it: @{Document.path}, @{Document["yourschema:yourfield"]}, @{Document.domain.title} You can also use methods provided by the DocumentWrapper, see below.

  • variable_name: If you set a context variable in a previous operation, you can access it in the parameter value by just referring to its name. In the following sample, we previously used Context.SetVar to store the path of a document in the path_of_the_workspace variable. The parameter's value will be this path: @{path_of_the_workspace} (or expr:path_of_the_workspace)

    Do not use "-" character in the variable name. Prefer the use of _.

  • Relative paths: Each time you need to use a path expression (whether it is as a direct parameter of an operation, such as move, or in a NXQL query, for STARTSWITH operator, you can leverage relative path capability:

    • "." will be replaced with the path of the input document;
    • ".." will be replaced with the path of the parent of the input document.
  • CurrentDate: The CurrentDate object provides various utility methods to handle dates (current date, date in one month, format a date, etc.), see "Date Wrapper" below.

  • CurrentUser: The CurrentUser object provides various utility methods returning information about the current user (login, first/last name, mail, etc.), see "User Wrapper" below.

  • Event: In the context of an Automation Chain called from an Event Handler, the Event object can be used to access some of the event's properties. For instance, @{Event.getName()} returns the name of event ("aboutToCreated", "beforeDocumentModification", etc.)

  • Fn: The Fn object provides several utilities to access miscellaneous information (see below): Get the label of a vocabulary given its ID, get all the mails of a group of users, get a sequence number, etc.

  • Other objects are available, less often used:

    • Env allows for getting the value of a configuration parameter (see below)
    • WorkflowVariables and NodeVariables allow for retrieving the values of your variables in the context of a workflow
    • Context is maintained for compatibility reason. All its objects are available in other objects, or can be accessed an easier way. For example, to get the value of a Context variable, you can write @{Context["the_variable"]}, but @{the_variable} looks more readable. The same would go for

Document Wrapper

The Document wrapper, used by the system for any document put in scripting context (whether under a variable name, or as the input document ("Document") provides several utility methods:

  • Document.parent: Returns a document wrapper of the parent of the document;

  • Document.workspace: Returns a document wrapper of the parent workspace of the document;

  • Document.domain: Returns a document wrapper of the parent domain of the document;

  • Document.path: Returns a string representing the value of the path of the document, like "/default-domain/workspaces/my-workspace";

  • Document.title: Returns the title of the document;

  • Document.description: Returns the description of the document;

  • Document.type: Returns the Nuxeo EP Document type of the document (like "File", "Folder", ...);

  • Document.lifeCycle: Returns the current lifecycle state of the document;

  • Document.name: Returns the name of the document (last part of the path);

  • Document.versionLabel: Returns the version name of the document (like "1.1"...).

currentDocument is an alias for Document.

We only listed here the main accessors. Document is a DocumentWrapper and you can also check the code to see all the available accessors.
For example, you can also use hasFacet(String facet), hasSchema(String schemaName), etc. Make sure to select the branch corresponding to the version of Nuxeo you are using.

Date Wrapper

The Date wrapper is useful to update documents' date properties and to build time-relative NXQL queries.

  • CurrentDate.date: returns the date. It is the method to use to update a document date field, like dc:valid, or whatever custom date field.

Some other methods are provided to display the current date as a string:

  • CurrentDate.format("_java formatting expression_ "): returns the current date in the specified format;

  • CurrentDate.time: returns the date in milliseconds;

  • CurrentDate.day: returns the day of the current time;

  • CurrentDate.month: returns the month of the current time;

  • CurrentDate.year: returns the year of the current time;

  • CurrentDate.hour: returns the hour of the current time;

  • CurrentDate.minute: returns the minutes of the current time;

  • CurrentDate.second: returns the seconds of the current time;

  • CurrentDate.week: returns the week of the current time.

Some others can be used when building an NXQL query to express dates relatively to the current date:

  • CurrentDate.days(-3): returns the current date minus three days;

  • CurrentDate.years(10): returns the current date plus ten years;

  • CurrentDate.months(-5).weeks(2).seconds(23): returns the current date minus five months plus two weeks and 23 seconds;

  • ...

  • If you want to work on a date stored in a field of your document, you first need to get a DateWrapper object, by using: @{Fn.calendar(Document["dc:created"])}
    For example: @{Fn.calendar(Document["dc:created"]).format("yyyy-MM-dd")}

  • To set a date property based on the current date, use the CurrentDate object, and ends the expression with the calendar or the date wrapper.
    For example, to set up a field to:

    • today: @{CurrentDate.days(0).calendar}
    • in 7 days: @{CurrentDate.days(7).date}
  • To create a date that you can set on a date property from a string, you can use @{new java.text.SimpleDateFormat("yyyy-MM-dd").parse(date_str)} where date_str is a Context variable containing a value such as "2019-09-26". You must pass to SimpleDateFormat the format of this date, so the parse() method work.

User Wrapper

The CurrentUser object wraps useful functions to get information about the current user:

  • CurrentUser.name: Returns the user ID (used for logging in)

  • User information: CurrentUser.email, CurrentUser.firstName, CurrentUser.lastName, CurrentUser.company.

  • User information - advanced: There also are accessors to get more advanced information about the current user.

    • CurrentUser["xpath"] returns the value of a field in the user schema
    • CurrentUser.allGroups returns an array with all the groups the user belongs to. It is a Java List<String>. To test if the current user is a member of the "marketing" group, you can use CurrentUser.allGroups.contains("marketing")
  • CurrentUser.principal returns the NuxeoPrincipal Java object, allowing for getting more tuned/detailed information (see the Source Code on GitHub or the Java Doc). For example: CurrentUser.principal.isMemberOf("marketing"), CurrentUser.principal.isAdministrator(), CurrentUser.principal.isAnonymous(), CurrentUser.principal.isTransient(), etc.

  • CurrentUser.actingUser: In a workflow context, all the automation operations executed by the workflow engine are executed using a temporary unrestricted session (if the current user is not an administrator, this is a session with the user "system"). This variable allows you to fetch the current user. This can also be useful when the operation "Users and groups > Login as" has been used to retrieve the current username.

    currentUser is an alias for CurrentUser.

Functions

The Functions object is providing a set of useful functions. This object is named Fn and it provides the following functions (the full list can also be checked in the code, make sure to select the branch of your version):

  • Fn.getNextId(String key): Returns a unique value for the given key using the default sequencer. Each time this function is called using the same key a different string will be returned.

  • Fn.getNextId(String key, String sequencerName): Same as Fn.getNextId(String key) but allows for using a specific sequencer.

  • Fn.getVocabularyLabel(String vocabularyName, String key): Returns a value from the named vocabulary that is associated with the given key.

  • Fn.getPrincipal(String userName): Returns a Java NuxeoPrincipal object for the given username string.

  • Fn.getPrincipalsFromGroup(String group): Returns a Java Set of NuxeoPrincipal for the given group. Also returns the subgroups.

  • Fn.getPrincipalsFromGroup(String group, boolean ignoreGroups): Returns a Java Set of NuxeoPrincipal for the given group. Only the users are returned, not subgroups.

  • Fn.getEmail(String userName): Returns the e-mail of the given username.

  • Fn.getEmails(List<String> userNames): Returns a list of e-mails for the given user name list.

  • Fn.getEmails(List<String> userNames, boolean usePrefix): Returns a list of e-mails for the given user name list. If userPrefix is true, each item is prefixed with user:.

  • Fn.getPrincipalEmails(List<NuxeoPrincipal> principals): Same as above but the input object is a list of Nuxeo principals.

  • Fn.getEmailsFromGroup(String groupName): Returns a list of e-mails of all the users of the group.

  • Fn.concatenateIntoList(List<T> list, Object... values): Adds the values to the list. If a value is itself an array or a collection, each of its members is added to the list. The list is returned.

  • Fn.concatenateValuesAsNewList(Object... values): Same as concatenateIntoList but using a newly-created list.

  • Fn.htmlEscape(String string): Returns an escaped version of the string suitable for HTML inclusion.

  • Fn.nxqlEscape(String string): Returns an escaped version of the string suitable for NXQL inclusion inside single quotes.

  • Fn.documentExists(CoreSession session, String idOrPath): Returns true if the document exists in the repository.

  • Fn.getDirService(): Returns the DirectoryService. Requires administration privileges.

Nuxeo Environment Properties

Nuxeo environment properties are accessible in scripts using the Env map object. All the properties defined in Nuxeo (typically in the nuxeo.conf file) are available through the Env map. This is very useful when you want to configure your operations using values that can be modified later on a running server.

For example, let's say you want to make an operation that is creating a document and initialize its description from a Nuxeo property named automation.document.description.

To do this:

  1. Fetch the property using the Env map in your operation parameter: Env["automation.document.description"].
  2. And then set the configuration parameter in the nuxeo.conf file of your Nuxeo Server: automation.document.description = My Description

Date Management Example

Notice: These examples are for usage in Automation. if you are writing an Automation Script, you also can use the JavaScript native Date object (while the CurrentDate object is still available in Automation Scripting if you need it.).

Operation Parameter Expected value Expression Example
Document > Document.SetProperty value Today, now @{CurrentDate.date}
Document > Document.SetProperty value In one month @{CurrentDate.month(1).date}
Document > Document.SetProperty value Hard-coded date 2020-07-14T00:00:00Z
Document > Document.SetProperty value Date property from the input document @{Document["achema:aDateField"]}

Adding values (days, months, ...) to a date field requires some manipulation (it is easier when using Automation Scripting with JavaScript), please read this blog on the topic.

If you have this error in server.log...

No type adapter found for input: class org.nuxeo.ecm.automation.core.scripting.DateWrapper and output class java.util.Date

... it means you are presenting a DateWrapper value type into a field that waits for a java.util.Date object.

User and Group Management Example

Operation Parameter Expected value Expression Example
Document > Document.SetProperty value Hardcoded user user:jdoe
Document > Document.SetProperty value Hardcoded group group:marketing
Document > Document.SetProperty value Current user @{CurrentUser.name}
Notification > Document.Mail from Current user mail @{CurrentUser.mail}
Notification > Document.Mail to All users of the "ClaimAdjusters" group @{Fn.getEmailsFromGroup("marketing")}

Using Auth.Login/Auth.Logout

During the flow of a chain, we must change the permissions on a document.

For example, Alice is creating a Marketing Campaign and assigns Bob as a creative. Now, Bob must be able to upload files to the campaign: We need to change Bob's permissions on the Marketing Campaign to give him ReadWrite access (assuming he only had Read permission).

Alice does not have enough rights to change the permission: Calling Document.AddPermission will fail. In our chain, we can do the following instead:

. . . previous operations . . .
Auth.LoginAs
Document.AddPermission:
  permission: ReadWrite
  acl: local
  blockInheritance: false
  notify: false
  username: user:bob
Auth.Logout
. . . next operations . . .

Numbers Management Example

Operation Parameter(s) Expected value Expression Example
Conversion > Picture.Resize maxHeight Hardcoded values 100
Conversion > Picture.Resize maxHeight Value from a field of input document @{Document["aSchema:maxHeightForImages"]}
Document > Document.SetProperty value Add two numeric fields @{Document["invoice:amount"] + Document["invoice:tax"]} (the xpath parameter of the operation would be @{Document["invoice:total"]}

Document Management Example

Operation Parameter Expected value Expression example Note
Document > Document.Copy target Hardcoded value /default-domain/workspaces Here is specified the path of the container where the document is to be copied, assuming this document exists
Document > Document.Create type Hardcoded value File Creating a File document type.
Document > Document.Create name String node-name-of-my-document This field is a technical one and used to build the URL. This is not the title. If you look at the URL after navigating to a document and you will see its path based on the names of the document's ancestor and its name. Typical example with Nuxeo out of the box: The domain's title is "Domain" while its name is "default-domain".

Referencing Automation Chain Parameters

It is possible to define parameters to your chain (in Nuxeo Studio, you can use the "Parameters" tab). When the chain is called, you access the values of the parameters with:

@{ChainParameters["parameterName"]}

Using the RunScript Operation

The Scripting > RunScript operation allows, basically, for using Java APIs (with security restriction. You cannot access a file on the server for example, etc.). When you need this, you may find it easier to use Automation Scripting (which also allows for using Java in JavaScript).

When calling the operation, you must use full qualified names. For example, when calling a service:

org.nuxeo.business.days.management.service.BusinessDaysService bdService = org.nuxeo.runtime.api.Framework.getService(org.nuxeo.business.days.management.service.BusinessDaysService);
Context["limitDate"] = bdService.getLimitDate("myRule",CurrentDate.date);

=> You now can use the context variable limitDate in other operations.

You must limit the usage of Java-in-Automation to very simple calls. If you find yourself writing several lines of Java inside a RunScript, then we strongly recommend that you write a Java plugin instead.