# Déclaration d'un modèle de workflow

Le modèle d'un workflow référence le graphe et la définition des paramètres. Ce modèle est une Smart Structure qui hérite de la Smart StructureWDOC.

Exemple : vendor/My/SmartStructures/MyWorkflow/MyWorkflowArticleModel.xml

<smart:config xmlns:smart="https://platform.anakeen.com/4/schemas/smart/1.0">
  <smart:structure-configuration name="MY_WFL_ARTICLE" label="Modèle suivi des articles">
    <smart:extends ref="WDOC"/>
    <smart:class>My\SmartStructures\MyWorkflow\MyWorkflowArticleBehavior</smart:class>
  </smart:structure-configuration>
</smart:config>

# Attachement d'un graphe de workflow

L'attachement du modèle à un graphe donné se fait dans la classe de comportement du modèle. La méthode SmartStructure\Wdoc::useWorkflowGraph() permet d'indiquer quel graphe est utilisé par le modèle. Cette association doit être faite dans le constructeur de la classe de comportement du modèle.

Exemple : vendor/My/SmartStructures/MyWorkflow/MyWorkflowArticleBehavior.php

namespace My\SmartStructures\MyWorkflow;

class MyWorkflowArticleBehavior extends \SmartStructure\Wdoc
{
  public function __construct(...$args)
  {
    $this->useWorkflowGraph(__DIR__ . "/MyArticleGraph.xml");
    parent::__construct(...$args);
  }
}

Attention

Le constructeur parent construit les droits des transitions en fonction du graphe. Il doit donc être indiqué après l'appel à la méthode useWorkflowGraph().

# Classe de comportement d'un modèle de workflow

La classe correspondante à un workflow doit définir la structure de ce workflow. Cette structure s'exprime sous la forme d'un graphe, exprimé notamment au moyen des propriétés de classe $cycle et $transitions.

Si la méthode SmartStructure\Wdoc::useWorkflowGraph() a été utilisée, alors les propriétés $cycle, $transitions et $stepLabels ont été initialisées avec le graphe indiqué.

Dans le cas contraire, il est nécessaire de renseigner ces propriétés afin de constituer le graphe du workflow.

Attention

La définition manuelle du workflow (sans passer par un XML) bien que possible est déconseillée.

# $cycle

La propriété $cycle définit le graphe à proprement parler. C'est une liste d'éléments de la forme :

[
  "e1" => "<état de départ>",
  "e2" => "<état d'arrivée>",
  "t" => "<transition à utiliser>"
];

  • e1 est l'identifiant de l'état de départ,
  • e2 est l'identifiant de l'état d'arrivée,
  • t est l'identifiant de la transition à utiliser pour passer de l'état de départ à l'état d'arrivée.

Exemple :

public $cycle = [
    [
        "e1" => "<état de départ 1>",
        "e2" => "<état d'arrivée 1>",
        "t"  => "<transition à utiliser 1>"
    ],,
    [
        "e1" => "<état de départ N>",
        "e2" => "<état d'arrivée N>",
        "t"  => "<transition à utiliser N>"
   ]
];

# $transitions

La propriété $transitions définit l'ensemble des types de transitions utilisables. C'est un tableau associatif dont la clé est le nom de la transition, et la valeur est un élément de la forme :

[
  "nr" => true,
  "m0" => "myFirstCondition",
  "m1" => function ($nextStep, $currentStep, $confirmationMessage) {},
  "m2" => "myFirstProcess",
  "m3" => "myLastProcess",
  "ask" => ["askId1",]
];

  • nr est un booléen indiquant qu'il ne faut pas demander de raison au passage de transition (nr pour No Reason).
    La valeur par défaut est à false ; dans ce cas, une popup demande à l'utilisateur de préciser la raison du passage de transition.
  • m0, m1, m2, m3 sont chacune le nom d'une méthode de la classe de workflow.
  • ask est un tableau d'identifiants de paramètres ou de Smart Fields du workflow ou de Smart Field de l'élément associé, qui sont utilisés pour générer la demande des informations de transition.

