Skip to content
Thomas Hufschmidt edited this page Oct 24, 2017 · 14 revisions

Basic Overview

This plugin structure can be divided into certain logic segments:

  • The RESTController (everything inside the RESTController folder) that handels all REST logic, such as managing routes, fetching data from ILIAS, handling authentification using tokens, interpreting REST calls and generating REST responses. The RESTController itself can also be devided into further logic segments:
    • Core routes (core folder) that are required for the admin-panel, authorisation as well as authentification.
    • The RESTController can be extended using extensions (extensions folder).
    • Common code (libs folder) that is useful for multiple routes/extensions.
  • The ILIAS-Plugin (plugin.php, sql-, lang- and classes-folders) that allows ILIAS to manage the RESTController to a certain degree. This mainly includes access to the admin-panel application and allowing the plugin to be disabled.
  • Applications using the REST interface (everything inside the apps folder).
    • The admin-panel which is build via HTML5, jQuery, AngularJS and certain other libraries and allows to manage client access-rights (API-Keys, secrets, authentification methods, route permissions) for the RESTPlugin.
    • A demo application showcasing all possible methods to gain access-tokens using OAuth2.
  • Some development tools (tools folder) allow easy "under the hood" access to ILIAS and the plugin itself.

Detailed Structure

  Customizing\global\plugins\Services\UIComponent\UserInterfaceHook\REST// Contains web-applications that are independent of ILIAS and use// the plugins REST-Interface to offer certain functionality.// This are fully functional demos of our REST-API, but at some point// those featues will be integrated into the ILIAS GUI directly instead // of using external software.
  ├── apps
  │   │   // The admin-panel application that controls plugin settings
  |   |   // by using its own REST-Interface.
  │   │   // The admin-panel uses various JavaScript and CSS libraries to
  │   │   // improve quality and to prevent reinventing the wheel.
  │   │   // Libs: AngularJS & Plugins, jQuery, LESS, Modernizr, Animate.css
  │   │   // Bootstrap.css, normalize.css, html5-poilerplates, etc.
  │   ├── admin
  │   |   // Small lightweight AngularJS application for making rest-calls to our API.
  │   |   // A more powerfull alternative be be found at: 
  │   |   //  https://advancedrestclient.com/
  │   └── checkout
  │
  │   // Contains some tools that can be used for development.// NOTE: You sould delete this directory in a deployment environment
  ├── tools
  │   │   // Contains a script that initializes ILIAS and allows easy
  │   │   // access to most ILIAS features.
  │   ├── dev
  │   │   // Contains the IPython shell that can be used to access
  │   │   // ILIAS directly.
  │   ├── shell
  │   │   // Demo-application that showcases how to implement all OAuth2
  |   |   // authentification methods .
  │   ├── oauth2demo
  |   |   // Unit-Testing for the restplugin
  │   ├── codeception
  │   │   // A minimal RESTClient test application writen in php using httpfull
  │   └── RESTClient
  │
  │   // Contains the main REST logic. Handles all routes, models and// optionally views.
  ├── RESTController
  │   │   // All RESTPlugin extensions should be placed inside this folder
  │   │   // following a certain sub-folder structure.
  │   ├── extensions
  │   │   │   // Each extension should be inside its own sub-folder,
  │   │   |   // preferably using a unique, descriptive name.
  │   │   ├── [Extension-Name]
  │   │   │   │   // Extension models should go into this folder,
  │   │   │   │   // See "Extensions" (Wiki) for details.
  │   │   │   │   // In short Models should include all logic.
  │   │   │   ├── models
  │   │   │   │   // Extension routes should go into this folder,
  │   │   │   │   // See "Extensions" (Wiki) for details.
  │   │   │   │   // In short Routes should use models to build responses.
  │   │   │   ├── routes
  │   │   │   │   // [Optional] If your route requires to render HTML
  │   │   │   │   // instead of create a REST response, templates should
  │   │   │   │   // be places inside this folder and "rendered" using
  │   │   │   │   // the Slim-Framework via \Slim\Views.
  │   │   │   ├── views [OPTIONAL]
  │   │   │   │   // A short desciption of the extension (using github
  │   │   │   │   // markdown wiki-language)
  │   │   │   ├── readme.md
  │   │   │   │   // A full human-readable API documentation, preferably
  │   │   │   │   // directly generated from your Routes using JAVADOC
  │   │   │   │   // and doxygen.
  │   │   │   └── apidoc.html
  │   |   |
  │   │   └── ...
  │   │
  │   |   // Ths folder contains all core extensions, eg. those required
  │   │   // by the admin-panel.
  │   ├── core
  │   │   └── ...
  │   |
  │   │   // This folder contains Active-Record like abstractions of all
  │   │   // RESTPlugin tables with a few additional model-driven additions.
  │   ├── database
  │   │   // Common code shared by all (core) extensions. This includes
  │   │   // stuff like token generation, authentification and unified
  │   │   // response- and request code, ILIAS integration via Login
  │   │   // and SOAP.
  │   ├── libs
  │   │   // Integrated Slim-Framework (now completely unmodified)
  │   ├── Slim
  │   │   // Slim-Framework application that manages all routes, enables
  │   │   // debugging and provides views, etc.
  │   │   // NOTE: This was formally named app.php
  │   └── RESTController.php
  │
  │   // Contains the ILIAS-Plugin's classes.// (required by ILIAS plugin infastructure)
  ├── classes
  │   │   // Used to handle and display the config UI inside ILIAS. This
  │   │   // just start the admin-panel app.
  │   ├── class.ilRESTConfigGUI.php
  │   │   // Stores plugin name is required by ILIAS.
  │   ├── class.ilRESTPlugin.php
  │   │   // Unused, but required by ILIAS since the plugin acts as an UIHook.
  │   └── class.ilRESTUIHookGUI.php
  │
  │   // Stores all localized strings for the ILIAS-Plugin.
  ├── lang
  │   │   // Language file for a specific language-CODE.
  │   └── ilias_[CODE].lang
  │
  │   // ILIAS database update files need to be stored here.
  ├── sql
  │   │   // This will be executed when installing/upgrading the plugin.
  │   └── dbupdate.php
  │
  │   // List of files to be ignore by git and thus will not be part of 
  |   // version-control.
  ├── .gitignore
  │   // Used by ILIAS to detect plugin data such as version, name and// ILIAS version requirements.
  ├── plugin.php
  │   // Starting point for all REST calls. Any call to this file should
     // start the RESTController, Initialize ILIAS, detect and process// the given route, eg. "example.com/restplugin.php/routes".// NOTE: This was formally named restplugin.php
  ├── api.php
  │   // This plugin adheres to GNU GENERAL PUBLIC LICENSE 3.0,// which was taken in accordance with ILIAS license.
  ├── LICENSE// Minimal project description
  └── README.md

