Same name and namespace in other branches
  1. 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_breadcrumb

Code

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