Skip to content

Plugins

Nicklas Börjesson edited this page Jul 27, 2016 · 1 revision

(Note: This section describes functionality under development)

The functionality of the Optimal Framework administrative interface is extended and/or changed by using plugins. This actually applies to the OF back end, too.

##Hooks

All initialization of plugins (and many other things) are carried out in hooks. Hooks are functions that are called at certain points in time that allows plugins to run code and affect different parts of the UI. Plugins add their functionality by implementing hooks in their hooks.ts-file.

Hooks are called by name. This means that of one want to implement a hook in one's hook file, one just implements a function with the same name as the hook. For example, to have something happen in the init_web-hook, one just implement a function with the exact same name, init_web. And then that function will be called in all plugins when the hook is called.

Wrapper

When the admin UI plugin initiates, its server side part looks at the definitions of all plugins for any admin-ui related settings. Those are located in the "admin-ui" section of the plugin definition file, this is an example, the OF-admin declaration for the initFramework and initRoutes hooks:

      "admin-ui": {
        "ui-hooks": [
          {
            "name":"initFramework",
            "description": "Called when initialising the Angular controllers and directives",
            "parameters": ["app"]
          },
          {
            "name":"initRoutes",
            "description": "Called when initialising the Angular routes",
            "parameters": ["$routeProvider"]
          }        ],
        "mountpoint": "."
      }

The OF admin plugin backend (specifically admin.py) uses those settings to generate a wrapper that it presents to the admin application as if it was a static file at /admin/hooks_wrapper.ts. That wrapper library is then loaded by the UI as a module.

This is an actual example how it looks taken from a running broker, with the OF-Admin, Optimal BPM and OF-QAL-plugins enabled:

// This file is generated by the OF backend and presented as /admin/hooks
// Defined hooks:
// initFramework: Called when initialising the Angular controllers and directives
// initRoutes: Called when initialising the Angular routes
import {initFramework as optimalbpm_initFramework} from 'process/hooks';
import {initRoutes as optimalbpm_initRoutes} from 'process/hooks';
import {initFramework as qal_initFramework} from 'qal/hooks';
import {initRoutes as qal_initRoutes} from 'qal/hooks';
import {initFramework as admin_initFramework} from './hooks';
import {initRoutes as admin_initRoutes} from './hooks';

export function hook_initFramework(app){
    if (optimalbpm_initFramework) {
        optimalbpm_initFramework(app);
    };
    if (qal_initFramework) {
        qal_initFramework(app);
    };
    if (admin_initFramework) {
        admin_initFramework(app);
    };

}
export function hook_initRoutes($routeProvider){
    if (optimalbpm_initRoutes) {
        optimalbpm_initRoutes($routeProvider);
    };
    if (qal_initRoutes) {
        qal_initRoutes($routeProvider);
    };
    if (admin_initRoutes) {
        admin_initRoutes($routeProvider);
    };

}

Currently, the generated code checks if a certain function is defined. Another method would be for the backend to actually check the source of the plugin hook files and only list those implemented by the plugin. Which is more correct may be up for discussion, the current solution may be less complex.

Invoking

So if some part of the UI wants to call a hook, it justs imports it from the wrapper, for example when registering routes:

    // noinspection TypeScriptCheckImport (want no warning, as the wrapper only exists in runtime)
    import {hook_initRoutes} from "../hook_wrapper";


    app.config(($routeProvider) => {
        // Initialize all routes
        hook_initRoutes($routeProvider);
    });

To add its own routes, the plugin would then just implement the corresponding hook in its hooks.ts:

export function initRoutes($routeProvider) {
    // Configure all routes

    $routeProvider
        .when("/analysis", {
            templateUrl: "views/analysis.html",

        })
        .when("/admin", {
            templateUrl: "views/admin.html",
            // This is the mbe-nodes external directive, it needs an associated controller
            controller: "AdminController"
        })
        .when("/about", {
            templateUrl: "views/about.html",
            controller: "AboutController"
        })
        .otherwise({
            redirectTo: "/about"
        });
    console.log("initRoutes for OF Admin was run");
}

And this would be an example of how a typical hooks.ts implementation would perhaps look, this is the Optimal BPM plugin implementing initFramework and initRoutes:

import {ProcessController} from "./controllers/process";
import {ControlController} from "./controllers/control";
import {VerticalDraggableMenuController} from "./controllers/verticalDraggableMenuController";
import {process} from "./directives/process";
import {control} from "./directives/control";
import {verticalDraggableMenu} from "./directives/verticalDraggableMenu";

export function initFramework(app) {

    app.controller("ProcessController", ["$scope", "$http", "$q", "$timeout", ProcessController]);
    app.controller("ControlController", ["$scope", "$http", "$cookies", "$interval", ControlController]);
    app.controller("VerticalDraggableMenuController", ["$scope", "$timeout", VerticalDraggableMenuController]);

    app.directive("process", process);
    app.directive("control", control);
    app.directive("verticalDraggableMenu", verticalDraggableMenu);
    console.log("initFramework for Optimal BPM was run");
};


export function initRoutes($routeProvider) {
    // Configure all routes
    $routeProvider.when("/process", {"templateUrl": "process/views/process.html"})
        .when("/control", {"templateUrl": "process/views/control.html"});
    console.log("initRoutes for Optimal BPM was run");
};

Examples

For an invocation example, see OF init.ts

For an example of a definition that adds hooks, see the OF admin one.

The above implementation example but in the repo, the Optimal BPM hooks

##Menu items Currently, menu items are defined in the definitions.json, it can be discussed if they instead could be initiated in a hook instead,

####initMenus(menus) A proposal would be an init menus-hook. InitMenu is called when the admin ui menu is initialised. menus is an array of dicts with subitems and so on.

export function initMenus(menus){
    menus.append({"caption": "control", "path";"/admin/control"})
}

Dependencies

Between plugins

Unimplemented

Plugins may have dependencies. One plugin may depend on another working, or a specific call order for its hooks. Therefor there are two fields in the definitions handling that:

  • dependencies - an array of plugins upon which the plugin depend. If any dependencies are missing, the plugin will not be loaded, nor will any of its hooks be called.
  • loadWeight - an arbitrary decimal number by which plugins are sorted when the call order is decided. More flexible than an actual order index. Actually the admin interface itself initiates in a hook, just like any plugin, just so that other things may run before it.

Javascript libraries

Unimplemented

The OF admin interface uses SystemJS/JSPM for its module management. It should be quite possible to add local repositories to the main UI.

Clone this wiki locally