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