Chris@0: context = $context; Chris@0: $this->accessManager = $access_manager; Chris@0: $this->router = $router; Chris@0: $this->pathProcessor = $path_processor; Chris@0: $this->config = $config_factory->get('system.site'); Chris@0: $this->titleResolver = $title_resolver; Chris@0: $this->currentUser = $current_user; Chris@0: $this->currentPath = $current_path; Chris@0: $this->pathMatcher = $path_matcher ?: \Drupal::service('path.matcher'); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function applies(RouteMatchInterface $route_match) { Chris@0: return TRUE; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function build(RouteMatchInterface $route_match) { Chris@0: $breadcrumb = new Breadcrumb(); Chris@0: $links = []; Chris@0: Chris@0: // Add the url.path.parent cache context. This code ignores the last path Chris@0: // part so the result only depends on the path parents. Chris@17: $breadcrumb->addCacheContexts(['url.path.parent', 'url.path.is_front']); Chris@0: Chris@0: // Do not display a breadcrumb on the frontpage. Chris@0: if ($this->pathMatcher->isFrontPage()) { Chris@0: return $breadcrumb; Chris@0: } Chris@0: Chris@0: // General path-based breadcrumbs. Use the actual request path, prior to Chris@0: // resolving path aliases, so the breadcrumb can be defined by simply Chris@0: // creating a hierarchy of path aliases. Chris@0: $path = trim($this->context->getPathInfo(), '/'); Chris@0: $path_elements = explode('/', $path); Chris@0: $exclude = []; Chris@0: // Don't show a link to the front-page path. Chris@0: $front = $this->config->get('page.front'); Chris@0: $exclude[$front] = TRUE; Chris@0: // /user is just a redirect, so skip it. Chris@0: // @todo Find a better way to deal with /user. Chris@0: $exclude['/user'] = TRUE; Chris@0: while (count($path_elements) > 1) { Chris@0: array_pop($path_elements); Chris@0: // Copy the path elements for up-casting. Chris@0: $route_request = $this->getRequestForPath('/' . implode('/', $path_elements), $exclude); Chris@0: if ($route_request) { Chris@0: $route_match = RouteMatch::createFromRequest($route_request); Chris@0: $access = $this->accessManager->check($route_match, $this->currentUser, NULL, TRUE); Chris@0: // The set of breadcrumb links depends on the access result, so merge Chris@0: // the access result's cacheability metadata. Chris@0: $breadcrumb = $breadcrumb->addCacheableDependency($access); Chris@0: if ($access->isAllowed()) { Chris@0: $title = $this->titleResolver->getTitle($route_request, $route_match->getRouteObject()); Chris@0: if (!isset($title)) { Chris@0: // Fallback to using the raw path component as the title if the Chris@0: // route is missing a _title or _title_callback attribute. Chris@0: $title = str_replace(['-', '_'], ' ', Unicode::ucfirst(end($path_elements))); Chris@0: } Chris@0: $url = Url::fromRouteMatch($route_match); Chris@0: $links[] = new Link($title, $url); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: // Add the Home link. Chris@0: $links[] = Link::createFromRoute($this->t('Home'), ''); Chris@0: Chris@0: return $breadcrumb->setLinks(array_reverse($links)); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Matches a path in the router. Chris@0: * Chris@0: * @param string $path Chris@0: * The request path with a leading slash. Chris@0: * @param array $exclude Chris@0: * An array of paths or system paths to skip. Chris@0: * Chris@0: * @return \Symfony\Component\HttpFoundation\Request Chris@0: * A populated request object or NULL if the path couldn't be matched. Chris@0: */ Chris@0: protected function getRequestForPath($path, array $exclude) { Chris@0: if (!empty($exclude[$path])) { Chris@0: return NULL; Chris@0: } Chris@0: // @todo Use the RequestHelper once https://www.drupal.org/node/2090293 is Chris@0: // fixed. Chris@0: $request = Request::create($path); Chris@0: // Performance optimization: set a short accept header to reduce overhead in Chris@0: // AcceptHeaderMatcher when matching the request. Chris@0: $request->headers->set('Accept', 'text/html'); Chris@0: // Find the system path by resolving aliases, language prefix, etc. Chris@0: $processed = $this->pathProcessor->processInbound($path, $request); Chris@0: if (empty($processed) || !empty($exclude[$processed])) { Chris@0: // This resolves to the front page, which we already add. Chris@0: return NULL; Chris@0: } Chris@0: $this->currentPath->setPath($processed, $request); Chris@0: // Attempt to match this path to provide a fully built request. Chris@0: try { Chris@0: $request->attributes->add($this->router->matchRequest($request)); Chris@0: return $request; Chris@0: } Chris@0: catch (ParamNotConvertedException $e) { Chris@0: return NULL; Chris@0: } Chris@0: catch (ResourceNotFoundException $e) { Chris@0: return NULL; Chris@0: } Chris@0: catch (MethodNotAllowedException $e) { Chris@0: return NULL; Chris@0: } Chris@0: catch (AccessDeniedHttpException $e) { Chris@0: return NULL; Chris@0: } Chris@0: } Chris@0: Chris@0: }