Formulaires + Actions

Créer, mettre à jour, ou supprimer du contenu.

Les formulaires Elgg sont envoyés aux actions. Les actions définissent le comportement pour l’envoi des formulaires.

Ce guide suppose une familiarité de base avec :

Enregistrer des actions

Les actions doivent être enregistrées avant de pouvoir être utilisées.

Il existe deux manières d’enregistrer des actions :

Utiliser elgg_register_action()

elgg_register_action("example", __DIR__ . "/actions/example.php");

Le fichier de script mod/example/actions/example.php sera maintenant utilisé à chaque fois qu’un formulaire est envoyé vers http://localhost/elgg/action/example.

Utilisez elgg-plugin.php

return [
   'actions' => [
      // defaults to using an action file in /actions/myplugin/action_a.php
      'myplugin/action_a' => [
         'access' => 'public',
      ],

      // define custom action path
      'myplugin/action_b' => [
         'access' => 'admin',
         'filename' => __DIR__ . '/actions/action.php'
      ],

      // define a controller
      'myplugin/action_c' => [
         'controller' => \MyPlugin\Actions\ActionC::class,
      ],
   ],
];

Avertissement

Un point d’achoppement pour de nombreux nouveaux développeurs est l’URL pour les actions. L’URL utilise toujours /action/ (singulier) et jamais /actions/ (pluriel). Toutefois, les fichiers de script d’action sont généralement enregistrés dans le répertoire /actions/ (pluriel) et ont toujours une extension. Utilisez elgg_generate_action_url() pour éviter toute confusion.

Enregistrement des actions en utilisant le fichier de configuration du plugin

Vous pouvez également enregistrer des actions via le fichier de configuration elgg-plugin. Pour cela vous devez fournir une section action dans le fichier de configuration. L’emplacement des fichiers d’action est situé dans le dossier /actions du plugin.

<?php

return [
        'actions' => [
            'blog/save' => [], // all defaults
            'blog/delete' => [ // all custom
                  'access' => 'admin',
                  'filename' => __DIR__ . 'actions/blog/remove.php',
            ],
        ],
];

Permissions

Par défaut, les actions ne sont accessibles qu’aux utilisateurs connectés.

Pour qu’une action soit disponible pour des utilisateurs non identifiés, passez "public" comme troisième argument :

elgg_register_action("example", $filepath, "public");

Pour restreindre l’action aux seuls administrateurs, passez "admin" en dernier paramètre :

elgg_register_action("example", $filepath, "admin");

Pour restreindre une action aux seuls utilisateurs non-connectés, passez "logged_out" en dernier paramètre :

elgg_register_action("example", $filepath, "logged_out");

Écrire des fichiers d’action

Utilisez la fonction get_input() pour accéder aux paramètres de la requête :

$field = get_input('input_field_name', 'default_value');

Vous pouvez ensuite utiliser l’api de la Base de données pour charger des entités et effectuer des actions dessus.

Pour indiquer une action réussie, utilisez elgg_ok_response(). Cette fonction accepte comme paramètre les données que vous souhaitez mettre à la disposition du client pour les appels XHR (ces données seront ignorées pour les appels non XHR)

$user = get_entity($guid);
// do something

$action_data = [
   'entity' => $user,
   'stats' => [
       'friends_count' => $user->getEntitiesFromRelationship([
           'type' => 'user',
           'relationship' => 'friend',
           'count' => true,
       ]);
   ],
];

return elgg_ok_response($action_data, 'Action was successful', 'url/to/forward/to');

Pour indiquer une erreur utilisez elgg_error_response()

$user = elgg_get_logged_in_user_entity();
if (!$user) {
   // show an error and forward the user to the referring page
   // send 404 error code on AJAX calls
   return elgg_error_response('User not found', REFERRER, ELGG_HTTP_NOT_FOUND);
}

if (!$user->canEdit()) {
   // show an error and forward to user's profile
   // send 403 error code on AJAX calls
   return elgg_error_response('You are not allowed to perform this action', $user->getURL(), ELGG_HTTP_FORBIDDEN);
}

Personnaliser des actions

Avant d’exécuter chaque action, Elgg déclenche un événement :

