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 :
Contenu
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);
Où $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 :
Il définit l’action vers l’URL appropriée, sur la base du nom de l’action que vous lui avez passé
Il ajoute des jetons anti-csrf (
__elgg_ts
et__elgg_token
) pour aider à garder vos actions sûresIl 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 WYSIWYGinput/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 nominput/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 Elgginput/tags
- affiche une entrée de type tagsinput/autocomplete
- affiche un sélecteur d’entités Elgginput/captcha
- vue destinée à être étendue par des pluginsinput/friendspicker
- affiche un sélecteur de contact Elgg par autocomplétioninput/userpicker
- affiche un sélecteur d’utilisateur Elgg avec autocomplétioninput/grouppicker
- affiche un sélecteur de groupe Elgg par autocomplétioninput/objectpicker
- affiche un sélecteur d’objet Elgg par autocomplétioninput/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ônesentity_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ônesicon_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
parelgg_make_sticky_form($name)
.
Aperçu
Le déroulement de l’utilisation des formulaires persistants est :
Appelez
elgg_make_sticky_form($name)
au début des actions des formulaires que vous voulez rendre persistants.Utilisez
elgg_is_sticky_form($name)
etelgg_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 parelgg_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
: unbool
pour activer la prise en charge automatique de formulaire persistantsticky_form_name
: unestring
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éellesticky_ignored_fields
: anarray
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 :
Définit les noms et les valeurs par défaut pour les champs de formulaire.
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.