Nuxeo Studio is an amazing configuration tool and every time I use it, I am blown away by what it can do. Today I will show one such interesting application of Studio.
Nuxeo Studio makes it very easy to add business logic using Nuxeo Automation to any Nuxeo application. For example, a validation workflow can start automatically when a new picture is created, a version can be automatically created when a lifecycle state becomes “Approved”, etc.
An Automation Chain is a set of ordered operations and, of course, it is called a chain because each operation runs one after the other - the output of one operation being the input of the next one. Nuxeo Studio exposes more than 170 operations grouped by themes and you can build your Automation Chains by just dragging and dropping the operations:
Nuxeo Studio does not expose each and every operation available. You can add more operations if needed. For example, you can add some operations handling Nuxeo Drive in the Operation Registry of your project, which will allow you to automatically synchronize a folder when a workflow starts.
Use a Single JavaScript Operation Instead of a Complex Set of Automation Chains
Even if you can do almost anything you want using Automation Chains, there are two areas where it gets a little complicated to handle a complex chain: loops and conditions. A typical example is when you want to run a loop on every document in a list, and for each document you want to update a field depending on the value of another field, or want to add some documents to a list that will be handled later.
In this context, you will have to write a complex set of chains calling sub-chains, which in turn will call more sub-chains, using the ternary operator inside a MVEL expression, maybe filling Context Variables used in different sub-chains, etc.
This is where JavaScript Automation comes to the rescue:
- It is plain JavaScript (via Nashorn) with all it’s core features:
- Includes String, Date, Array, etc.
- Makes it very easy to handle control flow (loop, if, etc.)
- Access each and every Operation directly from your JavaScript
- Access your documents and everything you already have in Automation (the quite useful
Fn
object for example) - It can be used:
- Like any Automation Chain: Called from a User Action, an Event Handler, a workflow transition, REST, etc.
- Inside an Automation Chain
Thanks to this, you can write a single JavaScript operation instead of a complex set of chains. This is also where Nuxeo Studio rocks again! - the JavaScript editor has all you need in terms of type ahead, auto completion and syntax checking.
Let’s take a concrete example. Say you want to let the user update a list of selected documents in the UI, such that the ac:internal_ref
field is filled with a sequence number depending on the value of the ac:department
field:
- The main chain gets the selected document
- A sub-chain is applied to each of them
- And a sub-sub chain is applied, depending on whether
ac:department
is empty or not. It requires two chains, one that updatesac:internal_ref
, and one that does nothing at all. - Then an InfoMessage is displayed to the user once it is done.
This is how it looks:
Here’s the detailed flow:
- Get the list of selected documents in the UI
- Store the number of documents to be handled, and initialize a numeric variable that will hold the number of documents that were updated
- Run the
UpdateSelected_One
sub chain for each document in the list UpdateSelected_One
gets the document- And it runs a sub chain, either
EmptyChain
if the department field is empty (“” ornull
), or theUpdateInternalRef
. - Here, the department was empty.
EmptyChain
just does nothing at all (which is fine. I would love to be an EmptyChain sometime) - Here, the department was not empty, so we are in
UpdateInternalRefv
, and start by getting the context document - We increment the context variable initialized at step #2
- We update the
ac:internal_ref
using the current year and a sequence number based on the value of the department. The full expression is:@{CurrentDate.format("yyyy")}-@{Fn.getNextId( Document["ac:department"] )}
- We are done and can display the information to the user.
Even if this flow is not that complicated, we still needed to build 4 chains. We would love to simplify this process and make it easier to maintain.
Now, let’s check out the JavaScript that does exactly the same. The call to operations is shown in bold, and the most interesting JavaScript part (control flow and native functions) is highlighted in bold-blue:
function run(input, params) {
var docs, nbToUpdate = 0, nbUpdated = 0;
// Get the selected docs, update the variable
docs = **Seam.GetSelectedDocuments**(input, {});
nbToUpdate = docs.length;
// Run a loop
docs.**<span style="color: #0000ff;">forEach**(function(oneDoc) {
// If the Department is not empty, update the reference
<span style="color: #0000ff;">**if**(oneDoc["ac:department"] !== null
&& oneDoc["ac:department"] !== "") {
nbUpdated += 1;
oneDoc = **Document.SetProperty**(oneDoc, {
'xpath': "ac:internal_ref",
'save': true,
'value': "" + <span style="color: #0000ff;">**new Date().getFullYear()** + "-"
+ **Fn.getNextId**(oneDoc["ac:department"])
});
}
});
// Inform the user
**WebUI.AddInfoMessage**(null, {
'message': "Updated: " + nbUpdated + "/" + nbToUpdate});
// Return the container (in case this script is called inside a chain)
return input;
}
If we don’t take the formatting into account, we have roughly 10 lines of code and it’s very easy to read, understand, and maintain. So, thanks to Nuxeo Scripting Automation and Nuxeo Studio, we could KISS and it’s great ;)