Routage

Elgg dispose de deux mécanismes pour répondre aux requêtes HTTP qui ne passent pas déjà par les systèmes des Actions et du Simplecache - Cache simple.

Identifiant d’URL et segments

Après avoir retiré l’URL du site, Elgg découpe le chemin de l’URL par / dans un tableau. Le premier élément, l’identificateur, est mis de côté, et les éléments restants sont appelés les segments. Par exemple, si l’URL du site est http://example.com/elgg/, l’URL http://example.com/elgg/blog/owner/jane?foo=123 produit :

Identificateur : 'blog'. Segments : ['owner', 'jane']. (les paramètres de la chaîne de requête sont disponibles via get_input())

L’URL du site (page d’accueil) est un cas spécial qui produit un identificateur de chaîne vide et un tableau de segments vide.

Avertissement

Les identificateurs/segments d’URL doivent être considérés comme des entrées utilisateur potentiellement dangereuses. Elgg utilise dessus htmlspecialchars pour échapper les entités HTML.

Gestion des pages

Elgg offre des outils pour gérer les pages de votre plugin via des routes personnalisées, permettant des URLs comme http://yoursite/my_plugin/section. Vous pouvez enregistrer une nouvelle route en utilisant elgg_register_route() ou via la configuration routes dans elgg-plugin.php. Les routes assurent la correspondance avec les vues de ressources, où vous pouvez générer le contenu de la page.

// in your 'init', 'system' handler
elgg_register_route('my_plugin:section' [
        'path' => '/my_plugin/section/{guid}/{subsection?}',
        'resource' => 'my_plugin/section',
        'requirements' => [
                'guid' => '\d+',
                'subsection' => '\w+',
        ],
]);

// in my_plugin/views/default/resources/my_plugin/section.php
$guid = elgg_extract('guid', $vars);
$subsection = elgg_extract('subsection', $vars);

// render content

Dans l’exemple ci-dessus, nous avons enregistré une nouvelle route qui est accessible via http://yoursite/my_plugin/section/<guid>/<subsection>. Chaque fois que cette route est accédée avec le segment requis guid et un segment facultatif subsection, le routeur va afficher la vue de la ressource spécifiée my_plugin/section et transmettre les paramètres extraits de l’URL à votre vue de ressource via $vars.

Noms des routes

Les noms de routes peuvent ensuite être utilisés pour générer une URL :

$url = elgg_generate_url('my_plugin:section', [
        'guid' => $entity->guid,
        'subsection' => 'assets',
]);

Les noms d’itinéraires sont uniques pour l’ensemble des plugins et pour le noyau, de sorte qu’un autre plugin peut surcharger une route en enregistrant d’autres paramètres sur le même nom de route.

Les noms des routes respectent un certain formalisme et seront, dans certains cas, utilisés pour résoudre automatiquement les URLs, par exemple pour afficher une entité.

Les conventions suivantes sont utilisées dans le noyau et recommandées pour les plugins :

view :<entity_type>:<entity_subtype>

Correspond à la page de profil de l’entité, par exemple view:user:user ou view:object:blog. Le chemin d’accès doit contenir un guid, ou un username pour les utilisateurs

edit:<entity_type>:<entity_subtype>

Correspond au formulaire pour modifier l’entité, par exemple edit:user:user ou edit:object:blog. Le chemin d’accès doit contenir un guid, ou un username pour les utilisateurs. Si vous avez besoin d’ajouter des sous-ressources, utilisez des suffixes, par exemple edit:object:blog:images, en gardant au moins une sous-ressource sans suffixe par défaut.

add:<entity_type>:<entity_subtype>

Correspond au formulaire pour ajouter une nouvelle entité d’un type donné, par exemple add:object:blog. Le chemin d’accès, en règle générale, contient le paramètre container_guid

collection:<entity_type>:<entity_subtype>:<collection_type>

Correspond aux pages de listes. Les noms de routes couramment utilisés dans le noyau sont les suivants :

  • collection:object:blog:all : liste tous les articles de blog

  • collection:object:blog:owner : liste les articles de blog appartenant à un utilisateur avec un identifiant donné

  • collection:object:blog:friends : liste les articles de blog appartenant à des contacts de l’utilisateur connecté (ou d’un utilisateur avec un identifiant donné)

  • collection:object:blog:group : liste les articles de blog dans un groupe

default:<entity_type>:<entity_subtype>

Fait correspondre à la page par défaut pour une ressource, par exemple pour le chemin d’accès /blog. Elgg utilise la collection « all » pour ces routes.

  • default:object:blog : gère le chemin générique /blog.

