You have been redirected from an outdated version of the article. Below is the content available on this topic. To view the old article click here.

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 })

  1. If we are only interested in a single parameter we can use the following code to retrieve it.

    {
    	var singleParam = Structr.retrieve('parameterName');
    }
    
  2. 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.

Search results for "Serverside JavaScript"

Relationship Details Dialog

The Cascading Delete settings allow configuration of what happens when either end of the relationship is deleted. The possible values are explained in-depth in the help popup in the dialog.
Automatic Creation of Related Nodes configures if it is allowed to include nested nodes in a REST POST request for this relationship. A node with the given property set is automatically created and linked to the node. If the nested node contains an id attribute (or another property marked as unique) a node is searched for that property and linked if found.

Permission Resolution allows configuration of rights propagation in the graph. If NONE is selected, no rights propagation is applied for this relationship. If SOURCE_TO_TARGET is selected the rights are propagated along the relationship direction to the next node. For TARGET_TO_SOURCE the rights propagation is works against the relationship direction. For ALWAYS the direction of the relationship does not matter and rights propagation is always applied.
Specific rights (Read, Write, Delete, AccessControl) can be added, kept or removed according to the propagation configuration. If a user has read rights to the previous node and Read is configured to Keep, the user also has read rights for the next node. (Specific User/Group rights are applied before using permission propagation - i.e. if a user has specific rights configured for a node, permission resolution is not evaluated as user rights are more specific).
Along the way of permission propagation, properties can be hidden in order to hide sensitive information from users who get rights from permission propagation. The property names can be separated by comma , or space character.

There are 3 tabs where the functionality of the type can be configured:

  • Local Attributes
    A Custom Type can be extended with dynamic properties to provide the data model for the intended use-case. This list contains all local properties (meaning they are defined on this type directly).
  • Views
    The properties of a type can be combined into named Views, which are accessible under an individual REST URL. Access to these URLs can be configured independently for each HTTP method using Resource Access Grants, which makes them an ideal tool to create specialised endpoints for different client applications (e.g. mobile clients etc.).
  • Methods
    There are different kinds of methods - callback methods and entity methods. Callback methods are automatically executed by the framework upon certain lifecycle events and have a strict naming convention. Entity methods are called by the user/programmer.
    Entity methods are not automatically run by the framework and must be called manually. This either means making a POST request to /structr/rest/(Type)/(<a data-id="7c9c8218bced42bab66868373e64d885" class="mention">UUID</a>)/(methodName) or in serverside JavaScript as node.methodName();

Type Details Dialog

There are 5 tabs where the functionality of the type can be configured:

  • Local Attributes
    A Custom Type can be extended with dynamic properties to provide the data model for the intended use-case. This list contains all local properties (meaning they are defined on this type directly).

  • Views
    The properties of a type can be combined into named Views, which are accessible under an individual REST URL. Access to these URLs can be configured independently for each HTTP method using Resource Access Grants, which makes them an ideal tool to create specialised endpoints for different client applications (e.g. mobile clients etc.).

  • Methods
    There are different kinds of methods - callback methods and entity methods. Callback methods are automatically executed by the framework upon certain lifecycle events and have a strict naming convention. Entity methods are called by the user/programmer.
    Entity methods are not automatically run by the framework and must be called manually. This either means making a POST request to /structr/rest/<Type>/<UUID>/<methodName> or in serverside JavaScript as <node>.<methodName>();

  • Remote Attributes
    Custom types can be linked to other types, including base types. Structr will automatically create Remote Attributes on either side of the link, which can then be used to create, update or remove the relationships between instances of the two types. In this tab the configuration of the remote attributes can be viewed and edited. The names configured here are used throughout the application to refer to the other side of the relationship(s).

  • Inherited Attributes
    The content of this tab is of informative character. All inherited attributes are shown with an information from where it was inherited.