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.. Utilisez elgg_register_action pour cela :

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.

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.

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" pour le dernier paramètre :

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

Écrire des fichiers d’action

Utilisez la fonction get_input pour avoir accès aux paramètres de requête :

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

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

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' => $user->getFriends(['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 une action, Elgg déclenche un hook :

$result = elgg_trigger_plugin_hook('action', $action, null, true);

$action est l’action qui est appelée. Si le hook retourne la valeur false alors l’action n’est pas exécutée.

Exemple : Captcha

Le module captcha l’utilise pour intercepter les actions register et user/requestnewpassword et les rediriger vers une fonction qui vérifie le code captcha. Cette vérification renvoie true si valide ou false si ce n’est pas le cas (ce qui empêche l’action associée d’être exécutée).

Ceci est fait comme suit :

elgg_register_plugin_hook_handler("action", "register", "captcha_verify_action_hook");
elgg_register_plugin_hook_handler("action", "user/requestnewpassword", "captcha_verify_action_hook");

...

function captcha_verify_action_hook($hook, $entity_type, $returnvalue, $params) {
  $token = get_input('captcha_token');
  $input = get_input('captcha_input');

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

  register_error(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 groupé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' => "action/entity/delete?guid=$guid&forward_url=$forward_url",
   'confirm' => true,
));

Vous pouvez personnaliser les clés du message de succès pour votre type d’entité et votre sous-type, en utilisant les clefs "entity:$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 array(
   '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 function 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 action 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. Voir les fichiers individuels des vues pour une liste de paramètres acceptés.

echo elgg_view('input/select', array(
   'required' => true,
   'name' => 'status',
   'options_values' => array(
      '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-dropdown">
   <option value="draft">Draft</option>
   <option value="published">Published</option>
</select>

Pour assurer la cohérence du balisage du champ, utilisez les paramètres 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(array(
   '#type' => 'select',
   '#label' => elgg_echo('blog:status:label'),
   '#help' => elgg_view_icon('help') . elgg_echo('blog:status:help'),
   'required' => true,
   'name' => 'status',
   'options_values' => array(
      '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>
   <select required="required" name="status" data-rel="blog" id="elgg-field-1" class="elgg-input-dropdown">
      <option value="draft">Draft</option>
      <option value="published">Published</option>
   </select>
   <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 groupés :

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

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

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

  • input/url - affiche une entrée de type adresse web (URL) <input type="url">

  • input/email - affiche une entrée de type email <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(s) <input type="radio">

  • input/submit - affiche un bouton d’envoi de formulaire <input 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

  • 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 contacts Elgg

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

  • 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’, array(‘name’ => ‘icon’));

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

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

Dans votre fichier d’action, utilisez la variable globale $_FILES pour accéder au fichier téléchargé :

$icon = $_FILES[‘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 1.8 inclut des fonctions d’aide afin que vous puissiez rendre n’importe quelle formulaire persistant.

Fonctions pratiques

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

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

elgg_clear_sticky_form($name) - Indique au moteur de supprimer toutes les valeurs persistantes 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 flux de base de l’utilisation des formulaires persistants est : Appelez elgg_make_sticky_form($name) en haut des actions pour les formulaires que vous voulez rendre persistants. 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. 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).

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');

...

$guid = register_user($username, $password, $name, $email, false, $friend_guid, $invitecode);

if ($guid) {
     elgg_clear_sticky_form('register');
     ....
}

Exemple : Signets (bookmarks)

Le formulaire et l’action d’enregistrement du plugin Bookmarks (Signets) 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 préparent les variables de formulaire et appellent elgg_view_form() en passant les valeurs correctes :

// mod/bookmarks/pages/add.php
$vars = bookmarks_prepare_form_vars();
$content = elgg_view_form('bookmarks/save', array(), $vars);

De même, mod/bookmarks/pages/edit.php utilise la même fonction, mais passe l’entité qui est en cours de modification comme argument :

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

...

$vars = bookmarks_prepare_form_vars($bookmark);
$content = elgg_view_form('bookmarks/save', array(), $vars);

Le fichier de bibliothèque définit bookmarks_prepare_form_vars(). Cette fonction accepte une ElggEntity comme argument et fait 3 choses :

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

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

  3. Extrait les valeurs d’un formulaire persistant si il existe.

TODO : inclure directement à partir de lib/bookmarks.php

// mod/bookmarks/lib/bookmarks.php
function bookmarks_prepare_form_vars($bookmark = null) {
     // input names => defaults
  $values = array(
    'title' => get_input('title', ''), // bookmarklet support
    'address' => get_input('address', ''),
    'description' => '',
    'access_id' => ACCESS_DEFAULT,
    'tags' => '',
    'shares' => array(),
    'container_guid' => elgg_get_page_owner_guid(),
    'guid' => null,
    'entity' => $bookmark,
  );

  if ($bookmark) {
       foreach (array_keys($values) as $field) {
       if (isset($bookmark->$field)) {
         $values[$field] = $bookmark->$field;
       }
    }
  }

  if (elgg_is_sticky_form('bookmarks')) {
       $sticky_values = elgg_get_sticky_values('bookmarks');
       foreach ($sticky_values as $key => $value) {
      $values[$key] = $value;
    }
  }

  elgg_clear_sticky_form('bookmarks');

  return $values;
}

L’action save vérifie l’entrée, puis efface le formulaire persistant en cas de réussite :

// mod/bookmarks/actions/bookmarks/save.php
elgg_make_sticky_form('bookmarks');
...

if ($bookmark->save()) {
     elgg_clear_sticky_form('bookmarks');
}

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");

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

$__elgg_ts = time();
$__elgg_token = generate_action_token($__elgg_ts);

Vous pouvez aussi accéder aux jetons par javascript :

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

Ceux-ci 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és. 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 peuvent être pas 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 des 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 requête URL 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.