Serverside JavaScript#
To enable more complex scripting the Rhino serverside JavaScript engine (see Rhino (Wikipedia)) has been integrated into Structr.
The template expression to inform the parser that we want to use JavaScript is ${{}}
(double curly brackets).
Note: In places where it is not necessary to surround StructrScript code with ${}
(e.g. Hide and show conditions, Function Queries, Custom Schema Methods) because it is automatically inserted we surround our code with {}
to be able to use JavaScript.
Functions
All built-in functions are made available via the global Structr
object. They can be accessed by their regular name or by using the CamelCased name.
Example:
${{
Structr.send_plaintext_mail( fromAddress, fromName, toAddress, toName, subject, content );
/* is equivalent to */
Structr.sendPlaintextMail( fromAddress, fromName, toAddress, toName, subject, content );
}}
Note: Not all of the built-in functions are necessary in JavaScript. For example the arithmetic functions add
, sub
and the like are available but should not be used as there are easier ways to do that in JavaScript.
Keywords
The basic StructrScript Keywords are also available in JavaScript - the way they are accessed is different though. We need to use the get(keyword)
function on the global Structr
object. The only exceptions are the me
and this
keywords which are accessible via the shortcut Structr.me
and Structr.this
.
${{
var obj = Structr.this;
var me = Structr.me;
var current = Structr.get("current");
var request = Structr.get("request");
}}
Parameters for custom methods
Custom schema methods can accept parameters. There are two ways of accessing these parameters. (Note that we are omitting the opening ${
and closing }
)
-
If we are only interested in a single parameter we can use the following code to retrieve it.
{ var singleParam = Structr.retrieve('parameterName'); }
-
If we want to retrieve all the parameters there is a special
Structr.vars
object which contains all parameters as a map indexed by the parameter name.Example:
Calling a method with the parameters “param1” = “Test” and “param2” = 12 would result in the following:
{ var singleParam = Structr.vars; /* singleParam now contains the object { param1: "Test", param2: 12 } */ }
Batching
Starting from version 2.1.4, the serverside JavaScript engine supports batching, similar to the StructrScript batch() function.
The first parameter of the batch()
function must be a function that performs the actual operation that should run in batched mode. The function must return the literal value true
to continue the batch operation, or return any other value (false, null, etc.) to stop batch processing. For reading/deleting graph operations the batch()
function is most useful in conjunction with the slice()
function which is why the first two examples make heavy use of slice(). Calling slice() on a collection returned from find() injects a “LIMIT/SKIP” into the query that fetches the results from the database.
The batch()
function in serverside JavaScript can be used like this:
${{
/************************************************************************************************
* Easy example: Because we delete the returned nodes, we can always slice from 0 to batchSize *
************************************************************************************************/
var batchSize = 1000;
Structr.batch(function() {
// Query only the first batchSize elements from the database
var items = Structr.slice(function () {
return Structr.find('Item');
}, 0, batchSize);
if (items.length > 0) {
Structr.delete(items);
return true; // run again
} else {
return false; // do not run again
}
});
}}
Alternative example
${{
/************************************************************************************************
* More complex example: We need to keep track of which nodes we have already seen. *
* That is why we must slice from 'start' to 'end' and keep incrementing both by the batchSize *
************************************************************************************************/
var start = 0;
var batchSize = 10;
var end = batchSize;
Structr.batch(function() {
// Load only the nodes from start to end index
var src = Structr.slice(function () {
return Structr.find('File');
}, start, end);
if (src.length > 0) {
src.forEach(function(e) {
// do stuff
Structr.log(e.id);
});
start = end;
end += batchSize;
return true;
} else {
return false;
}
});
}}
Another example without slice()
${{
/**********************************************************
* Example without slice(): We create objects in batches. *
**********************************************************/
var desiredObjects = 1000;
var batchSize = 100;
var objectsCreated = 0;
var batch = 0;
Structr.batch(function() {
batch++;
Structr.log('Creating objects ' + (batch-1) * batchSize + ' to ' + batch * batchSize + '.');
var cnt = 0;
while (cnt < batchSize) {
Structr.create('Device', {name: 'Object ' + objectsCreated});
cnt++;
objectsCreated++;
}
return (objectsCreated < desiredObjects);
});
Structr.log('Created ' + objectsCreated + ' objects.');
}}
Caution
The Rhino engine is pretty unforgiving when it comes to null values and the node rendering exits when such an error occurs. That is why there are many null-checks throughout serverside JavaScript code.