$result = elgg_trigger_event_results('action:validate', $action, [], true);

$action est l’action appelée. Si l’événement renvoie false alors l’action ne sera pas exécutée. Ne renvoyez rien si votre validation réussit.

Exemple : Captcha

Le module captcha utilise ceci pour intercepter les actions register et user/requestnewpassword et les rediriger vers une fonction qui vérifie le code captcha. Cette vérification renvoie false si la validation captcha échoue (ce qui bloque l’exécution de l’action associée).

Ceci est fait comme suit :

elgg_register_event_handler("action:validate", "register", "captcha_verify_action_event");
elgg_register_event_handler("action:validate", "user/requestnewpassword", "captcha_verify_action_event");

...

function captcha_verify_action_event(\Elgg\Event $event) {
  $token = get_input('captcha_token');
  $input = get_input('captcha_input');

  if (($token) && (captcha_verify_captcha($input, $token))) {
    return;
  }

  elgg_register_error_message(elgg_echo('captcha:captchafail'));

  return false;
}

Cela permet à un plugin d’étendre une action existante sans qu’il soit nécessaire de remplacer l’ensemble de l’action. Dans le cas du plugin captcha, cela permet de proposer un support captcha couplé de façon souple.

Actions disponibles dans le noyau

entity/delete

Si votre plugin n’implémente aucune logique personnalisée lors de la suppression d’une entité, vous pouvez utiliser l’action de suppression intégrée

$guid = 123;
// You can provide optional forward path as a URL query parameter
$forward_url = 'path/to/forward/to';
echo elgg_view('output/url', array(
   'text' => elgg_echo('delete'),
   'href' => elgg_generate_action_url('entity/delete', [
     'guid' => $guid,
     'forward_url' => $forward_url,
   ]),
   'confirm' => true,
));