La configuration d'une transition est effectuée au travers de l'objet Anakeen\SmartStructure\Wdoc\Transition fourni par la méthode Wdoc::getTransition().

Préconisation

Il est déconseillé de modifier directement l'attribut $this->transition de l'objet dans le cas où celui-ci est initialisé par un graphe en utilisant la méthode Wdoc::useWorkflowGraph(). Il est conseillé d'utilisé la méthode Wdoc::getTransition()

Exemple :

<?php
namespace My\SmartStructures\MyWorkflow;

class MyWorkflowArticleBehavior extends \SmartStructure\Wdoc
{
  public function __construct(...$args)
  {
    $this->useWorkflowGraph(__DIR__ . "/MyArticleGraph.xml");
    parent::__construct(...$args);
    $this->getTransition("transmission")->setM0(function () {
      // ...
    });
    // ...
    $this->getTransition("printing")
      ->setM2(function () {})
      ->setRequireComment(false);

    $this->getTransition("rejecting")->setAsks(function () {
      return [$this->getAttribute("wmy_reason"), $this->getSmartElement()->getAttribute("my_deadlinedate")];
    });
  }
}

# $firstState

La propriété $firstState définit l'état initial des Smart Elements liés à ce cycle de vie. Elle désigne l'identifiant d'un état.

Graphe XML

Indiqué par le Smart Field graph/steps/step/@initial .

# $attrPrefix

Lors de la génération d'un cycle de vie, Anakeen Platform génère un grand nombre de Smart Fields pour paramétrer finement chaque état et chaque transition. Ces Smart Fields sont préfixés par $attrPrefix pour éviter les éventuelles collisions en base de données.

Graphe XML

Indiqué par le Smart Field graph/@ns .

# $stepLabels

La propriété $stepLabels indique les libellés pour les états et les activités. C'est un tableau associatif ; la clé est l'identifiant de l'étape.

Exemple :

[
  "start" => ["state" => "Initialisé", "activity" => "Rédaction"],
  "control" => ["state" => "Rédigé", "activity" => "Vérification"],...
];

Graphe XML