<entity_subtype> peut être omis des noms de routes pour enregistrer des routes globales applicables à toutes les entités d’un type donné. Le générateur d’URL va d’abord essayer de générer une URL à l’aide du sous-type, puis se replie sur un nom de route sans sous-type. Par exemple, les profils utilisateur sont acheminés vers la même vue de ressource quel que soit le sous-type utilisateur.

elgg_register_route('view:object:attachments', [
        'path' => '/attachments/{guid}',
        'resource' => 'attachments',
]);

elgg_register_route('view:object:blog:attachments', [
        'path' => '/blog/view/{guid}/attachments',
        'resource' => 'blog/attachments',
]);

$blog = get_entity($blog_guid);
$url = elgg_generate_entity_url($blog, 'view', 'attachments'); // /blog/view/$blog_guid/attachments

$other = get_entity($other_guid);
$url = elgg_generate_entity_url($other, 'view', 'attachments'); // /attachments/$other_guid

Configuration du routage

Les segments peuvent être définis à l’aide de jokers, par exemple profile/{username}, qui correspond à toutes les URL qui contiennent profile/ suivies d’un nom d’utilisateur arbitraire.

Pour rendre un segment facultatif, vous pouvez ajouter un ? (point d’interrogation) au nom du joker, par exemple profile/{username}/{section?}. Dans ce cas, l’URL va correspondre même si le segment section n’est pas fourni.

Vous pouvez contraindre davantage les segments en utilisant les exigences regex :

// elgg-plugin.php
return [
        'routes' => [
                'profile' => [
                        'path' => '/profile/{username}/{section?}',
                        'resource' => 'profile',
                        'requirements' => [
                                'username' => '[\p{L}\p{Nd}._-]+', // only allow valid usernames
                                'section' => '\w+', // can only contain alphanumeric characters
                        ],
                        'defaults' => [
                                'section' => 'index',
                        ],
                ],
        ]
];

Par défaut, Elgg va définir les exigences suivantes pour les segments d’URL nommés :

$patterns = [
        'guid' => '\d+', // only digits
        'group_guid' => '\d+', // only digits
        'container_guid' => '\d+', // only digits
        'owner_guid' => '\d+', // only digits
        'username' => '[\p{L}\p{Nd}._-]+', // letters, digits, underscores, dashes
];

Routes dépendant d’un plugin

Si une route a besoin qu’un plugin spécifique soit actif, cela peut être configuré dans la configuration de la route.

// elgg-plugin.php
return [
        'routes' => [
                'collection:object:blog:friends' => [
                        'path' => '/blog/friends/{username?}/{lower?}/{upper?}',
                        'resource' => 'blog/friends',
                        'required_plugins' => [
                                'friends', // route only allowed when friends plugin is active
                        ],
                ],
        ]
];

Middleware de routage

Les middlewares de gestion du routage peuvent être utilisés pour empêcher l’accès à une certaine route, ou pour exécuter une certaine logique avant que la route ne soit rendue. Un middleware peut être utilisé, par exemple pour implémenter un système d’inscription payante, ou pour définir des métadonnées open graph.

Le noyau de Elgg implémente plusieurs gestionnaires de middleware. Le middleware suivant se trouve dans l’espace de noms \Elgg\Router\Middleware :

Gatekeeper

Ce gestionnaire d’accès empêchera l’accès des utilisateurs non authentifiés.

AdminGatekeeper

Ce gestionnaire d’accès empêchera l’accès des utilisateurs non-administrateurs.

LoggedOutGatekeeper

Ce gestionnaire d’accès empêchera l’accès à tout utilisateur authentifié.

AjaxGatekeeper

Ce gestionnaire d’accès empêchera l’accès à toute demande non-XHR.

PageOwnerGatekeeper

Ce contrôleur d’accès empêchera l’accès s’il n’y a pas d’entité propriétaire de la page.

GroupPageOwnerGatekeeper

Ce contrôleur d’accès étend PageOwnerGatekeeper et requiert également que le propriétaire de la page soit une entité ElggGroup.

UserPageOwnerGatekeeper

Ce contrôleur d’accès étend le PageOwnerGatekeeper et requiert également que le propriétaire de la page soit une entité ElggUser.

PageOwnerCanEditGatekeeper

Ce contrôleur d’accès bloquera l’accès si aucun propriétaire de page n’est détecté et que le propriétaire de la page ne peut pas être modifié.

GroupPageOwnerCanEditGatekeeper

Ce gestionnaire d’accès étend PageOwnerCanEditGatekeeper mais exige également que le propriétaire de la page soit une entité ElggGroup.

