Workflows

Guite edited this page Nov 12, 2014 · 3 revisions

Workflows

Das Zikula-Workflow-System basiert auf Zuständen (States) mit internem Object-Mapping. Damit können Entwickler relativ einfach Module mit konfigurierbaren Workflows erstellen. Workflows gibt eigentlich ohnehin in jedem Modul: Erst kommt die eine Aktion dann die nächste, nur dass sie selten als Workflows implementiert sind.

Der Vorteil der Nutzung des Workflow-Systems liegt darin, dass ohne Programmierarbeit Abläufe verändert und erweitert werden können. Dazu muss einfach eine XML-Datei angepasst werden.

Überblick

Um die Workflows zu nutzen, musst Du einfach ein neues Verzeichnis mit einige Dateien darin anlegen:

  • /module/MODULENAME/pnworkflows/ (Hier liegt alles, was mit den Workflows zu tun hat)
  • /module/MODULENAME/pnworkflows/standard.xml
  • /module/MODULENAME/pnworkflows/function.standard_permissioncheck.php
  • /module/MODULENAME/pnworkflows/operations/ (Hier liegen die Operation Funktionen - eine pro Datei)
  • /module/MODULENAME/pnworkflows/operations/function.createX.php (Beispiel)
  • /module/MODULENAME/pnworkflows/operations/function.updateX.php (Beispiel)
  • /module/MODULENAME/pnworkflows/operations/function.deleteX.php (Beispiel)

States und Actions

Das Zikula-Workflow-System geht davon aus, dass es in einem Workflow verschiedene Zustände (States) gibt, in denen jeweils nur bestimmte Aktionen (Actions) möglich sein. Das gilt für das einfache Einreichen und Freischalten von News-Artikeln bis hin zu komplexeren Verwaltungen von Zahlungsvorgängen.

Überlege Dir für Deinen Workflow, welche States und welche Actions es geben muss - dann kannst Du den Workflow in ein einfaches XML-Dokument fassen.

XML Struktur

Das Herzstück eines Workflows ist die XML-Definition, bestehend aus einigen einfachen XML-Tags. Der gesamte Workflow wird durch ein Tag umschlossen darin enthalten sein, müssen

<title> und wie hier in einem Minimal-Beispiel:
<workflow>
    <title>Beispiel-Workflow</title>
    <description>Eine kurze Beschreibung dessen, was der Workflow macht.</description>
</workflow>

States

Nun legst Du die Zustände fest, die Deine Inhalte annehmen können:

    <states>
        <state id="initial">
            <title>Initial</title>
            <description>Ausgangsstatus</description>
        </state>

        <state id="waiting">
            <title>Wating</title>
            <description>Inhalt wartet darauf, freigeschaltet zu werden</description>
        </state>

        <state id="approved">
            <title>Approved</title>
            <description>Der Inhalt wurde freigeschaltet</description>
        </state>
    </states>

Wie Du siehst besteht auch jeder State aus einem Umschließenden -Tag mit

<title> und drin. Umschlossen werden alle States von einem -Tag. Das Ganze befindet sich dann wiederum innerhalb des -Tags oben.

Actions

Als nächstes kommen die Aktionen (Actions) - hier findet die Zauberei statt: Jede Action enthält die Information, aus welchem State sie aufgerufen werden kann und in welchen State der Inhalt nach der Aktion versetzt werden soll. Alle Actions werden natürlich auch wieder durch -Tags eingefasst. Drin sollten folgende Informationen definiert werden:

  1. '''

    <title> ''' Name der Aktion
  2. Beschreibung

  3. State aus dem diese Aktion aufgerufen werden kann

  4. Zugriffsrechte, die dafür nötig sind

  5. Die Operation, die durchgeführt werden soll

  6. State, in den der Inhalt übergehen soll, wenn die Operation durchgeführt wurde

Beispiel:

    <actions>
        <!-- Einstiegsaktion für den ersten State -->
        <action id="submit">
            <title>Submit</title>
            <description>Ein neuer Artikel wird eingereicht zur Freischaltung</description>
            <permission>add</permission>
            <state>initial</state>
            <nextState>waiting</nextState>
            <operation online='0'>createX</operation>
        </action>

        <action id="approve">
            <title>Approve</title>
            <description>Artikel wird Freigeschaltet</description>
            <permission>moderate</permission>
            <state>waiting</state>
            <nextState>approved</nextState>
            <operation online='1'>updateX</operation>
        </action>

    </actions>

Operations

