Backend Live - Javascript Actions

The Javascript Actions extension provides you with the ability to define your own Action that can be used in subapp Action Bars or in Dialogs. You just need to create a Javascript file that specifies the execute function.

Installing to a Bundle

If you have an existing bundle, perhaps obtained by using the Magnolia CLI, you will need to add the modules as defined at Javascript Models 2.0, as well as its dependencies to each webapp’s /WEB-INF/lib directories:

Using a standard Magnolia Bundle, these directories would be:

  • apache-tomcat/webapps/magnoliaAuthor/WEB-INF/lib/

  • apache-tomcat/webapps/magnoliaPublic/WEB-INF/lib/

Installing with Maven

Maven is the easiest way to install the module. Add the following to your bundle:

your-webapp-maven-project/pom.xml
<dependency>
  <groupId>info.magnolia.backendlive</groupId>
  <artifactId>backend-live-actions</artifactId>
  <version>1.0</version>
</dependency>

Configuration

Make sure to follow the Configuration for Javascript Models 2.

You should already have exposed the cmsfn Templating function to your Javascript projects, but if not, this is how the configuration should look in the Configuration app:

image

Exposed Components can be considered "global", meaning that you do not reference them with this, rather just cmsfn. This will also apply to exposedComponents you define for each definition, which we will explain later.

If you’d like a full list of the default exposeedComponents and a description, you can click "Expand for More"

Expand for More

Component Description

cmsfn

Navigate content and create links.

damfn

Get assets and renditions and create links to assets.

sitefn

Get sites and themes.

imgfn

Get links to images from any workspace.

resfn

Create links to css and js files by given patterns.

restfn

Access REST clients.

searchfn

Search pages and content.

navfn

Create site navigation.

catfn

Get categories (tags) and access content by category.

Hooray!

You are now ready to start writing your own custom Javascript component.

Usage

This example will create a new Action using Javascript. This action will allow a user to open a completely different application, with the selected node selected in the subapp that you specify. Specifically, we are going to add an action in the Pages App to jump to the JCR Browser with the selected page, and to also do the same from the JCR Browser to the Pages App.

You can simply add the backend-live-actions-lm project from Backend-Live Samples if you want to see it in action. If you want to get a more hands-on experience, you can create a folder named backend-live-actions-lm in your Light Modules directory.

To create your Action with Javascript, simply create your javascript. The best place to maintain your Javascript files is in your Light Module’s backendScripts directory. In this sample, we are creating the file: light-modules/backend-live-actions-lm/backendScripts/actions/openSubApp.js.

The Javascript

Within this file, we need to create a class, define it’s execute function, and instantiate it. This is necessary for GraalVM to access the function. Here is the generic structure:

backend-live-actions-lm/backendScripts/actions/openSubApp.js
var OpenSubAppAction = function () {
    this.execute = function () {
        // Do something when Action clicked.
    }
};
new OpenSubAppAction();
  • Line 1: Create the Javascript Class

  • Line 2: Declare the execute function without parameters

  • Line 6: At the end of the file you must create an instance

Because this action will depend on several other Java classes, we can simplify our code by relying on an external utility Javascript file using our loadScript helper method.

Here is the utility class that can be used across multiple actions (or other backend extensions):

backend-live-actions-lm/backendScripts/utils.js
var Utils = function () {
    var NodeUtil = Java.type("info.magnolia.jcr.util.NodeUtil");
    var NodeTypes = Java.type("info.magnolia.jcr.util.NodeTypes");
    var DefaultLocation = Java.type("info.magnolia.ui.api.location.DefaultLocation");
    var Location = Java.type("info.magnolia.ui.api.location.Location");

    this.createDefaultLocation = function (locationType, appName, subAppId, path) {
        return new DefaultLocation(locationType, appName, subAppId, path);
    }

    this.getLocation = function () {
        return Location;
    }

    this.getNodeUtil = function () {
        return NodeUtil;
    }

    this.getNodeTypes = function () {
        return NodeTypes;
    }
}