Namespaces

The RESTController uses different namespaces to separate itself into smaller segments and also to separate itself from other components, such as the ILIAS core.
In addition namespaces are used to load its classes "on-demand" via php's autoload mechanism.

The following conventions are used and should (but don't have to) be followed:

  • All classes that belong to the RESTController should use \RESTController\ as namespace prefix.
  • All shared functionality is provided by classes in the \RESTController\libs\[name] namespace.
  • All core routes and models are located inside the \RESTController\core\[name] namespace.
  • All extension routes and models are located inside the \RESTController\extensions\[name] namespace.

In order to use models from one extension (or the core) inside another extension you have to make sure to use the correct namespace.
Either use the fully quantified names:

\RESTController\extensions\<B>\<ModelName>.doSomething();

Or alternatively add model B into A's namespace via:

use \RESTController\extensions\<B>\<ModelName>;
...
<ModelName>.doSomething();

Accessing non-namespace classes

While php is smart enough to fallback to the global root namespace for functions and variables that cannot be found inside the current namespace, this is not the case for classes. (Still true as of php 5.6)

This means in order to access ILIAS classes one has to directly tell php to look for them inside the root-namespace. This is done by prefixing class-names with \, eg. in order to use ilUserObj one has to reference it via \ilUserObj.
See Using namespaces: fallback to global function/constant.

Auto-Loading:

All routes (files) that are stored using the above folder-structure will be loaded (included) automatically during run(), since all routes need to be available in order to match the request-url to the correct route.

Models (and all other classes, eg. libs and exceptions) on the other hand will be loaded "on-demand" using a customized PSR-0 auto-loader, which treats namespaces as folders starting with the .../REST/RESTController folder as root. The first sub-namespace will be dropped, since it is used to separate different applications/libraries from each other. Once the auto-loader found the correct folder, it will try to include the file that matches the given class-name, if such a file exists, eg. using \RESTController\libs\TokenLib::doSomething(); will try to include .../REST/RESTController/libs/TokenLib.php if the TokenLib class isn't loaded yet.

The auto-loader tries to be smart about loading (core-)extensions and will automatically add a "models" sub-namespace to those classes, eg. \RESTController\core\clients will get \RESTController\core\clients\models. If no file with the correct class-name can be found, the auto-loader will fallback to using the namespace "as-is" without trying to be smart.

You can of course add your own auto-loader using spl_autoload_register.

Environment

The Slim-Framework provides a (global singleton) environment which can be accessed using \RESTController\RESTController::getInstance()->environment(). For a basic overview see Documentation.

The RESTController also inserts some variables into this environment, such as:

  • 'client_id' - ILIAS client-id, given during the setup stage of ILIAS. (Don not confuse with an ILIAS user)
  • app_directory - Full path (on the server) to the RESTController directory inside the plugin directory, eg /var/www/ilias/Customizing/global/plugins/Services/UIComponent/UserInterfaceHook/REST/RESController
  • When using one of the provided authentification middlewares (REST\RESTController\libs\AuthMiddleware), the following variables will also be available:
    • 'user' - The user that is stored inside the token
    • 'api_key' - The api-key that is stored inside the token
    • 'token' - The complete token found by the middleware

Fetching the request data

  • [WIP] Explain RESTRequest methods & fields

Sending a response

There are several standardized ways to send a response to the client. This methods should be preferred over using low-level php-functions such as echo, headers and even the Slim-Response class.
Assuming the $app variable points to an instance of the RESTController, eg. $app = \RESTController\RESTController::getInstance();, responses can be generated by the following methods:

  • If a route or model wants to send failure information (eg. missing request parameters) to the client it should use:
$app->halt($httpCode, $message = null, $restCode = null, $format = null);

$httpCode (Number) - The HTTP code that should be send (eg. 401)
$message (Mixed) - [Optional] The data/message that should be send to the client.
$restCode (String/Number) - [Optional] Add a more detailed REST specific error-code.
$format (String) - [Optional] The format that in which $message should be send. Currently supported are JSON (default) and RAW.

NOTE: As can be expected by its name, this method will stop code-execution after sending its information-data. (The information-data should be collected first and only be send once everything is available.)

  • If a route or model wants to send non-failure data (eg. the request results) to the client it should use:
$app->success($data, $format = null);

$data (Mixed) - The data that should be send to the client.
$format (String) - [Optional] The format that in which $message should be send. Currently supported are JSON (default) and RAW.

NOTE: Similar to halt(...) this will also stop further code-execution after sending its data. (The data should be collected first and only be send once everything is available)

  • If a route or model wants to terminate code-execution, without reaching the route-functions exit-point, it should use:
$app->stop();
  • If, for some strange reason, a model or route wants to prevent termination causes by another model using $app->stop(), $app->halt(...) or $app->success(...), it needs to try-catch the guilty Slim\Exception:
try {
    // Offending model-call here
} catch (\Slim\Exception $e) {
    // Recover here
}
]]]);

NOTE: This will not stop the data from $app->halt(...) or $app->success(...) being send, it only allows code-execution to continue.

  • If a model wants to send failure information to another model, route or the client it should throw an exception:
throw new \Exception([
    string $message = "" [,
    int $code = 0 [,
    Exception $previous = NULL
]]]);

NOTE: This allows other models to either catch the exception acting upon it and to continue execution or to ignore the exception, which will stop code-execution and send a JSON containing the exception data to the client.
Furthermore Extensions should create their own Exception-Classes by deriving from the base php Exception class.