Personnaliser le menu d'administration de Drupal 8

le menu d'un café

Drupal 8 dispose nativement d'une barre d'outils responsive permettant d'administrer le site. Cette barre d'outils contient le menu principal d'administration ainsi que l'accès aux raccourcis et au compte utilisateur, et si elle s'avère très utile à l'administration et conception du site (surtout complété par le module admin toolbar qui améliore son comportement avec quelques règles css), elle reste peu utile aux utilisateurs et gestionnaires de contenus d'un site.

Barre d'outil Drupal 8

Nous allons découvrir dans ce billet comment étendre et utiliser cette barre d'outils pour offrir des menus de gestion des contenus selon différents profils d'utilisateurs, depuis un simple contributeur ou rédacteur de contenu à un webmestre en charge de la gestion de tous les contenus et/ou de leur publication.

Le principe est simple. Nous allons créer des menus dédiés aux différentes tâches de gestion ou de configuration, menus qui nous permettront de gérer les liens mis à disposition des différents profils d'utilisateur, puis nous chargerons ces menus dans cette barre d'administration.

Création des menus de gestion

Nous avons la possibilité de créer manuellement les différents menus qui vont nous servir de support pour alimenter notre barre d'outils. Mais nous pouvons aussi les créer automatiquement depuis un module. Cette dernière méthode va nous permettre de pouvoir rajouter une option sur les menus créés : nous pouvons les verrouiller ce qui empêchera leur suppression malheureuse.

Nous allons créer donc un petit module, que nous intitulerons my_bo qui sera chargé de fournir ces menus, et qui ira les parcourir pour alimenter notre barre d'outils personnalisée.

Après avoir créer votre répertoire my_bo, et votre fichier my_bo.info.yml pour déclarer le module, nous allons créer un menu automatiquement à l'installation de notre module.

Pour ce faire, nous créons un répertoire config/install dans notre module et y ajoutons le fichier system.menu.edition.yml qui va créer automatiquement le menu Edition (nom système : edition)

# File config/install/system.menu.edition.yml

langcode: fr
status: true
dependencies: {  }
id: edition
label: Edition
description: 'Menu de gestion des contenus du Back office'
locked: true

Vous pouvez ajouter autant de menus que nécessaires, par un exemple un menu pour donner des accès personnalisés à la gestion des utilisateur, un menu pour personnaliser les accès à certaines pages de configuration, etc. Dans notre exemple, nous allons nous contenter d'un seul menu.

Après avoir installé votre nouveau module, vous devriez avoir votre nouveau menu disponible.

Un menu Drupal verrouillé

Notez que vous ne disposez pas de l'option "Supprimer le menu". Vous pouvez obtenir un résultat similaire avec la gestion de la configuration native à Drupal 8. Pour ce, il suffit de créer un menu manuellement, puis d'exporter la configuration, de rajouter la clé locked: true dans la configuration du menu, puis de réimporter la configuration dans la foulée.

Création des entrées dans la barre d'outils

Pour créer nos nouvelles entrées dans la barre d'outils, nous devons implémenter hook_toolbar() dans notre module. Dans le fichier my_bo.module, nous allons donc implémenter ce hook.

/**
 * Implements hook_toolbar().
 */
function my_bo_toolbar() {
  $items = [];
  $items['my_bo_toolbar_content'] = array(
    '#type' => 'toolbar_item',
    'tab' => array(
      '#type' => 'link',
      '#title' => t('Edition'),
      '#url' => Url::fromRoute('<front>'),
      '#attributes' => array(
        'title' => t("Content manageemnt"),
        'class' => array('toolbar-icon', 'toolbar-icon-edit'),
      ),
    ),
    'tray' => array(
      '#heading' => t('Content management'),
      '#attached' => array(
        'library' => [
          'admin_toolbar/toolbar.tree',
        ],
      ),
      'toolbar_my_bo_toolbar_content' => array(
        '#pre_render' => array(
          'my_bo_toolbar_content_pre_render',
        ),
        '#type' => 'container',
        '#attributes' => array(
          'class' => array('toolbar-menu-administration'),
        ),
      ),
    ),
    '#weight' => 90,
  );

  return $items;
}