UserPageOwnerCanEditGatekeeper

Ce gestionnaire d’accès étend PageOwnerCanEditGatekeeper mais exige également que le propriétaire soit une entité ElggUser.

CsrfFirewall

Ce middleware empêchera l’accès sans les jetons CSRF corrects. Ce middleware sera automatiquement appliqué aux actions.

ActionMiddleware

Ce middleware fournira une logique liée à l’action. Ce middleware sera automatiquement appliqué aux actions.

SignedRequestGatekeeper

Ce gestionnaire d’accès empêchera l’accès si l’URL a été trafiquée. Une URL sécurisée peut être générée à l’aide de la fonction elgg_http_get_signed_url.

UpgradeGatekeeper

Ce gestionnaire d’accès empêchera l’accès si l’URL de mise à niveau est sécurisée et que l’URL n’est pas valide.

WalledGarden

Ce middleware empêchera l’accès à une route si le site est configuré uniquement pour les utilisateurs authentifiés et qu’aucun utilisateur authentifié n’est connecté. Ce middleware est automatiquement activé pour toutes les routes. Vous pouvez désactiver le gardien du walled garden avec une option de configuration de route.

Middleware personnalisé

Les gestionnaires de middleware peuvent être définis par n’importe quelle fonction appellable qui reçoit une instance de \Elgg\Request : le gestionnaire devrait lancer une instance de \Elgg\Exceptions\HttpException pour empêcher l’accès à la route. Le gestionnaire peut renvoyer une instance de \Elgg\Http\ResponseBuilder pour empêcher la poursuite de la mise en œuvre de la séquence de routage (une réponse de redirection peut être renvoyée pour réacheminer la requête).

class MyMiddleware {

        public function __invoke(\Elgg\Request $request) {
                $entity = $request->getEntityParam();
                if ($entity) {
                        // do stuff
                } else {
                        throw new EntityNotFoundException();
                }
        }
}

elgg_register_route('myroute', [
        'path' => '/myroute/{guid?}',
        'resource' => 'myroute',
        'middleware' => [
                \Elgg\Router\Middleware\Gatekeeper::class,
                MyMiddleware::class,
        ]
]);

Contrôleurs de routage

Dans certains cas, l’utilisation des vues de ressources n’est pas appropriée. Dans ces cas, vous pouvez utiliser un contrôleur - toute fonction appelable qui reçoit une instance de \Elgg\Request :

class MyController {

        public function handleFoo(\Elgg\Request $request) {
                elgg_set_http_header('Content-Type: application/json');
                $data = [
                        'entity' => $request->getEntityParam(),
                ];
                return elgg_ok_response($data);
        }

}

elgg_register_route('myroute', [
        'path' => '/myroute/{guid?}',
        'controller' => [MyController::class, 'handleFoo'],
]);

L’événement route:rewrite

Pour la réécriture d’URL, l’événement route:rewrite (avec des arguments similaires à route) est déclenché très tôt, et permet de modifier le chemin de l’URL de la requête (par rapport au site Elgg).

Ici, nous réécrivons les requêtes de news/* vers blog/* :

function myplugin_rewrite_handler(\Elgg\Event $event) {
    $value = $event->getValue();

    $value['identifier'] = 'blog';

    return $value;
}

elgg_register_event_handler('route:rewrite', 'news', 'myplugin_rewrite_handler');

Avertissement

L’événement doit être enregistré directement dans la fonction boot du Bootstrap de votre plugin. La fonction init arrive trop tard.

Aperçu du routage

Pour les pages standard, le flux de programme de Elgg est quelque chose comme ceci :

  1. Un utilisateur demande http://example.com/news/owner/jane.

  2. Les plugins sont initialisés.

  3. Elgg analyse l’URL pour l’identificateur news et les segments ['owner', 'jane'].

  4. Elgg déclenche l’événement route:rewrite, news (voir ci-dessus).

  5. Elgg trouve une route enregistrée qui correspond au chemin d’accès final de la route, et affiche la vue de ressource qui lui est associée, en appellant elgg_view_resource('blog/owner', $vars), où $vars contient le nom d’utilisateur.

  6. La vue resources/blog/owner obtient le nom d’utilisateur via $vars['username'] et utilise de nombreuses autres vues et fonctions de mise en forme telles que elgg_view_layout() et elgg_view_page() pour créer la page HTML complète.

  7. PHP invoque la séquence d’arrêt de Elgg.

  8. L’utilisateur reçoit une page complète.

Les normes de codage de Elgg suggèrent une disposition d’URL particulière, mais il n’y a pas de syntaxe obligatoire.