Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
0.00% |
0 / 1 |
|
50.00% |
2 / 4 |
CRAP | |
79.25% |
42 / 53 |
| PathBasedBreadcrumbBuilder | |
0.00% |
0 / 1 |
|
50.00% |
2 / 4 |
19.58 | |
79.25% |
42 / 53 |
| __construct | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 9 |
|||
| applies | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| build | |
100.00% |
1 / 1 |
7 | |
100.00% |
25 / 25 |
|||
| getRequestForPath | |
0.00% |
0 / 1 |
8.09 | |
88.89% |
16 / 18 |
|||
| <?php | |
| /** | |
| * @file | |
| * Contains \Drupal\system\PathBasedBreadcrumbBuilder. | |
| */ | |
| 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\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 dynamic router service. | |
| * | |
| * @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; | |
| /** | |
| * 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. | |
| */ | |
| 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) { | |
| $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; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function applies(RouteMatchInterface $route_match) { | |
| return TRUE; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function build(RouteMatchInterface $route_match) { | |
| $breadcrumb = new Breadcrumb(); | |
| $links = array(); | |
| // 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 = array(); | |
| // 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; | |
| // Because this breadcrumb builder is entirely path-based, vary by the | |
| // 'url.path' cache context. | |
| $breadcrumb->addCacheContexts(['url.path']); | |
| 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(array('-', '_'), ' ', Unicode::ucfirst(end($path_elements))); | |
| } | |
| $url = Url::fromRouteMatch($route_match); | |
| $links[] = new Link($title, $url); | |
| } | |
| } | |
| } | |
| if ($path && '/' . $path != $front) { | |
| // Add the Home link, except for the front page. | |
| $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; | |
| } | |
| // @todo Use the RequestHelper once https://www.drupal.org/node/2090293 is | |
| // fixed. | |
| $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; | |
| } | |
| } | |
| } |