Nous créons ici un nouvel item my_bo_toolbar_content dans la barre d'outils, et lui associons un "tiroir" (tray) qui contiendra nos différentes entrées de menu. Notez ici que nous utilisons des icônes pré-existantes fournies par le module toolbar, mais vous pouvez très bien créer et utiliser vos propres icônes.

Nous déclarons pour le tray associé à notre entrée de la barre d'outil une fonction pre_render my_bo_toolbar_content_pre_render, qui va se charger de lire le menu précédemment créé et de charger tous les liens qui y sont déclarés.

Voici à quoi peut ressembler notre fonction.

function my_bo_toolbar_content_pre_render($element) {
  /** @var MyToolbar $service */
  $service = Drupal::service('my_bo.toolbar.menu');
  $service->setCacheContext(['user.roles']);
  $service->setMenuName('edition');
  $element = $service->populate($element);
  return $element;
}

 

Et c'est terminé ! Nous disposons d'une nouvelle entrée dans notre barre d'outil qui sera alimentée depuis le menu Edition. Et les utilisateurs, qui disposent bien sûr des droits pour accéder à chaque lien, pourront alors les utiliser dans leur activité quotidienne de gestion des contenus et/ou utilisateurs du site.

 

Une barre d'outil Drupal sur mesure

 

Enfin, c'est presque terminé au sujet de l'article. Car vous aurez pu constater que nous utilisons un service pour alimenter notre barre d'outil. Découvrons rapidement ce petit service qui nous permet de charger un menu et de restituer les différents liens le constituant.

Déclarons notre service dans notre module, au moyen du fichier my_bo.services.yml

#File my_bo.services.yml

services:
  my_bo.toolbar.menu:
    class: Drupal\my_bo\Service\MyToolbar
    arguments: ["@toolbar.menu_tree"]
    tags:
      - { name: backend_overridable }

Et notre Class MyToolbar déclarée dans le fichier MyToolbar.php

<?php

# File src/Services/MyToolbar.php

namespace Drupal\my_bo\Service;

use Drupal\Core\Menu\MenuLinkTreeInterface;
use Drupal\Core\Menu\MenuTreeParameters;

class MyToolbar {

  /**
   * @var MenuLinkTreeInterface
   */
  protected $menuTree;

  /**
   * @var string
   */
  protected $menuName = NULL;

  /**
   * @var string[]
   */
  protected $cacheContext = [];

  /**
   * Constructor.
   *
   * @param MenuLinkTreeInterface $menuLinkTreeInterface
   *   The menu link tree.
   */
  public function __construct(MenuLinkTreeInterface $menuLinkTreeInterface) {
    $this->menuTree = $menuLinkTreeInterface;
  }

  /**
   *
   * @inheritDoc
   */
  public function populate(array $element) {
    $parameters = new MenuTreeParameters();
    $parameters->excludeRoot()->onlyEnabledLinks();
    $tree = $this->menuTree->load($this->menuName, $parameters);

    if (empty($tree)) {
      return $element;
    }

    $manipulators = array(
        array('callable' => 'menu.default_tree_manipulators:checkAccess'),
        array('callable' => 'menu.default_tree_manipulators:generateIndexAndSort'),
    );

    $tree = $this->menuTree->transform($tree, $manipulators);
    $menuElement = $this->menuTree->build($tree);

    $menuElement['#cache']['contexts'] += $this->cacheContext;

    $element['administration_menu'] = $menuElement;

    return $element;
  }

  /**
   * Set the menu.
   *
   * @param string $menuName
   *   The menu name.
   */
  public function setMenuName($menuName) {
    $this->menuName = $menuName;
  }

  /**
   * Set cache context.
   *
   * @param mixed $cacheContext
   *   The cache context.
   */
  public function setCacheContext($cacheContext) {
    $this->cacheContext = $cacheContext;
  }

}

