Skip to content
Snippets Groups Projects
Select Git revision
  • 8.9.x
  • 11.x default protected
  • 11.2.x protected
  • 10.5.x protected
  • 10.6.x protected
  • 11.1.x protected
  • 10.4.x protected
  • 11.0.x protected
  • 10.3.x protected
  • 7.x protected
  • 10.2.x protected
  • 10.1.x protected
  • 9.5.x protected
  • 10.0.x protected
  • 9.4.x protected
  • 9.3.x protected
  • 9.2.x protected
  • 9.1.x protected
  • 9.0.x protected
  • 8.8.x protected
  • 10.5.2 protected
  • 11.2.3 protected
  • 10.5.1 protected
  • 11.2.2 protected
  • 11.2.1 protected
  • 11.2.0 protected
  • 10.5.0 protected
  • 11.2.0-rc2 protected
  • 10.5.0-rc1 protected
  • 11.2.0-rc1 protected
  • 10.4.8 protected
  • 11.1.8 protected
  • 10.5.0-beta1 protected
  • 11.2.0-beta1 protected
  • 11.2.0-alpha1 protected
  • 10.4.7 protected
  • 11.1.7 protected
  • 10.4.6 protected
  • 11.1.6 protected
  • 10.3.14 protected
40 results

PathBasedBreadcrumbBuilder.php

