WebCenter Dashboard Scripting API


title: WebCenter 24.03 Dashboard Scripting API

Introduction

This document describes the API available for scripting in Dashboard / Inline Editing framework.

Last updated: Mon Feb 26 2024

Getting started

This document describes the interface per type of object. To get to a specific object use a method which returns an object of that type (i.e. API.getTaskContext() returns a Task, API.getDocumentContext() returns a Document, API.getProjectContext() returns a Project).

The Script Editor provides autocomplete suggestion based on the context (Project/Document/Task Dashboard) you are in and the code you are writing. All methods are accessible from the global object API.

Sandbox

The scripts are run in a sandbox. Only a limited set of standard JavaScript functionality is available. Most global objects are not available. Available global objects are:

  • Date
  • Math
  • JSON
  • NaN
  • isNaN
  • Infinity
  • isFinite
  • parseFloat
  • parseInt
  • Error
  • console (log, warn and info)

It is not possible to access/modify the DOM or external systems using default JavaScript methods (e.g. window, document, jQuery, alert, confirm, XMLHttpRequest, fetch...). These globals will never become available, for all use cases we will try to provide alternatives through the API itself.

External systems can be contacted using the External API Connection functionality (API.doExternalCall(...)).

It is not possible (and never needed) to write asynchronous code, all methods of the API are synchronous.

Why is the sandbox so strict?

We know the sandbox is quite strict. This is on purpose.

WebCenter will compile the scripts so they are run as optimal as possible. This means that behind the scenes all scripts run in parallel even if WebCenter actually guarantees sequential execution order (see "Script Execution Order" below). Also scripts are run asynchronous even though the API looks synchronous. There is a strong cache and WebCenter does batched requests to the server in case information is missing. These optimisations can go quite far but you should never notice this. For all practical purposes, just think of the API as synchronous and that all scripts are always run in a specific sequential order one after another, because that is what you will actually see.

The strictness of the sandbox allows us to do these advanced optimisations behind the scenes without you noticing. It also allows us to keep the API as simple as possible (synchronous) and also allows us to guarantee that all scripts will keep working when upgrading WebCenter.

Debugging

It is possible to write logs to the console with console.log(). You can debug your scripts by adding a debugger; statement at the line where you want to debug. If you have the browsers developer tools open and the script hits a debugger; statement, the browser will break code execution and allow you to investigate variable values and step through your code.

It is important to remove all debugger; statements when the script is used in production. If a script has a debugger; statement, WebCenter will disable all optimisations on the script to make debugging easier. Because optimisations are disabled this means that just having a debugger; statement will cause performance to decrease considerably.

Types of scripts

Currently there are three types of scripts:

Before Validation Script

A Before Validation Script is run when the user saves the form. These scripts can read and adapt values of the Project/Document/Task.

Validation Script

Validation scripts are run when the user tries to save the form, after the "before validation scripts" have completed. In Validation scripts it is not possible to adapt values. Validation scripts keep running untill the form is valid and submitted or the user clicks away the validation error message.

To show a validation message (and block form submission), you can call the method API.addValidationError(). It is not needed to remove validation errors, before every run of the script all errors are removed.

Visibility Script

Visibility scripts are run on every render of the page and when the page is saved. The result of this script will determine the state of your block. Every visibility script should return one of the following constants, depending on the type of block:

  • Project/task/document information blocks:
    • READONLY: will make the field readonly. The user will only be able to view the value, not change it.
    • EDITABLE: makes the field editable so the user can change the value.
    • REQUIRED: this will make your field required. An error will be shown when the user tries to save the form when there is no value in this field. This will also make your field editable.
    • HIDDEN: will hide the block
  • Other Blocks:
    • VISIBLE: will make the block visible
    • HIDDEN: will hide the block

When no value is returned in the script, the returnvalue will default to HIDDEN. There can be only one visibility script defined per block. Validation scripts that are defined on blocks that are not visible (= HIDDEN) will not be executed. In Visibility scripts it is not possible to adapt values. It is not possible to make external API calls.

OnChange Script

OnChange scripts will be executed when the user modifies the field to which the script is linked. The field should be editable and in Edit or Creation Form. The script will not be executed when doing inline editing. The script will only be run when the user is finished editing a field (e.g. after jumping to another field or clicking outside of the field), and not every time the user makes a change like typing a letter. These scripts can read and adapt values of the Project/Document/Task