Using `Java.type("full.class.path") exposes a class to Javascript, so you can now do things like:

var DefaultLocation = Java.type("info.magnolia.ui.api.location.DefaultLocation");
var defaultLocation = new DefaultLocation();

To use this class, you can modify the action class code like this:

backend-live-actions-lm/backendScripts/actions/openSubApp.js
loadScript("/backend-live-actions-lm/backendScripts/utils.js");
var OpenSubAppAction = function () {
    var utils = new Utils();
    this.execute = function () {
        // Do something when Action clicked.
    }
};
new OpenSubAppAction();
  • Line 1: Load the utils.js using the Resource File Path.

  • Line 3: Instantiate the Utils class.

  • Line 8: Create a class instance.

In this example, we want our action to open an app within a specific subapp of a different app (for example, moving from Pages to JCR Browser, and vice versa). We’ll define some properties in our YAML later, but you can see here how those parameters are made available to the OpenSubAppAction action.

backend-live-actions-lm/backendScripts/actions/openSubApp.js
loadScript("/backend-live-actions-lm/backendScripts/utils.js");

var OpenSubAppAction = function () {
    var utils = new Utils();
    this.execute = function () {
        var pathToOpen = "/";
        var nodeUtils = utils.getNodeUtil();
        var nodeTypes = utils.getNodeTypes();

        var jcrItem = this.content;
        if (!jcrItem.isNode()) {
            jcrItem = jcrItem.getParent();
        }
        var isContent = nodeUtils.isNodeType(jcrItem, nodeTypes.Content.NAME);
        if (isContent || (this.parameters.containsKey("useContentSubNodes") && this.parameters.get("useContentSubNodes") === true))  {
            pathToOpen = jcrItem.getPath();
        } else {
            pathToOpen = cmsfn.parent(jcrItem, nodeTypes.Content.NAME).getPath();
            this.log.info("Current node is either not content or useContentSubNodes was set to false: ID: {} pathToOpen {}",
                this.content.getItemId().toString(), pathToOpen);
        }

        var location = utils.createDefaultLocation(utils.getLocation().LOCATION_TYPE_APP, this.parameters.get("appName"),
            this.parameters.get("subAppId"), pathToOpen);

        this.log.info("Moving to {} in the {} app.", pathToOpen, this.parameters.get("appName"));
        locationController.goTo(location);
    }
}

new OpenSubAppAction();
  • Line 15: Use of this.parameters comes from the YAML definition.

  • Line 18: Use of cmsfn comes from the javascript-models configuration

  • Line 19: Use of this.log is provided by the JavascriptAction

  • Line 27: Use of locationController is defined in the YAML definition

You may have noticed that there are several references using this to access some local item. Several of these items are made available to you, here is a list of object you have access to locally:

Component Description

parameters

Custom definition items defined in the YAML file under the parameters property.

content

This is the item selected when clicking on the Action. If in the Pages App, it is the page node, for example.

definition

This is the YAML definition that you’ve specified when declaring the $type: jsAction.

def

This is a shorthand representation of the definition, simply used as it is carried over from previous versions.

log

This is the Magnolia Log4j log bound to the Javascript Action class. You can log debug, info, warn and error messages using this.

The YAML

The final piece of the puzzle is to put this all together. Here’s what a typical app action definition would look like when you use a custom Java class:

subApps:
  browser:
    actions:
      toJcrBrowserApp:
        $type: openSubAppAction
        appName: jcr-browser-app
        subAppId: browser

In this case, the Action’s Definition class provides two properties appName and subAppId to be available. To make our Javascript class possible, here’s how our definition would look:

backend-live-actions-lm/decorations/pages-app/apps/pages-app.yaml
subApps:
  browser:
    actions:
      toJcrBrowserAppJs:
        $type: jsAction
        modelPath: /backend-live-actions-lm/backendScripts/actions/openSubApp.js
        parameters:
          appName: jcr-browser-app
          subAppId: browser
        exposedComponents:
          locationController:
            componentClass: info.magnolia.ui.api.location.LocationController
            name: locationController
  • Line 5: All actions are now jsAction

  • Line 6: modelPath is now what distinguishes its actual execution

  • Line 7: parameters are free-form and can be reused in the Javascript via this.parameters.<NAME>

  • Line 10: exposedComponents can be made available if not defined in the javascript-models configuration

Now if you add it to your actionbar (as you can see in backend-live-actions-lm/decorations/pages-app/apps/pages-app.yaml), the button will be available to the Pages App.

Property Description

$type

Required (if not using class)

The value must be jsAction.

class

Required (if not using $type)

The class must be info.magnolia.module.backendlive.actions.JavascriptActionDefinition.

modelPath

Required The path to the Javascript class. This is the resources path reference.

parameters

This is a free-form set of parameters that you can use within your Javascript class that would look like this:

var Dumbo = function () {
    this.getMyInt = function () {
        return this.parameters.myIntParam;
    }
};
new Dumbo();

exposedComponents

You can expose Java @Inject backend objects. Referencing the above YAML definition, you could then use it like this:

var Dumbo = function () {
    this.changeLocation = function (location) {
        locationController.goTo(location);
    }
};
new Dumbo();
exposedComponents operate the same way as components defined in the javascript-models configuration app under /modules/javascript-models/config/engineConfiguration/exposedComponents

     <object-name>

The arbitrary name that will be used to access the defined class.

         componentClass

The Java class that will expose its public methods to your Javascript.

         name

The arbitrary name that will be used to access the defined class.

Samples

There are light-module samples for each of the projects found in our Backend-Live Samples repository.

Once you check out the project, you can simply copy the light-modules/backend-live-actions-lm folder into your magnolia.resources.dir location (defined in YOUR-WEB-APP/WEB-INF/config/default/magnolia.properties).

If you are interested in a more complex demonstration, and tutorial on how to use multiple extensions in conjunction with each other, you may be interested in checking out our Backend Live Demo project.

Changelog

The Changelog covers all updates related to this module.

Version Notes

1.0

  • Custom Javascript Actions Released

Feedback

Incubators

×

Location

This widget lets you know where you are on the docs site.

You are currently perusing through the Backend Live docs.

Main doc sections

DX Core Headless PaaS Legacy Cloud Incubator modules