Pour conclure, et avoir le même comportement de vos barres de menu d'administration que celui fourni par le module Toolbar, notamment lorsque vous basculez vos "tiroirs" de menu en mode vertical, et disposer des menus déroulants dans ce mode, il restera à initialiser cette navigation verticale au moyen de jQuery. 

Navigation verticale avec toolbar

(function ($, Drupal) {
    //'use strict';

  Drupal.behaviors.my_bo = {
    attach: function(context, settings) {

      // Initialize vertical navigation on toolbar.
      if ('drupalToolbarMenu' in $.fn) {
        $('.toolbar-menu').drupalToolbarMenu();
      }

    }
  };

}(jQuery, Drupal));

En fonction des différents profils d'utilisateurs d'un site Drupal 8, nous pouvons alors leur proposer différentes entrées dans cette barre d'outils pour leur permettre d'accéder à des tableaux de bord personnalisés, des pages de gestion et / ou de configuration, sans avoir pour autant la nécessité de leur donner des droits d'accès à l'ensemble des pages d'administration, pages d'administration qui aboutiront souvent sur un accès refusé, car ne disposant pas des droits adéquats. 

Il est également possible de moduler ces différentes entrées de la barre d'outils en fonction des utilisateurs, et par exemple selon certains attributs qui leur seraient associés comme une direction, un service, etc, au moyen du hook hook_toolbar_alter(). Mais le principe reste sur le fond le même.

En quelques lignes de code, nous pouvons alors proposer aux utilisateurs et gestionnaires d'un site Drupal 8 une barre d'outils sur mesure, leur donnant des accès rapides aux principales fonctions qu'ils auront à utiliser au quotidien. Cette méthode permet aussi de donner accès à des chemins spécifiques d'administration du site, tout en maitrisant les accès aux autres pages d'administration et de configuration. Par exemple leur donner un accès au positionnement des blocs du thème principal par défaut, mais non au positionnement des blocs du thème de base ou encore du thème d'administration. Mais ceci est une autre histoire, et nécessite quelques personnalisations supplémentaires au niveau des droits d'accès.

Vous souhaitez améliorer l'expérience utilisateur sur votre site Drupal 8. Vous avez besoin d'un développeur Drupal 8, voire un peu plus ? N'hésitez pas me contacter. Je pourrais vous conseiller sur les stratégies et alternatives possibles, et les implémenter également.

Mise à jour 27/10/2018 : vous pouvez également utiliser le module Toolbar menu désormais disponible sur drupal 8.

Commentaires

Soumis par Fred (non vérifié) le 26/10/2018 à 10:46 - Permalien

Merci pour ce bon tuto, comment procéder pour supprimer le menu lors de la suppression du module ?

On peut créer le lien de menu avec l'interface de Drupal. Dans ce cas, ce dernier est créé et donc supprimable. C'est ce qu'explique l'auteur de ce guide en décrivant l'usage de l'outil de synchronisation de configuration via un export de l'élément de menu, une modification de la valeur de la variable Locked pour passer de false à true et rendre ainsi l'entrée de menu non supprimable. Il suffit de faire l'inverse. On se rend dans le menu Developpement/Synchronisation/export, on choisit un élément individuel et on sélectionne notre menu Edition. On voit alors apparaître le contenu du fichier system.menu.edition.yml avec sa valeur Locked à vraie. On copie tout le code, puis on réalise un import individuel. On colle ce que l'on vient de copier, on met false à la place de true pour la variable Locked et on importe. Le menu sera toujours présent, mais cette fois, il sera supprimable. Il ne reste plus qu'à le supprimer, et en faisant ainsi, on est sûr qu'il sera correctement éliminé.

Soumis par Yotta67 (non vérifié) le 04/01/2020 à 18:18 - Permalien

Bonjour,
J'ai réalisé votre exemple sous Drupal 8.8.2, et je rencontre un problème inexplicable :
Dans la classe du service "MyToolbar.php", $this->menuTree->load($this->menuName, $parameters) me renvoie array(0) ??? Je n'arrive pas à comprendre pourquoi...

Ajouter un commentaire