Operationen sind eine oder mehrere Funktionen, die ausgeführt werden, wenn die Aktion aufrufen wird, in der sie definiert sind.

Beispiel:

<operation>createX</operation>

In diesem Fall wird die Funktion createX() ausgeführt.

Das -Tag kann auch mehrere Attribute enthalten, die der Funktion als assoziatives Array übergeben werden.

Beispiel:

<operation state='1'>updateStatus</operation>

In diesem Fall wird an die Funktion updateStatus ('state' => '1') übergeben.

Du kannst auch mehrere Operationen pro Aktion aufrufen. Die werden dann der Reihe nach ausgeführt.

Beispiel:

<operation>createNews</operation>
<operation group='admin'>notify</operation>
<operation sugar='0' milk='1'>makeTea</operation>

Jede Operation benötigt eine gleichnamige Datei im Operations-Verzeichnis. Die werden nach dem gleichen Schema wie pnRender-Plugins benannt. Sie enthalten nach der gleichen Art eine Funktion:

  • Dateiname: function.{$operation}.php
  • Funktionsname: {$modulename}operation{$operation}

Beispiel: Die Datei /module/MODULENAME/pnworkflows/operations/function.makeTea.php enthält diese Funktion:

<?php
function mymodule_operation_makeTea($params) {

     // Dein Code...

}

The function will receive the object and any attributes parsed in the tag as an associative array of values. See below:

Beispiel: Hier kannst du eine Funktion sehen, die Attribute aus dem Workflow verarbeitet.

<?php

/**
*example workflow setOnline (insert news item to DB)
 *
*@param array $obj
*@param array, $params
 *
*@return bool
*/
function example_operation_setOnline(&$obj, $params)
{
    // Hier wird das Attribut "online" aus dem Workflow übernommen:
    $online = isset($params['online']) ? $params['online'] : false;

    // Der Wert wird dann dem Datenobjekt zugewiesen
    $obj['online'] = $online;


    return (bool)DBUtil::insertObject($obj, 'testtable', false, 'id');
}

Operationen können aber auch einfach Funktionen der Modul-API aufrufen. Durch die Operationen können wir die ganze Arbeit der Workflow-Engine überlassen: Einfach ein Objekt übergeben und durch den Workflow laufen lassen - dadurch lassen sich auch in einem Schritt mehrere Operationen durchführen. Das ist gerade in komplexen Anwendungen von Vorteil bei denen sich Workflows gelegentlich ändern.

Zugriffsrechte

Die Zugriffsrechte heißen so, wie im normalen Zikula-System:

  • overview
  • moderate
  • read
  • add
  • comment
  • edit
  • delete
  • admin

Der Grund dafür ist, dass die Zugriffsrechte von SecurityUtil::checkPermission() durchgeführt werden. Die Workflow-Engine übersetzt dann die Workflow-Rechte in die Zikula-rechte.

Jeder Workflow benötigt eine eigene Datei, die die Zugriffsrechte regelt. Die muss entsprechend benannt werden und eine gleichnamige Funktion enthalten:

  • Dateiname: function.{$workflowname}_permissioncheck.php
  • Function name: ($modulename}workflow{$workflowname)_permissioncheck()

Beispiel: Dieser Permission-Check wäre für die standard.xml und die Datei hieße function.standard_permissioncheck.php

<?php
/**
*@param array $obj
*@param int $permLevel
*@param int $currentUser
*@return bool
*/
function example_workflow_standard_permissioncheck($obj, $permLevel, $currentUser)
{
    // process $obj and calculate an instance
    $instance = '::';

    return SecurityUtil::checkPermission('example::', $instance, $permLevel, $currentUser);
}
?>

Der Check der Zugriffsrechte erfolgt auf diese Art, um die Flexibilität des Zikula-System auch für komplexere Anwendungen zu erhalten. Das obige Beispiel zeigt nicht alle Möglichkeiten ;-)

Zusammenfügen

Jetzt puzzeln wir das mal zusammen. Zunächst aber solltest Du beachten, wie clever die eigentlichen Objekt-Daten durch den Workflow getragen werden - Du musst Dich dabei um nichts kümmern. Du kannst die Objekte einfach auf der einen Seite in den Workflow hineinwerfen und die Workflows registrieren automatisch die Objekte, die sie verwalten sollen. Spannend ist auch die Möglichkeit in einer Aktion mehrere Operationen - auch im Zusammenspiel mit anderen Modulen - auszuführen.

Du solltest ein paar Funktionen aus der WorkflowUtil (Der Link ist tot!) kennen, um vernünftig damit arbeiten zu können.