Scripting Buttons

There is an inline action Run Script available for documents, projects or tasks that can be added to have a simple button that runs a script. The script will have access to the full API and can read and change any of the information in the form.

This action is also available in inline editing. When in inline editing, clicking the button will immediatelly save after the script finishes.

Script Execution Order

When looking at Script Execution Order, we can divide scripts in two groups: scripts that can adapt values (like "Scripting Buttons" and "Before Validation Scripts") and scripts that can only read what is on the page (like "Validation Scripts").

Scripts that can adapt values

For scripts that can do changes, WebCenter has a strong guarantee that scripts always produce results as if they are run sequentially one after one. You can always assume this.

For example, Scripting buttons are executed when you click them. If you click another button immediatelly after clicking the first one we guarantee that the second button sees all the changes done by the first button when it is run. (even in the rare situation when the first button would actually take a while to run and didn't finish yet) Side note: if the second button needs to wait because the first button is still running, a message "Processing..." will be shown to the user.

Like "Scripting Buttons", "Before Validation Scripts" can also do changes and are run in a strict sequential order. The next script in this order will see all the changes of the previous script, and so on. Additionally, the first "before validation scripts" is guaranteed to see all changes done by previously clicked buttons and all changes previously done by the user.

For "Before Validation Scripts" in particular, the order is as follows: we always start from the top-most dashboard in the form. In that dashboard different information blocks might have "before validation" scripts attached to them. These "before validation" scripts are executed in the strict order you get by sorting alphabetically on the name of the script. Usually you probably don't care, but if script execution order is important to you, you can control the order that scripts are executed by changing their name with some alphabetic prefix. Note that you don't need to have unique names for scripts, even scripts with the same name are run in a consistent order which doesn't change unless you completely delete/re-add blocks in the dashboard. However, before the scripts on the blocks in the dashboard are run, the scripts of all subdashboards (re-usable dashboard, open popup button, tabs) are executed. Scripts in subdashboards are always executed before the scripts in the current dashboard. Different subdashboards/tabs are executed in the strict order you get by sorting the name of the dashboard alphabetically. Subdashboards are executed one by one, so first all scripts of e.g. "DashboardExample_TabA" execute, then all scripts of e.g. "DashboardExample_TabB". Again, you usually won't care, but if needed you can control the execution of different subdashboards by changing their name.

When saving the page, the page will wait for all scripting buttons, before validation scripts and validation scripts to finish executing before the form is submitted.

Scripts that can only read what is on the page

For scripts that can only read what is on the page, execution order doesn't actually matter as you cannot see the difference.

For example, Validation scripts are reexecuted all the time to provide up to date validation messages. The first time they run after clicking Save they do however only start after all the "before validation scripts" have finished executing so they are guaranteed to see the latest results when doing the initial validation.

Performance considerations

Scripting is very powerful, but too much scripting can make the page slow. Some tips to keep scripting fast:

In general, simple is better:

  • Use as little scripting as possible. Only use it when really needed.
  • If you do need scripting, keep scripts themselves simple.
  • Avoid looping through lot's of data. NEVER loop through all documents in a library! Avoid running through all documents in a document reference if possible
  • Check if you can avoid looping through data in scripts by tracking the information in a project attribute instead. E.g. for a simple thing like some status of the project, don't recalculate that every time.

Tips specifically for Dashboard Scripting:

  • Make sure to put scripts in the right dashboard to avoid doing the exact same thing multiple times. For example, don't put a script which only uses project information in a drilldown document dashboard. The script would run for every document even if the script is actually calculating the same project attribute the exact same way. (unnecessary repeating the same calculations)
  • Avoid setting the same attribute from the same script multiple times. Instead, calculate the value and then set it only at the end when the value is known. Setting intermediate values has more performance impact than you might expect and can be easily avoided.

FAQ & Other

Why do I get "permission denied" when trying to set a value using a script?

Dashboards are run with the permissions of the current user. If the current user does not have permissions to e.g. modify a certain role, trying to do so with the scripting API will give a "permission denied".

You will need to give the user permissions to change roles/attributes/project status/... if you want a Dashboard Script to be able to do so. If you do not want to give the user permissions, you can always use the workflow or rule engine instead after saving the form.

Known scripting peculiarities

  • Writing new Array() is not supported, instead simply write [] to make a new array.

Useful Editor shortcuts

Description Windows / Linux Mac
Format Script Shift-Alt-F Shift-Alt-F
Search Ctrl+F Command+F
Replace Ctrl+F Command+Alt+F
Undo Ctrl+Z Command+Z
Redo Ctrl+Y Command+Y or Shift+Command+Z

Error indications in the Editor

The Script Editor will try to help you write correct scripts by telling you which functions you can call on that object or indicate when trying to call a function on a wrong object. It does this by guessing what type each function returns and what each variable contains. When it knows that, it knows what you can do with it.

Note that errors that are indicated in the script code in the editor will not actually prevent execution of the script. It will just tell you that the editor guesses there is probably something wrong and the script will likely fail when you run it.

The editor is usually quite good at guessing types of things, but sometimes it guesses wrong and you may want to give it some hints. While you could always simply ignore the error, autocomplete might not function as expected when the editor cannot guess the type.

Null checks on API calls

Tip summary: Always assign to a variable first before doing the null check. (first example instead of second example)

Details:

Some functions in the scripting API can return null. They return null when there is no object to return. The editor will know that the function can return null and if you try to call a method on it without checking, it will tell you that the object can be null and should be checked to see if it exists first.

There are a couple of ways to do this, like optional chaining (the ?. operator in JavaScript) or by simply adding an if-check around it:

const project = API.getProjectContext();
if(project) {
// Works:
const projectName = project.getName();
}

Note that if you try to do it like this instead the editor will not know:

if(API.getProjectContext()) {
// Shows error in editor:
const projectName = API.getProjectContext().getName();
}

The editor does not know that the calling the same method API.getProjectContext() twice returns the same result. We know it does in this case, but the editor doesn't.

Advanced use case: Declaring custom JavaScript objects that you modify from the script

Tip summary: In case you are writing advanced scripts where you create custom JavaScript objects in the script and modify them later, the editor might guess the wrong type sometimes. You can simply add /** @type {?} */ above the variable to disable errors and autocomplete on that variable.

Details:

In some more advanced scripts you might want to define your own JavaScript objects. For example:

const myCustomObject = {
someProperty: "",
listOfProjectNames: []
};

// later in the code:
myCustomObject.listOfProjectNames.push(project.getName());

The editor tends to be quite good at guessing the type you intended with these objects, but sometimes it guesses wrong. In the example, because the array listOfProjectNames inside our custom object doesn't have any values on creation and it is nested in an object, the editor doesn't know what type is in the array and guess that it will simply always be an empty array. So, when we then try to add something to the array later in the script the editor will show an error.

There are a couple of ways to resolve this. The simplest is probably to simply disable type checking and autocomplete on the variable by simply adding /** @type {?} */ above it:

/** @type {?} */
const myCustomObject = {
someProperty: "",
listOfProjectNames: []
};

// later in the code:
myCustomObject.listOfProjectNames.push(project.getName());

This way the editor will stop showing errors when using the variable. A disadvantage of using this is that this will also disable all autocompletion features on that variable since you just told it that it guessed the wrong type anyway (e.g. it will not suggest myCustomObject.listOfProjects.push(...) as a possible function). This is probably fine since if you do things this complex, you probably know how to push things on an array.

Another solution to this is by avoiding such an empty array in the first place by using things like array.map and array.filter to immediatelly have a filled array instead.

Yet another solution is to simply tell the editor the type of the array. That way you keep all the editor features. In the example this would be (note the string[]):

/** @type { {someProperty: string, listOfProjectNames: string[]} } */
const myCustomObject = {
someProperty: "",
listOfProjectNames: []
};

// later in the code:
myCustomObject.listOfProjectNames.push(project.getName());

In the example the type is {someProperty: string, listOfProjectNames: string[]}. You can have all kinds of types, simple types: string, number, boolean. Arrays of things: number[], boolean[], Project[]. Objects with things inside: for example an object with two properties could look like this: {name: string, count: number}. You can also have arrays inside objects, e.g.: {name: string, users: User[]} or arrays of objects, e.g.: {name: string, count: number}[] and so on... The editor is using the typescript js-doc syntax for defining types.

Generated using TypeDoc