The Cyclos scripting module (available from version 4.2 onwards) provides an integration layer that allows connecting from Cyclos to third party software, as well executing custom operations and scheduled tasks within Cyclos self. The scripting module offers an easy way to customize and extend Cyclos, without losing compatibility with future Cyclos versions. The scripting engine can access the full Cyclos services layer which makes it a powerful feature. For security reasons only global administrators can add scripts. Network administrators can be given permissions to bound the scripts to elements such as extension points (eg. payment, user profile, advertisement), custom validations (for input fields), custom calculations (account fees, transaction fees), custom operations and scheduled tasks. Any internal entity in Cyclos (e.g. user, address, payment, authorization, reference etc.) can be accessed by the scripts. When developing custom operations it is likely that you want to store and use new values/entities. It is possible to create specific record types and custom fields and make them available to the scripts. The record types can be of the type 'system' or 'user' depending on the requirements.
On this page you will find links with documentation about the available extensions and examples. In the future we will add a repository of useful scripts. If you wrote a script that could serve other projects we will be happy to add it. Please post it on our Forum or send it to info@cyclos.org.
Global admins can write and store scripts directly within Cyclos. Each script ‘type’ has its own functions which have to be implemented. A network admin can chose from the available scripts and bind them to Cyclos operations and events, or to new operations. The variables used in the scripts can be managed outside the scripts in the extensions self (by the network admin). This avoids the need for a global admin having to modify a script every time a new or different input value is required. It is also possible to define additional information and confirmation texts that can be displayed to the user when a custom operation is initiated or submitted.
The scripting language currently supported is Groovy. It offers a powerful scripting language that is very similar to Java, with a close to zero learning curve for Java developers. It is possible to write scripts that will be available in a shared script library, so that other scripts within the same context can make use of it. All scripts are compiled to Java bytecode which makes them highly performatic. Currently Cyclos requires Java 7 or above. Be aware that JDK 7 versions ranging from 7u21 to 7u55 have bugs are buggy with regards to invokedynamic (see information here). If you plan to use Cyclos scripting, make sure you either use 7u56+ or JDK 8
When running, scripts have a set of bindings, that is, available top-level variables. At runtime, the bindings will vary according to the script type and context. For example, each extension point type has one or more specific bindings. On all cases, however, the following variables are bound:
scriptParameters: In the script details page, or in every every page where a script is chosen to be used (for example, in the extension point or custom operation details page) there will be a textarea where parameters may be added to the script. They allow scripts to be reused in different contexts, just with different parameters. The text is parsed as Java Properties, and the format is described here. The library parameters are included first (if any), then the own script parameters (if any), then the specific page parameters. This allows overridding parameters at more specific levels.
scriptHelper: An instance of org.cyclos.impl.system.ScriptHelper. It contains some useful methods, like:
wrap(object[, customFields]): wraps the given object in a Map, with some custom characteristics:
If the wrapped object contains custom fields, it will allow getting / setting custom field values using the internal name
Values will be automatically converted to the expected destination type
If a list of custom fields are passed, then they are considered. If not, will attempt to read the current fields for the object, which might not always be available (for example, when creating a new record) or even no longer active (for example, when the product of an user just removed a field, and the value is still there)
Example:
def bean = scriptHelper.wrap(user) def gender = bean.gender // gender will be a org.cyclos.entities.system.CustomFieldPossibleValue // if gender is an enumerated field def date = bean.customDate // date will be a java.util.Date if customDate is a date field def relatedUser = bean.relatedUser // relatedUser will be an org.cyclos.entities.users.User // if relatedUser is linked entity field of type user
bean(class): returns a bean by type. The class reference needs to be passed.
addOnCommit(runnable), addOnRollback(runnable): Adds callbacks to be executed after the main database transaction ends, either successfully or with failure. Be aware that those callbacks will be invoked outside any transaction scope within Cyclos, so things like scriptHelper.sessionData.loggedUser won't work (because it requires retrieving the User object from the database). However, it is more efficient, as no new database access needs to be done. This is mostly useful to notify an external application that some data has been persisted in Cyclos (after we're 100% sure that the data is persistent). Keep in mind that there is a (very) small chance that the main transaction is committed / rolled back but then the server crashes, and the callback weren't yet called. So, when synchronizing with external systems, it is always wise to do some form of timeout / recovery mechanism.
addOnCommitTransactional(runnable), addOnRollbackTransactional(runnable): Same as the non-transactional counterparts, but they are executed inside a new transaction in Cyclos
sessionData: The currently bound org.cyclos.impl.access.SessionData.
formatter: A org.cyclos.impl.utils.formatting.FormatterImpl.
Services and Handlers: All *ServiceLocal and *Handler objects are bound via simple names, starting with lowercase characters. Services are bound as 'nameService' and handlers as 'nameHandler'. For example, org.cyclos.impl.users.UserServiceLocal is bound as userService, and org.cyclos.impl.access.ConfigurationHandler is bound as configurationHandler.