executeAction()

Mit einem einfachen Aufruf übergibst Du Dein Objekt dem Workflow: WorkflowUtil::executeAction()

Workflow::executeAction benötigt einige Parameter:

  1. Name des Workflows (zum Beispiel: Standard (siehe oben))
  2. Das Datenobjekt, das behandelt werden soll
  3. Name der Aktion, die Aufgerufen werden soll
  4. Name der Datenbanktabelle, in der das Objekt gespeichert werden soll
  5. Name des Moduls zu dem der aufgerufene Workflow gehört (Default: Das aufrufende Modul)
  6. Name des ID-Feldes des Objektes

Die Funktion gibt ein Mixed oder true/false zurück.

Referenz: executeAction

WorkflowUtil::executeAction('standard', $address, 'submit', 'adresses');

Mit diesem Aufruf wird das Objekt (eine Adresse) in den den Workflow ("standard") übergeben und durch die Aktion "submit" in einen ersten Zustand versetzt. Diese Aktion sollte dann eine Operation enthalten, die das Objekt in die Datenbank einträgt. Danach wird automatisch der Workflow registriert und auf den Datenbankeintrag gemappt.

Um das Objekt nun in seinem Workflow "weiter zu befördern" musst Du das Objekt aus der Datenbank holen und der "nächsten" Aktion übergeben:

WorkflowUtil::executeAction('standard', $address, 'reject', 'adresses');

getActionsForObject()

Woher aber weißt Du, welches "die nächste Aktion" im Workflow ist? Um das herauszufinden, gibt es WorkflowUtil::getActionsForObject() - diese Funktion benötigt folgende Parameter:

  1. Datenobjekt
  2. Name der Datentabelle, in dem das Objekt liegt
  3. Name des ID-Feldes des Objektes
  4. Name des Moduls zu dem das Datenobjekt gehört (Default: Das aufrufende Modul)

Die Funktion gibt ein Mixed oder true/false zurück.

Referenz: getActionsForObject

$actions = WorkflowUtil::getActionsForObject($obj, 'adresses');

getActionsByState()

Mit WorkflowUtil::getActionsByState kannst Du alle Aktionen holen, die - gefiltert nach Zugriffsrechten - die möglich sind. Die Funktion benötigt folgende Parameter:

  1. Name des Workflows
  2. Name des Moduls zu dem das Datenobjekt gehört (Default: Das aufrufende Modul)
  3. Zustand (Default: initial)
  4. Datenobjekt

Die Funktion gibt ein Mixed oder true/false zurück.

Referenz: getActionsByState

$actions = WorkflowUtil::getActionsByState('standard');

Beispiel

Im folgenden Code-Beispiel kannst Du sehen, wie ein neues Objekt erstellt und editiert wird:

<?php
function howtoworkflow_user_edit()
{
    // retreive $id from GET request.
    $id = FormUtil::getPassedValue('id', null, 'GET');

    // start pnRender instance
    $pnRender = new pnRender('HowtoWorkflow', false);

    if(isset($id)) {
        // new item
        $obj = array();

        // assign blank array so we can display empty form
        $pnRender->assign('obj', $obj);

        // get actions for workflow (will default to initial state)
        $actions = WorkflowUtil::getActionsByState('standard');

        // assign these action to pnRender
        $pnRender->assign('actions', $actions);

        // process and display template
        return $pnRender->fetch('howtoworkflow_user_edit.htm');
    }

    // get item from the database
    $obj = DBUtil::selectObjectByID('htwf_testdata', $id);

    // get the possible actions for this object in whatever workflow state it's in
    $actions = WorkflowUtil::getActionsForObject($obj, 'htwf_testdata');


    // assign the action array to pnRender so template can process it
    $pnRender->assign('actions', $actions);

    // assign the variables received from input form to pnRender for display
    $pnRender->assign('obj', $obj);

    // process and display template
    return $pnRender->fetch('howtoworkflow_user_edit.htm');
}


/**
*process form edit
 *
*@return array
*/
function howtoworkflow_user_editaction()
{
    // get form input from POST array
    $obj = FormUtil::getPassedValue('obj', null, 'POST');

    // get actions from form input from POST array
    $action = FormUtil::getPassedValue('action', null, 'POST');

    // execute the workflow for this object
    WorkflowUtil::executeAction('standard', $obj, $action, 'htwf_testdata');

    // redirect to view function which will display updated results if any
    return pnRedirect(pnModURL('HowtoWorkflow','user','view'));
}

?>

Links

Clone this wiki locally
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.