vendor/contao/core-bundle/src/EventListener/Menu/BackendMenuListener.php line 64

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4.  * This file is part of Contao.
  5.  *
  6.  * (c) Leo Feyer
  7.  *
  8.  * @license LGPL-3.0-or-later
  9.  */
  10. namespace Contao\CoreBundle\EventListener\Menu;
  11. use Contao\Backend;
  12. use Contao\BackendUser;
  13. use Contao\CoreBundle\Event\MenuEvent;
  14. use Contao\CoreBundle\Framework\ContaoFramework;
  15. use Contao\StringUtil;
  16. use Symfony\Component\HttpFoundation\RequestStack;
  17. use Symfony\Component\Routing\RouterInterface;
  18. use Symfony\Component\Security\Core\Security;
  19. use Symfony\Contracts\Translation\TranslatorInterface;
  20. /**
  21.  * @internal
  22.  */
  23. class BackendMenuListener
  24. {
  25.     /**
  26.      * @var Security
  27.      */
  28.     private $security;
  29.     /**
  30.      * @var RouterInterface
  31.      */
  32.     private $router;
  33.     /**
  34.      * @var RequestStack
  35.      */
  36.     private $requestStack;
  37.     /**
  38.      * @var TranslatorInterface
  39.      */
  40.     private $translator;
  41.     /**
  42.      * @var ContaoFramework
  43.      */
  44.     private $framework;
  45.     public function __construct(Security $securityRouterInterface $routerRequestStack $requestStackTranslatorInterface $translatorContaoFramework $framework)
  46.     {
  47.         $this->security $security;
  48.         $this->router $router;
  49.         $this->requestStack $requestStack;
  50.         $this->translator $translator;
  51.         $this->framework $framework;
  52.     }
  53.     public function __invoke(MenuEvent $event): void
  54.     {
  55.         $user $this->security->getUser();
  56.         if (!$user instanceof BackendUser) {
  57.             return;
  58.         }
  59.         $name $event->getTree()->getName();
  60.         if ('mainMenu' === $name) {
  61.             $this->buildMainMenu($event$user);
  62.         } elseif ('headerMenu' === $name) {
  63.             $this->buildHeaderMenu($event$user);
  64.         }
  65.     }
  66.     private function buildMainMenu(MenuEvent $eventBackendUser $user): void
  67.     {
  68.         $factory $event->getFactory();
  69.         $tree $event->getTree();
  70.         $modules $user->navigation();
  71.         $path $this->router->generate('contao_backend');
  72.         foreach ($modules as $categoryName => $categoryData) {
  73.             $categoryNode $tree->getChild($categoryName);
  74.             if (!$categoryNode) {
  75.                 $categoryNode $factory
  76.                     ->createItem($categoryName)
  77.                     ->setLabel($categoryData['label'])
  78.                     ->setUri($categoryData['href'])
  79.                     ->setLinkAttribute('class'$this->getClassFromAttributes($categoryData))
  80.                     ->setLinkAttribute('title'$categoryData['title'])
  81.                     ->setLinkAttribute('onclick'"return AjaxRequest.toggleNavigation(this, '".$categoryName."', '".$path."')")
  82.                     ->setChildrenAttribute('id'$categoryName)
  83.                     ->setExtra('translation_domain'false)
  84.                 ;
  85.                 if (isset($categoryData['class']) && preg_match('/\bnode-collapsed\b/'$categoryData['class'])) {
  86.                     $categoryNode->setAttribute('class''collapsed');
  87.                 }
  88.                 $tree->addChild($categoryNode);
  89.             }
  90.             // Create the child nodes
  91.             foreach ($categoryData['modules'] as $nodeName => $nodeData) {
  92.                 $moduleNode $factory
  93.                     ->createItem($nodeName)
  94.                     ->setLabel($nodeData['label'])
  95.                     ->setUri($nodeData['href'])
  96.                     ->setLinkAttribute('class'$this->getClassFromAttributes($nodeData))
  97.                     ->setLinkAttribute('title'$nodeData['title'])
  98.                     ->setCurrent((bool) $nodeData['isActive'])
  99.                     ->setExtra('translation_domain'false)
  100.                 ;
  101.                 $categoryNode->addChild($moduleNode);
  102.             }
  103.         }
  104.     }
  105.     private function buildHeaderMenu(MenuEvent $eventBackendUser $user): void
  106.     {
  107.         $factory $event->getFactory();
  108.         $tree $event->getTree();
  109.         $ref $this->getRefererId();
  110.         $systemMessages $this->translator->trans('MSC.systemMessages', [], 'contao_default');
  111.         $alerts $event->getFactory()
  112.             ->createItem('alerts')
  113.             ->setLabel($this->getAlertsLabel($systemMessages))
  114.             ->setUri($this->router->generate('contao_backend_alerts'))
  115.             ->setLinkAttribute('class''icon-alert')
  116.             ->setLinkAttribute('title'$systemMessages)
  117.             ->setLinkAttribute('onclick'"Backend.openModalIframe({'title':'".StringUtil::specialchars(str_replace("'""\\'"$systemMessages))."','url':this.href});return false")
  118.             ->setExtra('safe_label'true)
  119.             ->setExtra('translation_domain'false)
  120.         ;
  121.         $tree->addChild($alerts);
  122.         $submenu $factory
  123.             ->createItem('submenu')
  124.             ->setLabel($this->translator->trans('MSC.user', [], 'contao_default').' '.$user->username)
  125.             ->setAttribute('class''submenu')
  126.             ->setLabelAttribute('class''h2')
  127.             ->setExtra('translation_domain'false)
  128.         ;
  129.         $tree->addChild($submenu);
  130.         $info $factory
  131.             ->createItem('info')
  132.             ->setLabel(sprintf('<strong>%s</strong> %s'$user->name$user->email))
  133.             ->setAttribute('class''info')
  134.             ->setExtra('safe_label'true)
  135.             ->setExtra('translation_domain'false)
  136.         ;
  137.         $submenu->addChild($info);
  138.         $login $factory
  139.             ->createItem('login')
  140.             ->setLabel('MSC.profile')
  141.             ->setUri($this->router->generate('contao_backend', ['do' => 'login''ref' => $ref]))
  142.             ->setLinkAttribute('class''icon-profile')
  143.             ->setExtra('translation_domain''contao_default')
  144.         ;
  145.         $submenu->addChild($login);
  146.         $security $factory
  147.             ->createItem('security')
  148.             ->setLabel('MSC.security')
  149.             ->setUri($this->router->generate('contao_backend', ['do' => 'security''ref' => $ref]))
  150.             ->setLinkAttribute('class''icon-security')
  151.             ->setExtra('translation_domain''contao_default')
  152.         ;
  153.         $submenu->addChild($security);
  154.         $buger $factory
  155.             ->createItem('burger')
  156.             ->setLabel('<button type="button" id="burger"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12h18M3 6h18M3 18h18"/></svg></button>')
  157.             ->setAttribute('class''burger')
  158.             ->setExtra('safe_label'true)
  159.             ->setExtra('translation_domain'false)
  160.         ;
  161.         $tree->addChild($buger);
  162.     }
  163.     private function getAlertsLabel(string $systemMessages): string
  164.     {
  165.         /** @var Backend $adapter */
  166.         $adapter $this->framework->getAdapter(Backend::class);
  167.         $count substr_count($adapter->getSystemMessages(), 'class="tl_error');
  168.         if ($count 0) {
  169.             $systemMessages .= ' <sup>'.$count.'</sup>';
  170.         }
  171.         return $systemMessages;
  172.     }
  173.     private function getRefererId(): string
  174.     {
  175.         if (!$request $this->requestStack->getCurrentRequest()) {
  176.             throw new \RuntimeException('The request stack did not contain a request');
  177.         }
  178.         return $request->attributes->get('_contao_referer_id');
  179.     }
  180.     private function getClassFromAttributes(array $attributes): string
  181.     {
  182.         $classes = [];
  183.         // Remove the default CSS classes and keep potentially existing custom ones (see #1357)
  184.         if (isset($attributes['class'])) {
  185.             $classes array_flip(array_filter(explode(' '$attributes['class'])));
  186.             unset($classes['node-expanded'], $classes['node-collapsed'], $classes['trail']);
  187.         }
  188.         return implode(' 'array_keys($classes));
  189.     }
  190. }