Blame
  • xjm's avatar
    Issue #3116147 by idebr, tim.plunkett: Remove @todo pertaining to...
    Jess authored
    Issue #3116147 by idebr, tim.plunkett: Remove @todo pertaining to RequestHelper::duplicate(), which has been removed
    
    (cherry picked from commit aada3e1f)
    686f68e9
    History
    Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    PathBasedBreadcrumbBuilder.php 7.93 KiB
    <?php
    
    namespace Drupal\system;
    
    use Drupal\Component\Utility\Unicode;
    use Drupal\Core\Access\AccessManagerInterface;
    use Drupal\Core\Breadcrumb\Breadcrumb;
    use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
    use Drupal\Core\Config\ConfigFactoryInterface;
    use Drupal\Core\Controller\TitleResolverInterface;
    use Drupal\Core\Link;
    use Drupal\Core\ParamConverter\ParamNotConvertedException;
    use Drupal\Core\Path\CurrentPathStack;
    use Drupal\Core\Path\PathMatcherInterface;
    use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
    use Drupal\Core\Routing\RequestContext;
    use Drupal\Core\Routing\RouteMatch;
    use Drupal\Core\Routing\RouteMatchInterface;
    use Drupal\Core\Session\AccountInterface;
    use Drupal\Core\StringTranslation\StringTranslationTrait;
    use Drupal\Core\Url;
    use Symfony\Component\HttpFoundation\Request;
    use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
    use Symfony\Component\Routing\Exception\MethodNotAllowedException;
    use Symfony\Component\Routing\Exception\ResourceNotFoundException;
    use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
    
    /**
     * Class to define the menu_link breadcrumb builder.
     */
    class PathBasedBreadcrumbBuilder implements BreadcrumbBuilderInterface {
      use StringTranslationTrait;
    
      /**
       * The router request context.
       *
       * @var \Drupal\Core\Routing\RequestContext
       */
      protected $context;
    
      /**
       * The menu link access service.
       *
       * @var \Drupal\Core\Access\AccessManagerInterface
       */
      protected $accessManager;
    
      /**
       * The dynamic router service.
       *
       * @var \Symfony\Component\Routing\Matcher\RequestMatcherInterface
       */
      protected $router;
    
      /**
       * The inbound path processor.
       *
       * @var \Drupal\Core\PathProcessor\InboundPathProcessorInterface
       */
      protected $pathProcessor;
    
      /**
       * Site config object.
       *
       * @var \Drupal\Core\Config\Config
       */
      protected $config;
    
      /**
       * The title resolver.
       *
       * @var \Drupal\Core\Controller\TitleResolverInterface
       */
      protected $titleResolver;
    
      /**
       * The current user object.
       *
       * @var \Drupal\Core\Session\AccountInterface
       */
      protected $currentUser;
    
      /**
       * The current path service.
       *
       * @var \Drupal\Core\Path\CurrentPathStack
       */
      protected $currentPath;
    
      /**
       * The patch matcher service.
       *
       * @var \Drupal\Core\Path\PathMatcherInterface
       */
      protected $pathMatcher;
    
      /**
       * Constructs the PathBasedBreadcrumbBuilder.
       *
       * @param \Drupal\Core\Routing\RequestContext $context
       *   The router request context.
       * @param \Drupal\Core\Access\AccessManagerInterface $access_manager
       *   The menu link access service.
       * @param \Symfony\Component\Routing\Matcher\RequestMatcherInterface $router
       *   The dynamic router service.
       * @param \Drupal\Core\PathProcessor\InboundPathProcessorInterface $path_processor
       *   The inbound path processor.
       * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
       *   The config factory service.
       * @param \Drupal\Core\Controller\TitleResolverInterface $title_resolver
       *   The title resolver service.
       * @param \Drupal\Core\Session\AccountInterface $current_user
       *   The current user object.
       * @param \Drupal\Core\Path\CurrentPathStack $current_path
       *   The current path.
       * @param \Drupal\Core\Path\PathMatcherInterface $path_matcher
       *   The path matcher service.
       */
      public function __construct(RequestContext $context, AccessManagerInterface $access_manager, RequestMatcherInterface $router, InboundPathProcessorInterface $path_processor, ConfigFactoryInterface $config_factory, TitleResolverInterface $title_resolver, AccountInterface $current_user, CurrentPathStack $current_path, PathMatcherInterface $path_matcher = NULL) {
        $this->context = $context;
        $this->accessManager = $access_manager;
        $this->router = $router;
        $this->pathProcessor = $path_processor;
        $this->config = $config_factory->get('system.site');
        $this->titleResolver = $title_resolver;
        $this->currentUser = $current_user;
        $this->currentPath = $current_path;
        $this->pathMatcher = $path_matcher ?: \Drupal::service('path.matcher');
      }
    
      /**
       * {@inheritdoc}
       */
      public function applies(RouteMatchInterface $route_match) {
        return TRUE;
      }
    
      /**
       * {@inheritdoc}
       */
      public function build(RouteMatchInterface $route_match) {
        $breadcrumb = new Breadcrumb();
        $links = [];
    
        // Add the url.path.parent cache context. This code ignores the last path
        // part so the result only depends on the path parents.
        $breadcrumb->addCacheContexts(['url.path.parent', 'url.path.is_front']);
    
        // Do not display a breadcrumb on the frontpage.
        if ($this->pathMatcher->isFrontPage()) {
          return $breadcrumb;
        }
    
        // General path-based breadcrumbs. Use the actual request path, prior to
        // resolving path aliases, so the breadcrumb can be defined by simply
        // creating a hierarchy of path aliases.
        $path = trim($this->context->getPathInfo(), '/');
        $path_elements = explode('/', $path);
        $exclude = [];
        // Don't show a link to the front-page path.
        $front = $this->config->get('page.front');
        $exclude[$front] = TRUE;
        // /user is just a redirect, so skip it.
        // @todo Find a better way to deal with /user.
        $exclude['/user'] = TRUE;
        while (count($path_elements) > 1) {
          array_pop($path_elements);
          // Copy the path elements for up-casting.
          $route_request = $this->getRequestForPath('/' . implode('/', $path_elements), $exclude);
          if ($route_request) {
            $route_match = RouteMatch::createFromRequest($route_request);
            $access = $this->accessManager->check($route_match, $this->currentUser, NULL, TRUE);
            // The set of breadcrumb links depends on the access result, so merge
            // the access result's cacheability metadata.
            $breadcrumb = $breadcrumb->addCacheableDependency($access);
            if ($access->isAllowed()) {
              $title = $this->titleResolver->getTitle($route_request, $route_match->getRouteObject());
              if (!isset($title)) {
                // Fallback to using the raw path component as the title if the
                // route is missing a _title or _title_callback attribute.
                $title = str_replace(['-', '_'], ' ', Unicode::ucfirst(end($path_elements)));
              }
              $url = Url::fromRouteMatch($route_match);
              $links[] = new Link($title, $url);
            }
          }
        }
    
        // Add the Home link.
        $links[] = Link::createFromRoute($this->t('Home'), '<front>');
    
        return $breadcrumb->setLinks(array_reverse($links));
      }
    
      /**
       * Matches a path in the router.
       *
       * @param string $path
       *   The request path with a leading slash.
       * @param array $exclude
       *   An array of paths or system paths to skip.
       *
       * @return \Symfony\Component\HttpFoundation\Request
       *   A populated request object or NULL if the path couldn't be matched.
       */
      protected function getRequestForPath($path, array $exclude) {
        if (!empty($exclude[$path])) {
          return NULL;
        }
        $request = Request::create($path);
        // Performance optimization: set a short accept header to reduce overhead in
        // AcceptHeaderMatcher when matching the request.
        $request->headers->set('Accept', 'text/html');
        // Find the system path by resolving aliases, language prefix, etc.
        $processed = $this->pathProcessor->processInbound($path, $request);
        if (empty($processed) || !empty($exclude[$processed])) {
          // This resolves to the front page, which we already add.
          return NULL;
        }
        $this->currentPath->setPath($processed, $request);
        // Attempt to match this path to provide a fully built request.
        try {
          $request->attributes->add($this->router->matchRequest($request));
          return $request;
        }
        catch (ParamNotConvertedException $e) {
          return NULL;
        }
        catch (ResourceNotFoundException $e) {
          return NULL;
        }
        catch (MethodNotAllowedException $e) {
          return NULL;
        }
        catch (AccessDeniedHttpException $e) {
          return NULL;
        }
      }
    
    }