Indiqué par le Smart Field graph/steps/step/@state-labeletgraph/steps/step/@activity-label` .

# Les méthodes de transition

Les méthodes appelées lors des transitions en m0, m1, m2, m3 sont des méthodes de la Smart Structure de workflow.

Pour déclarer une méthode mi, il faut utiliser la méthode setMi() de l'objet transition en indiquant une fonction closure en paramètre.

# Définition méthode m0

La méthode appelée en m0 est de la forme suivante:

setM0($nextStep, $currentStep, $confirmationMessage);

où :

  • $nextStep est l'identifiant de la prochaine étape, (étape d'arrivée)
  • $currentStep est l'identifiant de l'étape actuelle, (étape de départ)
  • $confirmationMessage est le message de confirmation (si la méthode est exécutée pour l'affichage des menus, le message est vide)

Si elle retourne une chaîne vide, alors la transition peut être effectuée. Dans le cas contraire, elle doit retourner un message localisé qui indiquera à l'utilisateur la raison pour laquelle la transition ne peut pas être effectuée.

Le message retourné par la méthode est présenté dans un "tooltip" lors du survol de l'entrée de l'étape dans le menu Étapes, ou dans un "popover" lors du clic sur l'entrée de l'étape.

Note : Cette méthode est aussi appelée lors de l'affichage de la liste des transitions accessibles. Cela permet notamment de signaler à l'utilisateur les transitions qu'il a le droit d'effectuer, mais pour lesquelles il doit faire des modifications sur le Smart Element. De fait, cette méthode ne doit pas modifier le Smart Element.

<?php
namespace My\SmartStructures\MyWorkflow;

class MyWorkflowArticleBehavior extends \SmartStructure\Wdoc
{
  public function __construct(...$args)
  {
    $this->useWorkflowGraph(__DIR__ . "/MyArticleGraph.xml");
    parent::__construct(...$args);
    $this->getTransition("transmission")->setM0(function ($newState, $startState, $comment = '') {
      // Ici le smart element à la structure de MY_ARTICLE
      if (mb_strlen($this->getSmartElement()->getRawValue("my_content")) < 1000) {
        return "Le contenu est doit avoir au moins 1000 caractères";
      }
    });
  }
}

# Définition méthode m1

La méthode appelée en m1 est de la forme :

setM1($nextStep, $currentStep, $confirmationMessage);

où :

  • $nextStep est l'identifiant de la prochaine étape, (étape d'arrivée)
  • $currentStep est l'identifiant de l'étape actuelle, (étape de départ)
  • $confirmationMessage est le message de confirmation

Si elle retourne une chaîne vide, alors la transition peut être effectuée. Dans le cas contraire, elle doit retourner un message localisé qui indiquera à l'utilisateur la raison pour laquelle la transition ne peut pas être effectuée.

# Définition méthode m2

La méthode appelée en m2 est de la forme :

setM2($currentStep, $previousStep, $confirmationMessage);

où :

  • $currentStep est l'identifiant de l'étape actuelle, (étape d'arrivée)
  • $previousStep est l'identifiant de l'ancienne étape, (étape de départ)
  • $confirmationMessage est le message de confirmation

Si elle retourne une chaîne non vide, cette chaîne est considérée comme un message d'erreur qui sera affiché à l'utilisateur à l'issue de la transition (Cette méthode n'est pas bloquante).

# Définition méthode m3

La méthode appelée en m3 est de la forme :

setM3($currentStep, $previousStep, $confirmationMessage);

où :

  • $currentStep est l'identifiant de l'étape actuelle, (étape d'arrivée)
  • $previousStep est l'identifiant de l'ancienne étape, (étape de départ)
  • $confirmationMessage est le message de confirmation

Si elle retourne une chaîne non vide, cette chaîne est considérée comme un message d'erreur qui sera affiché à l'utilisateur à l'issue de la transition (Cette méthode n'est pas bloquante).

# Information de transition du workflow ask

Les informations de transition, qui sont utilisables dans les ask peuvent être :

Exemple de déclaration d'un paramètre de workflow utilisable pour une information de transition :

<smart:config xmlns:smart="https://platform.anakeen.com/4/schemas/smart/1.0">
    <smart:structure-configuration name="MY_WFL_ARTICLE">

        <smart:parameters>
            <smart:field-set name="my_infos_transitions" type="frame" label="Informations de transition" access="ReadWrite">
                <smart:field-longtext name="wmy_reason" label="Raison de refus" access="ReadWrite" needed="true"/>
            </smart:field-set>
        </smart:parameters>

    </smart:structure-configuration>
</smart:config>

# Utilisation des informations de transition ask

Les informations de transition sont envoyées lors de la requête de transition effectuée par l'interface web.

Les valeurs présentées sur l'interface sont les valeurs des paramètres ou des Smart Fields référencés au moment de la demande de transition.

Les valeurs des informations de transition utilisées sont enregistrées dans l'historique du Smart Element.

Si le paramètre référencé est obligatoire, l'interface exigera que le paramètre soit complété. Cette contrainte n'est pas testée lorsqu'il s'agit de passer une transition par programmation. Elle n'est prise en compte que par l'interface web. De même, les valeurs des informations de transition ne sont remplies que lors d'une transition effectuée par l'interface.

Attention

Contrairement aux paramètres de Smart Structure ordinaires, les contraintes ne sont pas prises en compte dans les informations de transition.

Dans les méthodes m1, m2, m3, la récupération des valeurs des informations de transition s'effectue au moyen de la méthode WDoc::getAskValue($askFieldId). La méthode m0 n'a pas accès aux valeurs des ask, car elle est aussi utilisée pour la constitution de menu des transition.

Lors de la recherche des transitions accessibles, la méthode m0 ne peut accéder aux informations de transition (elles ne sont pas encore demandées).

Si le ask référence des Smart Fields du Smart Element, la valeur du ask est enregistrée dans le Smart Element entre le m0 et le m1. Par contre, le Smart Element n'est pas encore enregistré sur le serveur. Il sera enregistré après le m1 si cette méthode ne retourne pas d'erreur.

La déclaration des ask est réalisée par la méthode Transition::setAsks(). Elle prend en argument une fonction qui doit retourner un tableau d'objets (Smart Field) de type Anakeen\Core\SmartStructure\BasicAttribute.

<?php
namespace My\SmartStructures\MyWorkflow;

class MyWorkflowArticleBehavior extends \SmartStructure\Wdoc
{
  public function __construct(...$args)
  {
    $this->useWorkflowGraph(__DIR__ . "/MyArticleGraph.xml");
    parent::__construct(...$args);

    $this->getTransition("transmission")
      ->setM1(function () {
        $ctoComment = $this->getAskValue("my_content");
        $c = mb_strlen($ctoComment);
        if ($c < 1000) {
          return sprintf("Seulement %d caractères.\nLe commentaire doit avoir plus de 1000 caractères", $c);
        }
      })
      ->setM2(function () {
        $reason = $this->getAskValue("wmy_reason");
        $this->getSmartElement()->addHistoryEntry("Raison du rejet : " . $reason);
      })
      ->setAsks(function () {
        return [
          // Paramètre du workflow
          $this->getAttribute("wmy_reason"),
          // Smart Field du Smart Element courant
          $this->getSmartElement()->getAttribute("my_content")
        ];
      });
  }
}

# Inhiber la demande de raison

Par défaut, une confirmation avec une demande de la raison pour effectuer la transition est demandée. La méthode setRequiredComment de l'objet Transition permet de désactiver cette demande.

<?php
class MyWorkflowArticleBehavior extends \SmartStructure\Wdoc
{
  public function __construct(...$args)
  {
    $this->useWorkflowGraph(__DIR__ . "/MyArticleGraph.xml");
    parent::__construct(...$args);

    $this->getTransition("transmission")->setRequiredComment(false);
  }
}

Cette méthode modifie l'index nr du tableau de transitions.

# Programmer un graphe sans XML

Considérons le workflow de l'introduction. Pour avoir un graphe identique, la classe le définissant contiendra :

<?php
namespace My\SmartStructures\MyWorkflow;

class MyWorkflowArticleBehavior extends \SmartStructure\Wdoc
{
  public $attrPrefix = "MY";
  public $firstState = "start";

  public $cycle = [
    [
      'e1' => 'start',
      'e2' => 'control',
      't' => 'transmission'
    ],
    [
      'e1' => 'control',
      'e2' => 'start',
      't' => 'correcting'
    ],
    [
      'e1' => 'control',
      'e2' => 'rejected',
      't' => 'rejecting'
    ],
    [
      'e1' => 'control',
      'e2' => 'published',
      't' => 'publishing'
    ],
    [
      'e1' => 'published',
      'e2' => 'rejected',
      't' => 'rejecting'
    ],
    [
      'e1' => 'published',
      'e2' => 'printed',
      't' => 'printing'
    ]
  ];
  public $transition = [
    'transmission' => [
      'label' => 'Transmettre'
    ],
    'correcting' => [
      'label' => 'Corriger'
    ],
    'rejecting' => [
      'label' => 'Abandonner'
    ],
    'publishing' => [
      'label' => 'Diffuser'
    ],
    'printing' => [
      'label' => 'Imprimer'
    ]
  ];
  public $stepLabels = [
    'start' => [
      'state' => 'Initialisé',
      'activity' => 'Rédaction'
    ],
    'control' => [
      'state' => 'Rédigé',
      'activity' => 'Vérification'
    ],
    'published' => [
      'state' => 'Publié',
      'activity' => 'Publication'
    ],
    'printed' => [
      'state' => 'Imprimé',
      'activity' => ''
    ],
    'rejected' => [
      'state' => 'Rejeté',
      'activity' => ''
    ]
  ];
}