Same name in other branches
- 8.x-1.x src/EasyBreadcrumbBuilder.php \Drupal\easy_breadcrumb\EasyBreadcrumbBuilder::build()
Fichier
-
src/
EasyBreadcrumbBuilder.php, line 268
Classe
- EasyBreadcrumbBuilder
- Primary implementation for the Easy Breadcrumb builder.
Namespace
Drupal\easy_breadcrumbCode
public function build(RouteMatchInterface $route_match) {
$breadcrumb = new Breadcrumb();
$links = [];
$exclude = [];
$curr_lang = $this->languageManager
->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)
->getId();
$replacedTitles = [];
$configTitles = $this->config
->get(EasyBreadcrumbConstants::REPLACED_TITLES);
$mapValues = !empty($configTitles) ? preg_split('/[\\r\\n]+/', $configTitles) : [];
$limit_display = $this->config
->get(EasyBreadcrumbConstants::LIMIT_SEGMENT_DISPLAY);
$segment_limit = $this->config
->get(EasyBreadcrumbConstants::SEGMENT_DISPLAY_LIMIT);
foreach ($mapValues as $mapValue) {
$values = explode("::", $mapValue);
if (count($values) == 2) {
$replacedTitles[$values[0]] = $values[1];
}
}
// Set request context from the $route_match if route is available.
$this->setRouteContextFromRouteMatch($route_match);
// General path-based breadcrumbs. Use the actual request path, prior to
// resolving path aliases so the breadcrumb can be defined by creating a
// hierarchy of path aliases.
$path = trim($this->context
->getPathInfo(), '/');
// Ensure that Views AJAX requests do not seep into the breadcrumb. This
// can be a problem when the breadcrumb exists inside the view header.
if ($route_match->getRouteName() == 'views.ajax') {
$path = trim($this->currentPath
->getPath(), '/');
}
$path = urldecode($path);
$path_elements = explode('/', $path);
$front = $this->siteConfig
->get('page.front');
// Give the option to keep the breadcrumb on the front page.
$keep_front = !empty($this->config
->get(EasyBreadcrumbConstants::HOME_SEGMENT_TITLE)) && $this->config
->get(EasyBreadcrumbConstants::HOME_SEGMENT_KEEP);
$exclude[$front] = !$keep_front;
$exclude[''] = !$keep_front;
$exclude['/user'] = TRUE;
// See if we are doing a Custom Path override.
$path_crumb_row = preg_split('/[\\r\\n]+/', (string) $this->config
->get(EasyBreadcrumbConstants::CUSTOM_PATHS));
$path_crumb_row = array_filter($path_crumb_row);
foreach ($path_crumb_row as $path_crumb) {
$values = explode("::", $path_crumb);
// Shift path off array.
$custom_path = array_shift($values);
// Strip of leading/ending slashes and spaces/tabs (allows indenting
// rows on config page).
$custom_path = mb_strtolower(trim($custom_path, "/ \t"));
// Check if custom path includes the flag used to signify that the
// path is expressed as a regular expression pattern.
$regex_match = [];
$is_regex = preg_match('/^regex\\s*!\\s*\\/(.*)/', $custom_path, $regex_match);
if ($is_regex) {
$custom_path = $regex_match[1];
$regex_group_matches = [];
}
// If the path matches the current path, build the breadcrumbs.
if ($is_regex && preg_match("|" . $custom_path . "|", $path, $regex_group_matches) || !$is_regex && $path == $custom_path) {
if ($this->config
->get(EasyBreadcrumbConstants::INCLUDE_HOME_SEGMENT)) {
$links[] = Link::createFromRoute($this->config
->get(EasyBreadcrumbConstants::HOME_SEGMENT_TITLE), '<front>');
}
if ($is_regex && count($regex_group_matches) > 1) {
// Discard first element as that's the full matched string
// rather than a captured group.
array_shift($regex_group_matches);
}
// Get $title|[$url] pairs from $values.
foreach ($values as $pair) {
$settings = explode("|", $pair);
$title = Html::decodeEntities(Xss::filter(trim($settings[0])));
$use_current_page_title = trim($settings[0]) === '<title>';
// If the custom title uses the current page title, fetch it.
if ($use_current_page_title) {
$route_request = $this->getRequestForPath($path, []);
if ($route_request) {
$route_match = RouteMatch::createFromRequest($route_request);
$access = $this->accessManager
->check($route_match, $this->currentUser, NULL, TRUE);
$breadcrumb = $breadcrumb->addCacheableDependency($access);
// The set of breadcrumb links depends on the access result, so merge
// the access result's cacheability metadata.
if ($access->isAllowed()) {
if ($this->config
->get(EasyBreadcrumbConstants::TITLE_FROM_PAGE_WHEN_AVAILABLE)) {
$title = $this->normalizeText($this->getTitleString($route_request, $route_match, $replacedTitles));
}
}
}
}
elseif ($is_regex) {
foreach ($regex_group_matches as $group_num => $captured_str) {
$title = str_replace('$' . ($group_num + 1), urlencode($captured_str), $title);
}
}
// Get URL if it is provided.
$url = '';
if (isset($settings[1])) {
$url = trim($settings[1]);
// If the custom path includes any regex match groups
// (eg. "/foo/(\d*)/bar") then check if the urls for any segments
// have matched group variables (eg. $1 or $3) and if they do
// substitute them out for the corresponding matched strings.
if ($is_regex) {
foreach ($regex_group_matches as $group_num => $captured_str) {
$url = str_replace('$' . ($group_num + 1), urlencode($captured_str), $url);
}
}
// If URL is invalid, then display warning and disable the link.
if (!UrlHelper::isValid($url)) {
$this->messenger
->addWarning($this->t("EasyBreadcrumb: Custom crumb for @path URL '@url' is invalid.", [
'@path' => $path,
'@url' => $url,
]));
$url = '';
}
// If URL is not start with slash then display warning
// and disable the link.
if ($url[0] != '/') {
$this->messenger
->addWarning($this->t("EasyBreadcrumb: Custom crumb for @path URL '@url' should start with slash(/).", [
'@path' => $path,
'@url' => $url,
]));
$url = '';
}
}
if ($url) {
$links[] = new Link($title, Url::fromUserInput($url, [
'absolute' => TRUE,
]));
}
else {
$links[] = Link::createFromRoute($title, '<none>');
}
}
// Handle views path expiration cache expiration.
$parameters = $route_match->getParameters();
foreach ($parameters as $key => $parameter) {
if ($key === 'view_id') {
$breadcrumb->addCacheTags([
'config:views.view.' . $parameter,
]);
}
if ($parameter instanceof CacheableDependencyInterface) {
$breadcrumb->addCacheableDependency($parameter);
}
}
// Expire cache by languages and config changes.
$breadcrumb->addCacheContexts([
'route',
'url.path',
'languages',
]);
// Expire cache context for config changes.
$breadcrumb->addCacheableDependency($this->config);
return $breadcrumb->setLinks($links);
}
}
// Handle views path expiration cache expiration.
$parameters = $route_match->getParameters();
foreach ($parameters as $key => $parameter) {
if ($key === 'view_id') {
$breadcrumb->addCacheTags([
'config:views.view.' . $parameter,
]);
}
if ($parameter instanceof CacheableDependencyInterface) {
$breadcrumb->addCacheableDependency($parameter);
}
}
// Expire cache by languages and config changes.
$breadcrumb->addCacheContexts([
'route',
'url.path',
'languages',
]);
$breadcrumb->addCacheableDependency($this->config);
$i = 0;
$add_langcode = FALSE;
// Remove the current page if it's not wanted.
if (!$this->config
->get(EasyBreadcrumbConstants::INCLUDE_TITLE_SEGMENT)) {
array_pop($path_elements);
}
if (isset($path_elements[0])) {
// Remove the first parameter if it matches the current language.
if (!$this->config
->get(EasyBreadcrumbConstants::LANGUAGE_PATH_PREFIX_AS_SEGMENT)) {
$curr_lang_prefix = $curr_lang;
if ($prefixes = $this->languageNegotiationConfig
->get('url.prefixes')) {
// Using null-coalescing to check for prefix existence for $curr_lang.
$curr_lang_prefix = $prefixes[$curr_lang] ?? '';
}
if (mb_strtolower($path_elements[0]) == mb_strtolower($curr_lang_prefix)) {
// Preserve case in language to allow path matching to work properly.
$curr_lang = $path_elements[0];
array_shift($path_elements);
$add_langcode = TRUE;
}
}
}
// Remove leading breadcrumb segments by limiting the following loop.
$loop_limit = $limit_display && isset($segment_limit) ? $segment_limit : 0;
while (count($path_elements) > $loop_limit) {
$check_path = '/' . implode('/', $path_elements);
if ($add_langcode) {
$check_path = '/' . $curr_lang . $check_path;
}
// Copy the path elements for up-casting.
$route_request = $this->getRequestForPath($check_path, $exclude);
if ($this->config
->get(EasyBreadcrumbConstants::EXCLUDED_PATHS)) {
$config_textarea = $this->config
->get(EasyBreadcrumbConstants::EXCLUDED_PATHS);
$excludes = preg_split('/[\\r\\n]+/', $config_textarea, -1, PREG_SPLIT_NO_EMPTY);
if (in_array(end($path_elements), $excludes)) {
array_pop($path_elements);
continue;
}
}
if ($route_request) {
$route_match = RouteMatch::createFromRequest($route_request);
$access = $this->accessManager
->check($route_match, $this->currentUser, NULL, TRUE);
$breadcrumb = $breadcrumb->addCacheableDependency($access);
// The set of breadcrumb links depends on the access result, so merge
// the access result's cacheability metadata.
if ($access->isAllowed()) {
if ($this->config
->get(EasyBreadcrumbConstants::TITLE_FROM_PAGE_WHEN_AVAILABLE)) {
// Get the title if the current route represents an entity.
$title = FALSE;
if (($route = $route_match->getRouteObject()) && ($parameters = $route->getOption('parameters'))) {
foreach ($parameters as $name => $options) {
if (isset($options['type']) && strpos($options['type'], 'entity:') === 0) {
$entity = $route_match->getParameter($name);
if ($entity instanceof EntityInterface && $entity->hasLinkTemplate('canonical')) {
$title = $this->normalizeText($this->getTitleString($route_request, $route_match, $replacedTitles));
// Add this entity's cacheability metadata.
$breadcrumb->addCacheableDependency($entity);
$title = (string) $entity->label();
// If the title is to be replaced replaces the title.
if (!empty($title) && array_key_exists($title, $replacedTitles)) {
$title = $replacedTitles[$title];
}
if ($title && $this->config
->get(EasyBreadcrumbConstants::TRUNCATOR_MODE)) {
$title = $this->truncator($title);
}
break;
}
}
}
}
if (!$title) {
$title = $this->normalizeText($this->getTitleString($route_request, $route_match, $replacedTitles));
if ($this->config
->get(EasyBreadcrumbConstants::TRUNCATOR_MODE)) {
$title = $this->truncator($title);
}
if (empty($title)) {
unset($title);
}
// If the title is to be replaced...
if (!empty($title) && array_key_exists($title, $replacedTitles)) {
// Replaces the title.
$title = $replacedTitles[(string) $title];
}
}
}
if (!isset($title)) {
if ($this->config
->get(EasyBreadcrumbConstants::USE_MENU_TITLE_AS_FALLBACK)) {
// Try resolve the menu title from the route.
$route_name = $route_match->getRouteName();
$route_parameters = $route_match->getRawParameters()
->all();
$menu_links = $this->menuLinkManager
->loadLinksByRoute($route_name, $route_parameters);
if (empty($menu_links)) {
if ($this->config
->get(EasyBreadcrumbConstants::USE_PAGE_TITLE_AS_MENU_TITLE_FALLBACK)) {
$title = $this->getTitleString($route_request, $route_match, $replacedTitles);
if (!empty($title) && array_key_exists($title, $replacedTitles)) {
$title = $replacedTitles[$title];
}
if (!empty($title) && $this->config
->get(EasyBreadcrumbConstants::TRUNCATOR_MODE)) {
$title = $this->truncator($title);
}
}
}
else {
$preferred_menu = $this->config
->get(EasyBreadcrumbConstants::MENU_TITLE_PREFERRED_MENU);
if ($preferred_menu) {
$preferred_found = FALSE;
foreach ($menu_links as $link) {
if ($link->getMenuName() == $preferred_menu) {
$menu_link = $link;
$preferred_found = TRUE;
break;
}
}
if (!$preferred_found) {
$menu_link = reset($menu_links);
}
}
else {
$menu_link = reset($menu_links);
}
$title = $this->normalizeText($menu_link->getTitle());
if (array_key_exists($title, $replacedTitles)) {
$title = $replacedTitles[$title];
}
if ($this->config
->get(EasyBreadcrumbConstants::TRUNCATOR_MODE)) {
$title = $this->truncator($title);
}
}
}
// Fallback to using the raw path component as the title if the
// route is missing a _title or _title_callback attribute.
if (!isset($title)) {
$title = $this->normalizeText(str_replace([
'-',
'_',
], ' ', end($path_elements)));
if (array_key_exists($title, $replacedTitles)) {
$title = $replacedTitles[$title];
}
if ($this->config
->get(EasyBreadcrumbConstants::TRUNCATOR_MODE)) {
$title = $this->truncator($title);
}
}
}
// Add a linked breadcrumb unless it's the current page.
if ($i == 0 && $this->config
->get(EasyBreadcrumbConstants::INCLUDE_TITLE_SEGMENT) && !$this->config
->get(EasyBreadcrumbConstants::TITLE_SEGMENT_AS_LINK)) {
$links[] = Link::createFromRoute($title, '<none>');
}
elseif ($route_match->getRouteObject()) {
$url = Url::fromRouteMatch($route_match);
if ($this->config
->get(EasyBreadcrumbConstants::ABSOLUTE_PATHS)) {
$url->setOption('absolute', TRUE);
}
$links[] = new Link($title, $url);
}
// Add all term parents.
if ($i == 0 && $this->config
->get(EasyBreadcrumbConstants::TERM_HIERARCHY) && ($term = $route_match->getParameter('taxonomy_term'))) {
$parents = $this->entityTypeManager
->getStorage('taxonomy_term')
->loadAllParents($term->id());
// Unset current term.
array_shift($parents);
foreach ($parents as $parent) {
$parent = $this->entityRepository
->getTranslationFromContext($parent);
$links[] = $parent->toLink();
}
}
unset($title);
$i++;
}
}
elseif ($this->config
->get(EasyBreadcrumbConstants::INCLUDE_INVALID_PATHS) && empty($exclude[implode('/', $path_elements)])) {
$title = $this->normalizeText(str_replace([
'-',
'_',
], ' ', end($path_elements)));
$this->applyTitleReplacement($title, $replacedTitles);
$links[] = Link::createFromRoute($title, '<none>');
unset($title);
}
array_pop($path_elements);
}
// Add the home link, if desired.
if ($this->config
->get(EasyBreadcrumbConstants::INCLUDE_HOME_SEGMENT)) {
$home_route_name = '<front>';
if (count($links) === 0 && !$this->config
->get(EasyBreadcrumbConstants::TITLE_SEGMENT_AS_LINK)) {
$home_route_name = '<none>';
}
if (!$this->config
->get(EasyBreadcrumbConstants::USE_SITE_TITLE)) {
$links[] = Link::createFromRoute($this->normalizeText($this->config
->get(EasyBreadcrumbConstants::HOME_SEGMENT_TITLE)), $home_route_name);
}
else {
$links[] = Link::createFromRoute($this->siteConfig
->get('name'), $home_route_name);
}
if ($this->config
->get(EasyBreadcrumbConstants::HIDE_SINGLE_HOME_ITEM) && count($links) === 1) {
return $breadcrumb->setLinks([]);
}
}
$links = array_reverse($links);
if ($this->config
->get(EasyBreadcrumbConstants::REMOVE_REPEATED_SEGMENTS)) {
$links = $this->removeRepeatedSegments($links);
}
return $breadcrumb->setLinks($links);
}