123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- <?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;
- }
- }
- }
|