Vous pouvez personnaliser les clefs du message de succès pour votre type d’entité et votre sous-type, en utilisant les clefs `"entity:delete:$type:$subtype:success" et "entity:delete:$type:success".

// to add a custom message when a blog post or file is deleted
// add the translations keys in your language files
return [
   'entity:delete:object:blog:success' => 'Blog post has been deleted,
   'entity:delete:object:file:success' => 'File titled %s has been deleted',
];

Formulaires

Pour afficher un formulaire, utilisez la fonction elgg_view_form comme ceci :

echo elgg_view_form('example');

Faire ceci génère quelque chose comme le balisage suivant :

<form action="http://localhost/elgg/action/example">
  <fieldset>
    <input type="hidden" name="__elgg_ts" value="1234567890" />
    <input type="hidden" name="__elgg_token" value="3874acfc283d90e34" />
  </fieldset>
</form>

Elgg fait automatiquement plusieurs choses pour vous quand vous générez un formulaire de cette manière :

  1. Il définit l’action vers l’URL appropriée, sur la base du nom de l’action que vous lui avez passé

  2. Il ajoute des jetons anti-csrf (__elgg_ts et __elgg_token) pour aider à garder vos actions sûres

  3. Il recherche automatiquement le corps du formulaire dans la vue forms/example.

Ajoutez le contenu de votre formulaire dans la vue forms/example de votre plugin :

// /mod/example/views/default/forms/example.php
echo elgg_view('input/text', array('name' => 'example'));

// defer form footer rendering
// this will allow other plugins to extend forms/example view
elgg_set_form_footer(elgg_view('input/submit'));

Désormais quand vous appelez elgg_view_form('example'), Elgg va produire :

<form action="http://localhost/elgg/action/example">
  <fieldset>
    <input type="hidden" name="__elgg_ts" value="...">
    <input type="hidden" name="__elgg_token" value="...">

    <input type="text" class="elgg-input-text" name="example">
    <div class="elgg-foot elgg-form-footer">
        <input type="submit" class="elgg-button elgg-button-submit" value="Submit">
    </div>
  </fieldset>
</form>

Entrées

Pour rendre une entrée de formulaire, utilisez l’une des vues d’entrée intégrées, qui couvrent tous les éléments d’entrée HTML standard. Voyez les fichiers individuels des vues pour une liste complète des paramètres acceptés.

echo elgg_view('input/select', array(
   'required' => true,
   'name' => 'status',
   'options_values' => [
      'draft' => elgg_echo('status:draft'),
      'published' => elgg_echo('status:published'),
   ],
   // most input views will render additional parameters passed to the view
   // as tag attributes
   'data-rel' => 'blog',
));

L’exemple ci-dessus va afficher une liste déroulante :

<select required="required" name="status" data-rel="blog" class="elgg-input-select">
   <option value="draft">Draft</option>
   <option value="published">Published</option>
</select>

Pour assurer la cohérence du balisage du champ, utilisez elgg_view_field(), qui accepte tous les paramètres de l’entrée à afficher, ainsi que les paramètres #label et #help (qui sont tous deux facultatifs et acceptent du HTML ou du texte).

echo elgg_view_field([
   '#type' => 'select',
   '#label' => elgg_echo('blog:status:label'),
   '#help' => elgg_view_icon('help') . elgg_echo('blog:status:help'),
   'required' => true,
   'name' => 'status',
   'options_values' => [
      'draft' => elgg_echo('status:draft'),
      'published' => elgg_echo('status:published'),
   ],
   'data-rel' => 'blog',
]);

Ce qui précède générera le balisage suivant :

<div class="elgg-field elgg-field-required">
   <label for="elgg-field-1" class="elgg-field-label">Blog status<span title="Required" class="elgg-required-indicator">*</span></label>
   <div class="elgg-field-input">
      <select required="required" name="status" data-rel="blog" id="elgg-field-1" class="elgg-input-select">
         <option value="draft">Draft</option>
         <option value="published">Published</option>
      </select>
   </div>
   <div class="elgg-field-help elgg-text-help">
      <span class="elgg-icon-help elgg-icon"></span>This indicates whether or not the blog is visible in the feed
   </div>
</div>

Types d’entrées

Une liste des types/vues de saisie inclus :

  • input/text - afficher une entrée texte <input type="text">

  • input/plaintext - affiche une zone de texte brut <textarea></textarea>

  • input/longtext - affiche une zone de texte riche - avec éditeur WYSIWYG

  • input/url - affiche une entrée d’adresse web - URL <input type="url">

  • input/email - affiche un champ de saisie de type e-mail <input type="email">

  • input/checkbox - affiche une case à cocher <input type="checkbox">

  • input/checkboxes - affiche un jeu de cases à cocher portant le même nom

  • input/radio - affiche un ou plusieurs bouton radio <input type="radio">

  • input/submit - affiche un bouton d’envoi <button type="submit">

  • input/button - affiche un bouton <button></button>

  • input/file - affiche un sélecteur de fichier <input type="file">

  • input/select - affiche une liste déroulante <select></select>

  • input/hidden - affiche une entrée invisible <input type="hidden">

  • input/password - affiche une entrée de type mot de passe <input type="password">

  • input/number - affiche une entrée de type nombre <input type="number">

  • input/date - affiche un sélecteur de date jQuery

Elgg offre quelques types de saisies facilitées

  • input/access - affiche une liste de niveaux d’accès Elgg

  • input/tags - affiche une entrée de type tags

  • input/autocomplete - affiche un sélecteur d’entités Elgg

  • input/captcha - vue destinée à être étendue par des plugins

  • input/friendspicker - affiche un sélecteur de contact Elgg par autocomplétion

  • input/userpicker - affiche un sélecteur d’utilisateur Elgg avec autocomplétion

  • input/grouppicker - affiche un sélecteur de groupe Elgg par autocomplétion

  • input/objectpicker - affiche un sélecteur d’objet Elgg par autocomplétion

  • input/location affiche une entrée de type adresse

Fichiers et images

Utilisez la vue input/file dans la vue du contenu de votre formulaire.

// /mod/example/views/default/forms/example.php
echo elgg_view('input/file', ['name' => 'icon']);

Si vous souhaitez télécharger une icône pour l’entité, vous pouvez utiliser la vue entity/edit/icon. Cette vue affiche une entrée de fichier pour le téléchargement d’une nouvelle icône pour l’entité, une miniature de l’icône actuelle et l’option de supprimer l’icône actuelle.

La vue prend en charge quelques variables pour contrôler la sortie

  • entity - l’entité pour laquelle ajouter ou supprimer l’icône. Si elle est fournie, la miniature et l’option de suppression s’affichent en fonction de cette entité

  • entity_type - le type d’entité pour lequel l’icône sera téléchargée. Des plugins pourraient trouver cela utile, peut-être pour valider les dimensions d’icônes

  • entity_subtype - le sous-type d’entité pour lequel l’icône sera téléchargée. Des plugins pourraient trouver cela utile, peut-être pour valider les dimensions d’icônes

  • icon_type - le type de l’icône (par défaut : icon)

  • name - le nom du champ input/file (par défaut : icon)

  • remove_name - nom de la bascule de suppression de l’icône (par défaut : $vars[“name”] . “_remove”)

  • required - est-ce que le téléchargement d’une icône est requis (par défaut : false)

  • cropper_enabled - le recadrage des icônes est-il autorisé (par défaut : true)

  • show_remove - affiche l’option pour supprimer l’icône (par défaut : true)

  • show_thumb - afficher la miniature de l’entité si elle est disponible (par défaut : true)

  • thumb_size - la taille de l’icône à utiliser pour la miniature (par défaut : medium)

Si vous utilisez la vue, vous pouvez utiliser le code suivant dans votre action pour enregistrer l’icône dans l’entité ou supprimer l’icône actuelle.

if (get_input('icon_remove')) {
   $entity->deleteIcon();
} else {
   $entity->saveIconFromUploadedFile('icon');
}

Définissez le type d’encodage du formulaire sur multipart/form-data :

echo elgg_view_form('example', array(
  'enctype' => 'multipart/form-data'
));

Note

Le type d’encodage enctype de tous les formulaires qui utilisent la méthode POST est par défaut multipart/form-data.

Dans votre fichier d’action, utilisez elgg_get_uploaded_file('nom-de-votre-input') pour accéder au fichier téléchargé :

$icon = elgg_get_uploaded_file('icon');

Formulaires persistants

Les formulaires persistants sont des formulaires qui conservent les entrées de l’utilisateur en cas d’échec de l’enregistrement. Ils sont « persistants » parce que les données de l’utilisateur « persistent » dans le formulaire après soumission, bien qu’elles n’aient jamais été enregistrées dans la base de données. Cela améliore considérablement l’expérience utilisateur en minimisant la perte de données. Elgg inclut des fonctions d’assistance afin que vous puissiez rendre n’importe quel formulaire persistant.

Fonctions d’assistance

Les formulaires persistants sont implémentés dans Elgg par les fonctions suivantes :

  • elgg_make_sticky_form($name) - Indique au moteur de persister les valeurs de toutes les entrées d’un formulaire.

  • elgg_clear_sticky_form($name) - Indique au moteur de supprimer toutes les valeurs d’un formulaire.

  • elgg_is_sticky_form($name) - Vérifie si $name est un formulaire persistant valide.

  • elgg_get_sticky_values($name) - Renvoie toutes les valeurs persistantes enregistrées pour $name par elgg_make_sticky_form($name).

Aperçu

Le déroulement de l’utilisation des formulaires persistants est :

  1. Appelez elgg_make_sticky_form($name) au début des actions des formulaires que vous voulez rendre persistants.

  2. Utilisez elgg_is_sticky_form($name) et elgg_get_sticky_values($name) pour obtenir des valeurs persistantes lors du rendu d’une vue de formulaire.

  3. Appelez elgg_clear_sticky_form($name) une fois l’action terminée avec succès ou après que les données ont été chargées par elgg_get_sticky_values($name).

Note

Depuis Elgg 5.0, les formulaires rendus avec elgg_view_form() peuvent définir l’indicateur $form_vars['sticky_enabled'] = true pour obtenir automatiquement la prise en charge des formulaires persistants. Les valeurs soumises à l’action seront automatiquement renseignées dans $body_vars lorsqu’une erreur survient dans l’action.

elgg_view_form() prend en charge les $form_vars suivants pour faciliter la prise en charge des formulaires persistants :

  • sticky_enabled : un bool pour activer la prise en charge automatique de formulaire persistant

  • sticky_form_name : une string facultative pour définir l’endroit où les valeurs du formulaire collant sont enregistrées. La valeur par défaut est $action_name et ne doit être modifiée que si $action_name est différent de l’action réelle

  • sticky_ignored_fields: an array with the names fo the form fields that should be saved. For example password fields

Exemple : Inscription d’un utilisateur

Les formulaires persistants simples nécessitent peu de logique pour déterminer les valeurs d’entrée du formulaire. Cette logique est placée en haut de la vue du corps de formulaire.

L’affichage du formulaire d’inscription définit d’abord les valeurs par défaut pour les entrées, puis vérifie s’il y a des valeurs persistantes. Si c’est le cas, il charge les valeurs persistantes avant de supprimer le formulaire persistant :

// views/default/forms/register.php
$password = $password2 = '';
$username = get_input('u');
$email = get_input('e');
$name = get_input('n');

if (elgg_is_sticky_form('register')) {
   extract(elgg_get_sticky_values('register'));
   elgg_clear_sticky_form('register');
}

Les ensembles d’action d’inscription créent le formulaire persistant et l’effacent une fois l’action terminée :

// actions/register.php
elgg_make_sticky_form('register', ['password', 'password2']);

elgg_register_user([
   'username' => $username,
   'password' => $password,
   'name' => $name,
   'email' => $email,
]);

elgg_clear_sticky_form('register');

Astuce

La fonction elgg_make_sticky_form() supporte un second argument optionnel $ignored_field_names. Cela doit être un array des noms de champs que vous ne souhaitez pas rendre persistants. Ceci est utile pour les champs qui contiennent des données sensibles, comme les mots de passe.

Exemple : Signets - Bookmarks

Le formulaire et l’action d’enregistrement du plugin « Bookmarks » inclus est un exemple de formulaire persistant complexe.

La vue du formulaire pour l’action d’enregistrement d’un signet utilise elgg_extract() pour extraire des valeurs du tableau $vars :

// mod/bookmarks/views/default/forms/bookmarks/save.php
$title = elgg_extract('title', $vars, '');
$desc = elgg_extract('description', $vars, '');
$address = elgg_extract('address', $vars, '');
$tags = elgg_extract('tags', $vars, '');
$access_id = elgg_extract('access_id', $vars, ACCESS_DEFAULT);
$container_guid = elgg_extract('container_guid', $vars);
$guid = elgg_extract('guid', $vars, null);
$shares = elgg_extract('shares', $vars, array());

Les scripts du gestionnaire de page activent le support des formulaires persistants en passant les valeurs correctes à elgg_view_form() :

// mod/bookmarks/pages/add.php
$content = elgg_view_form('bookmarks/save', ['sticky_enabled' => true]);

De même, mod/bookmarks/pages/edit.php utilise le même support persistant, mais transmet l’entité qui est en cours d’édition :

$bookmark_guid = get_input('guid');
$bookmark = get_entity($bookmark_guid);

...

$content = elgg_view_form('bookmarks/save', ['sticky_enabled' => true], ['entity' => $bookmark]);

Le plugin a un écouteur d’événement sur l’événement 'form:prepare:fields', 'bookmarks/save' et le gestionnaire fait 2 choses :

  1. Définit les noms et les valeurs par défaut pour les champs de formulaire.

  2. Extrait les valeurs d’un objet bookmark s’il est passé.

// mod/bookmarks/classes/Elgg/Bookmarks/Forms/PrepareFields.php
/**
      * Prepare the fields for the bookmarks/save form
      *
      * @since 5.0
      */
     class PrepareFields {

             /**
              * Prepare fields
              *
              * @param \Elgg\Event $event 'form:prepare:fields', 'bookmarks/save'
              *
              * @return array|null
              */
             public function __invoke(\Elgg\Event $event): ?array {
                     $vars = $event->getValue();

                     // input names => defaults
                     $values = [
                             'title' => get_input('title', ''), // bookmarklet support
                             'address' => get_input('address', ''),
                             'description' => '',
                             'access_id' => ACCESS_DEFAULT,
                             'tags' => '',
                             'container_guid' => elgg_get_page_owner_guid(),
                             'guid' => null,
                     ];

                     $bookmark = elgg_extract('entity', $vars);
                     if ($bookmark instanceof \ElggBookmark) {
                             // load current bookmark values
                             foreach (array_keys($values) as $field) {
                                     if (isset($bookmark->$field)) {
                                             $values[$field] = $bookmark->$field;
                                     }
                             }
                     }

                     return array_merge($vars, $values);
             }
     }

L’action de sauvegarde n’a pas besoin de faire quoi que ce soit avec la prise en charge des formulaires persistants car tout est géré par le système.

Ajax

Voir le guide Ajax pour savoir comment appeler les actions depuis JavaScript.

Sécurité

Pour une sécurité renforcée, toutes les actions nécessitent un jeton CSRF. Les appels aux URLs d’action qui n’incluent pas les jetons de sécurité seront ignorés et un avertissement sera généré.

Quelques vues et fonctions génèrent automatiquement des jetons de sécurité :

elgg_view('output/url', array('is_action' => true));
elgg_view('input/securitytoken');
$url = elgg_add_action_tokens_to_url("http://localhost/elgg/action/example");
$url = elgg_generate_action_url('myplugin/myaction');

Dans de rares cas, vous devrez peut-être générer des jetons manuellement :

$__elgg_ts = elgg()->csrf->getCurrentTime()->getTimestamp();
$__elgg_token = elgg()->csrf->generateActionToken($__elgg_ts);

Vous pouvez aussi accéder aux jetons depuis JavaScript :

elgg.security.token.__elgg_ts;
elgg.security.token.__elgg_token;

Ces jetonsci sont rafraîchis périodiquement aussi ils devraient être à jour.

Jetons de sécurité

À l’occasion, nous devons transmettre des données par l’intermédiaire d’une tierce partie non digne de confiance ou générer un jeton impossible à deviner basé sur certaines données. L’algorithme HMAC est le bon outil pour cela. Il nous permet de vérifier que les données reçues ont été générées par notre site, et n’ont pas été trafiquées. Notez que même les fonctions de hachage fortes comme SHA-2 ne devraient pas être utilisés sans HMAC pour ces tâches.

Elgg fournit elgg_build_hmac() pour générer et valider des codes d’authentification des messages HMAC qui ne puissent pas être devinés sans la clé privée du site.

// generate a querystring such that $a and $b can't be altered
$a = 1234;
$b = "hello";
$query = http_build_query([
    'a' => $a,
    'b' => $b,
    'mac' => elgg_build_hmac([$a, $b])->getToken(),
]);
$url = "action/foo?$query";

// validate the querystring
$a = (int) get_input('a', '', false);
$b = (string) get_input('b', '', false);
$mac = get_input('mac', '', false);

if (elgg_build_hmac([$a, $b])->matchesToken($mac)) {
    // $a and $b have not been altered
}

Remarque : si vous utilisez une non-chaîne comme données HMAC, vous devez utiliser les types de manière cohérente. Considérez ce qui suit :

$mac = elgg_build_hmac([123, 456])->getToken();

// type of first array element differs
elgg_build_hmac(["123", 456])->matchesToken($mac); // false

// types identical to original
elgg_build_hmac([123, 456])->matchesToken($mac); // true

URLs signées

Les URL signées offrent un niveau de sécurité limité pour les situations où les jetons d’action ne conviennent pas, par exemple lors de l’envoi d’un lien de confirmation par e-mail. Les signatures de l’URL vérifient que l’URL a été générée par votre installation Elgg (en utilisant le secret du site) et que les éléments de l’URL de requête n’ont pas été trafiqués.

URLs signées avec une clef SHA-256 HMAC impossible à deviner. Pour plus de détails, consultez Jetons de Sécurité .

$url = elgg_http_add_url_query_element(elgg_normalize_url('confirm'), [
   'user_guid' => $user_guid,
]);

$url = elgg_http_get_signed_url($url);

notify_user($user_guid, $site->guid, 'Confirm', "Please confirm by clicking this link: $url");

Avertissement

Les URL signées n’offrent pas de protection CSRF et ne doivent pas être utilisées à la place des jetons d’action.