From 6e6f3918062bfc37ec375fbc4c173e0a9937d6da Mon Sep 17 00:00:00 2001 From: Alex Pott Date: Thu, 14 Mar 2024 11:43:09 +0000 Subject: [PATCH] Issue #3370946 by kunal.sachdev, lauriii, tim.plunkett, omkar.podey, alexpott, ckrina, smustgrave, larowlan, rkoller, hooroomoo, duadua: Page title should contextualize the local navigation (cherry picked from commit ad606a8fb7eed1c59f5b28af8cc19e14cf6fb5ed) --- core/core.services.yml | 8 + core/includes/theme.inc | 30 ++- .../Block/Plugin/Block/PageTitleBlock.php | 145 ++++++++++++++- .../Controller/BaseRouteTitleResolver.php | 68 +++++++ .../lib/Drupal/Core/Menu/LocalTaskManager.php | 133 +++++++------ .../Core/Menu/LocalTaskManagerInterface.php | 11 ++ .../Drupal/Core/Utility/RequestGenerator.php | 77 ++++++++ core/modules/block/block.post_update.php | 19 ++ .../src/Functional/PageTitleBlockTest.php | 175 ++++++++++++++++++ .../Update/BlockTitleUpdateTest.php | 57 ++++++ .../src/Functional/BlockContentListTest.php | 2 +- .../Functional/BlockContentListViewsTest.php | 2 +- .../tests/src/Functional/FieldUIRouteTest.php | 10 +- .../src/Functional/LayoutBuilderTest.php | 6 +- .../system/config/schema/system.schema.yml | 8 + .../system/src/PathBasedBreadcrumbBuilder.php | 135 ++++++-------- core/modules/system/system.services.yml | 2 +- .../src/Functional/Module/UninstallTest.php | 4 +- .../PathBasedBreadcrumbBuilderTest.php | 59 +++--- .../install/block.block.umami_page_title.yml | 1 + .../install/block.block.stark_page_title.yml | 1 + ...sting_theme_optional_blocks_page_title.yml | 1 + .../KernelTests/Config/Schema/MappingTest.php | 3 + .../Core/Utility/RequestGeneratorTest.php | 163 ++++++++++++++++ .../config_install/multilingual.tar.gz | Bin 30577 -> 30742 bytes .../testing_config_install.tar.gz | Bin 10886 -> 11146 bytes .../optional/block.block.claro_page_title.yml | 1 + .../block.block.olivero_page_title.yml | 1 + 28 files changed, 940 insertions(+), 182 deletions(-) create mode 100644 core/lib/Drupal/Core/Controller/BaseRouteTitleResolver.php create mode 100644 core/lib/Drupal/Core/Utility/RequestGenerator.php create mode 100644 core/modules/block/tests/src/Functional/PageTitleBlockTest.php create mode 100644 core/modules/block/tests/src/Functional/Update/BlockTitleUpdateTest.php create mode 100644 core/tests/Drupal/Tests/Core/Utility/RequestGeneratorTest.php diff --git a/core/core.services.yml b/core/core.services.yml index 252d620af0c..54efa83842f 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -1073,6 +1073,14 @@ services: class: Drupal\Core\Utility\LinkGenerator arguments: ['@url_generator', '@module_handler', '@renderer'] Drupal\Core\Utility\LinkGeneratorInterface: '@link_generator' + request_generator: + class: Drupal\Core\Utility\RequestGenerator + arguments: ['@path_processor_manager', '@path.current', '@router'] + Drupal\Core\Utility\RequestGenerator: '@request_generator' + base_route_title_resolver: + class: Drupal\Core\Controller\BaseRouteTitleResolver + arguments: ['@url_generator', '@title_resolver', '@plugin.manager.menu.local_task', '@router.route_provider', '@request_generator'] + Drupal\Core\Utility\BaseRouteTitleResolver: '@base_route_title_resolver' router: class: Drupal\Core\Routing\AccessAwareRouter arguments: ['@router.no_access_checks', '@access_manager', '@current_user'] diff --git a/core/includes/theme.inc b/core/includes/theme.inc index f9a6f37adeb..bb5c1388bf1 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -1319,11 +1319,31 @@ function template_preprocess_html(&$variables) { $variables['page']['#title'] = (string) \Drupal::service('renderer')->render($variables['page']['#title']); } if (!empty($variables['page']['#title'])) { - $head_title = [ - // Marking the title as safe since it has had the tags stripped. - 'title' => Markup::create(trim(strip_tags($variables['page']['#title']))), - 'name' => $site_config->get('name'), - ]; + $head_title = []; + + // Marking the title as safe since it has had the tags stripped. + $head_title['title'] = Markup::create(trim(strip_tags($variables['page']['#title']))); + + $maintenance_mode = defined('MAINTENANCE_MODE') || \Drupal::state()->get('system.maintenance_mode'); + $is_admin_route = (bool) \Drupal::routeMatch()->getRouteObject()?->getOption('_admin_route'); + if (!$maintenance_mode && $is_admin_route) { + $base_route_title = \Drupal::service('base_route_title_resolver')->getTitle(\Drupal::requestStack()->getCurrentRequest(), \Drupal::routeMatch()->getRouteObject()); + if (is_array($base_route_title)) { + $base_route_title = (string) \Drupal::service('renderer')->render($base_route_title); + } + if ($base_route_title) { + // Marking the title as safe since it has had the tags stripped. + $base_route_title = Markup::create(trim(strip_tags($base_route_title))); + + // If the base route title is not the same as the current title, append it + // to the head title. + if ((string) $base_route_title !== (string) $head_title['title']) { + $head_title['base_route_title'] = $base_route_title; + } + } + } + + $head_title['name'] = $site_config->get('name'); } // @todo Remove once views is not bypassing the view subscriber anymore. // @see https://www.drupal.org/node/2068471 diff --git a/core/lib/Drupal/Core/Block/Plugin/Block/PageTitleBlock.php b/core/lib/Drupal/Core/Block/Plugin/Block/PageTitleBlock.php index 2689acae4a3..1b5cc9a010e 100644 --- a/core/lib/Drupal/Core/Block/Plugin/Block/PageTitleBlock.php +++ b/core/lib/Drupal/Core/Block/Plugin/Block/PageTitleBlock.php @@ -2,9 +2,15 @@ namespace Drupal\Core\Block\Plugin\Block; +use Symfony\Component\DependencyInjection\ContainerInterface; use Drupal\Core\Block\Attribute\Block; use Drupal\Core\Block\BlockBase; use Drupal\Core\Block\TitleBlockPluginInterface; +use Drupal\Core\Controller\TitleResolverInterface; +use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Plugin\ContainerFactoryPluginInterface; +use Drupal\Core\Routing\RouteMatchInterface; +use Symfony\Component\HttpFoundation\RequestStack; use Drupal\Core\StringTranslation\TranslatableMarkup; /** @@ -17,7 +23,7 @@ use Drupal\Core\StringTranslation\TranslatableMarkup; 'settings_tray' => FALSE, ] )] -class PageTitleBlock extends BlockBase implements TitleBlockPluginInterface { +class PageTitleBlock extends BlockBase implements TitleBlockPluginInterface, ContainerFactoryPluginInterface { /** * The page title: a string (plain title) or a render array (formatted title). @@ -26,6 +32,58 @@ class PageTitleBlock extends BlockBase implements TitleBlockPluginInterface { */ protected $title = ''; + /** + * Constructs a new PageTitleBlock. + * + * @param array $configuration + * A configuration array containing information about the plugin instance. + * @param string $plugin_id + * The plugin ID for the plugin instance. + * @param mixed $plugin_definition + * The plugin implementation definition. + * @param \Drupal\Core\Controller\TitleResolverInterface|null $titleResolver + * The title resolver. + * @param \Drupal\Core\Routing\RouteMatchInterface|null $routeMatch + * The route match. + * @param \Symfony\Component\HttpFoundation\RequestStack|null $requestStack + * The request stack. + * @param \Drupal\Core\Controller\TitleResolverInterface|null $baseRouteTitleResolver + * The base route title resolver. + */ + public function __construct( + array $configuration, + $plugin_id, + $plugin_definition, + protected ?TitleResolverInterface $titleResolver, + protected ?RouteMatchInterface $routeMatch, + protected ?RequestStack $requestStack, + protected ?TitleResolverInterface $baseRouteTitleResolver, + ) { + parent::__construct($configuration, $plugin_id, $plugin_definition); + if (!$this->titleResolver || !$this->routeMatch || !$this->requestStack || !$this->baseRouteTitleResolver) { + @trigger_error('Calling PageTitleBlock::__construct() without the $titleResolver, $routeMatch, $requestStack, and $baseRouteTitleResolver arguments is deprecated in drupal:10.3.0 and will be required in drupal:11.0.0. See https://www.drupal.org/node/3397210', E_USER_DEPRECATED); + $this->titleResolver = \Drupal::service('title_resolver'); + $this->routeMatch = \Drupal::service('current_route_match'); + $this->requestStack = \Drupal::service('request_stack'); + $this->baseRouteTitleResolver = \Drupal::service('base_route_title_resolver'); + } + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('title_resolver'), + $container->get('current_route_match'), + $container->get('request_stack'), + $container->get('base_route_title_resolver'), + ); + } + /** * {@inheritdoc} */ @@ -38,17 +96,98 @@ class PageTitleBlock extends BlockBase implements TitleBlockPluginInterface { * {@inheritdoc} */ public function defaultConfiguration() { - return ['label_display' => FALSE]; + return [ + 'label_display' => FALSE, + 'base_route_title' => FALSE, + ]; } /** * {@inheritdoc} */ public function build() { + $title = $this->title; + if ($this->configuration['base_route_title']) { + $base_route_title = $this->getTitleBasedOnBaseRoute(); + if (!is_null($base_route_title)) { + $title = $base_route_title; + } + } return [ '#type' => 'page_title', - '#title' => $this->title, + '#title' => $title, ]; } + /** + * {@inheritdoc} + */ + public function blockForm($form, FormStateInterface $form_state) : array { + $form['base_route_title'] = [ + '#type' => 'radios', + '#title' => $this->t('Title to be displayed'), + '#options' => [ + 0 => $this->t('Current page title'), + 1 => $this->t('Section page title'), + ], + '#default_value' => (int) $this->configuration['base_route_title'], + '#description' => $this->t('Choose whether to display the title of the current page or the current section. The section page title is preferred if the title is displayed before local tasks and if it is displayed after local tasks then the current page title is preferred.'), + ]; + return $form; + } + + /** + * {@inheritdoc} + */ + public function blockSubmit($form, FormStateInterface $form_state) : void { + $this->configuration['base_route_title'] = (bool) $form_state->getValue('base_route_title'); + } + + /** + * Gets title based on base route. + * + * @return array|string|null|\Stringable + * The title based on base route. + */ + private function getTitleBasedOnBaseRoute(): array|string|null|\Stringable { + $controller_title = $this->titleResolver->getTitle($this->requestStack->getCurrentRequest(), $this->routeMatch->getRouteObject()); + + // Controller render arrays using `#title` take precedent over the title + // resolvers. + if ((string) $this->titleToString($controller_title) !== (string) $this->titleToString($this->title)) { + return $this->title; + } + + $base_route_title = $this->baseRouteTitleResolver->getTitle($this->requestStack->getCurrentRequest(), $this->routeMatch->getRouteObject()); + if (!is_null($base_route_title)) { + // If the titles are equal, return the original title. + if ((string) $this->titleToString($base_route_title) === (string) $this->titleToString($this->title)) { + return $this->title; + } + + return $this->t('@section_title: @current_title', [ + '@section_title' => $this->titleToString($base_route_title), + '@current_title' => $this->titleToString($this->title), + ]); + } + + return $this->title; + } + + /** + * Converts title to string. + * + * @param array|string|null|\Stringable $title + * A title that could be an array, string or stringable object. + * + * @return string|\Stringable + */ + private function titleToString(array|string|null|\Stringable $title): string|\Stringable { + if (is_array($title)) { + $title = \Drupal::service('renderer')->render($title); + } + + return $title ?? ''; + } + } diff --git a/core/lib/Drupal/Core/Controller/BaseRouteTitleResolver.php b/core/lib/Drupal/Core/Controller/BaseRouteTitleResolver.php new file mode 100644 index 00000000000..495f7daadf9 --- /dev/null +++ b/core/lib/Drupal/Core/Controller/BaseRouteTitleResolver.php @@ -0,0 +1,68 @@ +localTaskManager->getBaseRouteNames($route_match->getRouteName()); + $title = NULL; + if ($base_route_names) { + $base_route_name = reset($base_route_names); + if ($base_route_name !== $route_match->getRouteName()) { + try { + $path = $this->urlGenerator->getPathFromRoute($base_route_name, $route_match->getRawParameters()->all()); + } + catch (RouteNotFoundException | InvalidParameterException) { + return NULL; + } + $route_request = $this->requestGenerator->generateRequestForPath($path, []); + if ($route_request) { + $title = $this->titleResolver->getTitle($route_request, $this->routeProvider->getRouteByName($base_route_name)); + } + } + } + return $title; + } + +} diff --git a/core/lib/Drupal/Core/Menu/LocalTaskManager.php b/core/lib/Drupal/Core/Menu/LocalTaskManager.php index 6fb8bdfe935..ec49720e0bd 100644 --- a/core/lib/Drupal/Core/Menu/LocalTaskManager.php +++ b/core/lib/Drupal/Core/Menu/LocalTaskManager.php @@ -197,58 +197,11 @@ class LocalTaskManager extends DefaultPluginManager implements LocalTaskManagerI public function getLocalTasksForRoute($route_name) { if (!isset($this->instances[$route_name])) { $this->instances[$route_name] = []; - if ($cache = $this->cacheBackend->get($this->cacheKey . ':' . $route_name)) { - $base_routes = $cache->data['base_routes']; - $parents = $cache->data['parents']; - $children = $cache->data['children']; - } - else { - $definitions = $this->getDefinitions(); - // We build the hierarchy by finding all tabs that should - // appear on the current route. - $base_routes = []; - $parents = []; - $children = []; - foreach ($definitions as $plugin_id => $task_info) { - // Fill in the base_route from the parent to insure consistency. - if (!empty($task_info['parent_id']) && !empty($definitions[$task_info['parent_id']])) { - $task_info['base_route'] = $definitions[$task_info['parent_id']]['base_route']; - // Populate the definitions we use in the next loop. Using a - // reference like &$task_info causes bugs. - $definitions[$plugin_id]['base_route'] = $definitions[$task_info['parent_id']]['base_route']; - } - if ($route_name == $task_info['route_name']) { - if (!empty($task_info['base_route'])) { - $base_routes[$task_info['base_route']] = $task_info['base_route']; - } - // Tabs that link to the current route are viable parents - // and their parent and children should be visible also. - // @todo - this only works for 2 levels of tabs. - // instead need to iterate up. - $parents[$plugin_id] = TRUE; - if (!empty($task_info['parent_id'])) { - $parents[$task_info['parent_id']] = TRUE; - } - } - } - if ($base_routes) { - // Find all the plugins with the same root and that are at the top - // level or that have a visible parent. - foreach ($definitions as $plugin_id => $task_info) { - if (!empty($base_routes[$task_info['base_route']]) && (empty($task_info['parent_id']) || !empty($parents[$task_info['parent_id']]))) { - // Concat '> ' with root ID for the parent of top-level tabs. - $parent = empty($task_info['parent_id']) ? '> ' . $task_info['base_route'] : $task_info['parent_id']; - $children[$parent][$plugin_id] = $task_info; - } - } - } - $data = [ - 'base_routes' => $base_routes, - 'parents' => $parents, - 'children' => $children, - ]; - $this->cacheBackend->set($this->cacheKey . ':' . $route_name, $data, Cache::PERMANENT, $this->cacheTags); - } + $data = $this->getLocalTasksDataForRoute($route_name); + $base_routes = $data['base_routes']; + $children = $data['children']; + $parents = $data['parents']; + // Create a plugin instance for each element of the hierarchy. foreach ($base_routes as $base_route) { // Convert the tree keyed by plugin IDs into a simple one with @@ -411,4 +364,80 @@ class LocalTaskManager extends DefaultPluginManager implements LocalTaskManagerI return $active; } + /** + * {@inheritdoc} + */ + public function getBaseRouteNames(string $route_name): array { + $data = $this->getLocalTasksDataForRoute($route_name); + return $data['base_routes'] ?? []; + } + + /** + * Gets the local task data for the given route. + * + * @param string $route_name + * The route name. + * + * @return array + * The local task data with the following keys: + * - base_routes: An array of base route names of the given route. + * - children: An array of the route's child local task definitions keyed by + * local task ID. + * - parents: An array of the route's parent local task definitions. + */ + protected function getLocalTasksDataForRoute(string $route_name): array { + if ($cache = $this->cacheBackend->get($this->cacheKey . ':' . $route_name)) { + $data = $cache->data; + } + else { + $definitions = $this->getDefinitions(); + // We build the hierarchy by finding all tabs that should + // appear on the current route. + $base_routes = []; + $parents = []; + $children = []; + foreach ($definitions as $plugin_id => $task_info) { + // Fill in the base_route from the parent to insure consistency. + if (!empty($task_info['parent_id']) && !empty($definitions[$task_info['parent_id']])) { + $task_info['base_route'] = $definitions[$task_info['parent_id']]['base_route']; + // Populate the definitions we use in the next loop. Using a + // reference like &$task_info causes bugs. + $definitions[$plugin_id]['base_route'] = $definitions[$task_info['parent_id']]['base_route']; + } + if ($route_name == $task_info['route_name']) { + if (!empty($task_info['base_route'])) { + $base_routes[$task_info['base_route']] = $task_info['base_route']; + } + // Tabs that link to the current route are viable parents + // and their parent and children should be visible also. + // @todo - this only works for 2 levels of tabs. + // instead need to iterate up. + $parents[$plugin_id] = TRUE; + if (!empty($task_info['parent_id'])) { + $parents[$task_info['parent_id']] = TRUE; + } + } + } + if ($base_routes) { + // Find all the plugins with the same root and that are at the top + // level or that have a visible parent. + foreach ($definitions as $plugin_id => $task_info) { + if (!empty($base_routes[$task_info['base_route']]) && (empty($task_info['parent_id']) || !empty($parents[$task_info['parent_id']]))) { + // Concat '> ' with root ID for the parent of top-level tabs. + $parent = empty($task_info['parent_id']) ? '> ' . $task_info['base_route'] : $task_info['parent_id']; + $children[$parent][$plugin_id] = $task_info; + } + } + } + $data = [ + 'base_routes' => $base_routes, + 'parents' => $parents, + 'children' => $children, + ]; + $this->cacheBackend->set($this->cacheKey . ':' . $route_name, $data, Cache::PERMANENT, $this->cacheTags); + } + + return $data; + } + } diff --git a/core/lib/Drupal/Core/Menu/LocalTaskManagerInterface.php b/core/lib/Drupal/Core/Menu/LocalTaskManagerInterface.php index 9352e4011bc..dc0f2ea6578 100644 --- a/core/lib/Drupal/Core/Menu/LocalTaskManagerInterface.php +++ b/core/lib/Drupal/Core/Menu/LocalTaskManagerInterface.php @@ -70,4 +70,15 @@ interface LocalTaskManagerInterface extends PluginManagerInterface { */ public function getLocalTasks($route_name, $level = 0); + /** + * Gets base route names for the given route. + * + * @param string $route_name + * The route name. + * + * @return string[] + * The base route names. + */ + public function getBaseRouteNames(string $route_name): array; + } diff --git a/core/lib/Drupal/Core/Utility/RequestGenerator.php b/core/lib/Drupal/Core/Utility/RequestGenerator.php new file mode 100644 index 00000000000..e1efb14f036 --- /dev/null +++ b/core/lib/Drupal/Core/Utility/RequestGenerator.php @@ -0,0 +1,77 @@ +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 | ResourceNotFoundException | MethodNotAllowedException | AccessDeniedHttpException | NotFoundHttpException) { + return NULL; + } + } + +} diff --git a/core/modules/block/block.post_update.php b/core/modules/block/block.post_update.php index b14275c7897..e825577447a 100644 --- a/core/modules/block/block.post_update.php +++ b/core/modules/block/block.post_update.php @@ -5,6 +5,9 @@ * Post update functions for Block. */ +use Drupal\block\BlockInterface; +use Drupal\Core\Config\Entity\ConfigEntityUpdater; + /** * Implements hook_removed_post_updates(). */ @@ -16,3 +19,19 @@ function block_removed_post_updates() { 'block_post_update_replace_node_type_condition' => '10.0.0', ]; } + +/** + * Add 'base_route_title' setting for page title blocks. + */ +function block_post_update_add_base_route_title_page_title(&$sandbox = NULL): void { + \Drupal::classResolver(ConfigEntityUpdater::class) + ->update($sandbox, 'block', function (BlockInterface $block) { + if ($block->get('plugin') === 'page_title_block') { + $settings = $block->get('settings'); + $settings['base_route_title'] = $block->get('theme') === 'claro'; + $block->set('settings', $settings); + return TRUE; + } + return FALSE; + }); +} diff --git a/core/modules/block/tests/src/Functional/PageTitleBlockTest.php b/core/modules/block/tests/src/Functional/PageTitleBlockTest.php new file mode 100644 index 00000000000..f6ef4d17a37 --- /dev/null +++ b/core/modules/block/tests/src/Functional/PageTitleBlockTest.php @@ -0,0 +1,175 @@ +drupalPlaceBlock('page_title_block', ['id' => 'stark_page_title']); + + // Create node type. + $this->drupalCreateContentType([ + 'type' => 'article', + 'name' => 'Article', + ]); + + // Create administrative user. + $admin_user = $this->drupalCreateUser([ + 'administer blocks', + 'administer themes', + 'administer software updates', + 'administer nodes', + 'administer modules', + 'create article content', + 'edit any article content', + 'delete any article content', + 'delete any article content', + ]); + $this->drupalLogin($admin_user); + } + + /** + * Check if the contextualized title is displayed. + */ + public function testContextualizeTitle(): void { + $edit['admin_theme'] = $this->defaultTheme; + $this->drupalGet('admin/appearance'); + $this->submitForm($edit, 'Save configuration'); + + // Make sure the title shown is non-contextualized. + $this->drupalGet('admin/modules/update'); + $this->assertSession()->elementTextEquals('css', 'h1', 'Update'); + + // Checking if the title block is configured for showing + // contextualized title and if it's not then configure it. + $this->drupalGet('admin/structure/block/manage/' . $this->defaultTheme . '_page_title'); + // In stark theme it's not configured to show contextualized title + // therefore the value of the configuration will be 1 otherwise 0. + // @see \Drupal\Core\Block\Plugin\Block\PageTitleBlock::blockForm() + $this->assertSession()->fieldValueEquals('settings[base_route_title]', 0); + $this->submitForm(['settings[base_route_title]' => 1], 'Save block'); + + // Make sure the title shown is contextualized. + $this->drupalGet('admin/modules/update'); + $this->assertSession()->elementTextEquals('css', 'h1', 'Extend: Update'); + $title = $this->assertSession()->elementExists('xpath', '//h1'); + $this->assertSession()->elementExists('xpath', '/span[@class="visually-hidden"]', $title); + $this->assertSession()->elementTextEquals('xpath', '//h1/span', ': Update'); + + } + + /** + * Tests the contextualized title if base route is not accessible. + */ + public function testContextualizeTitleWhenBaseRouteIsNotAccessible(): void { + // Create administrative user with access to 'admin/modules/update' but no + // access to base route 'admin/modules'. + $admin_user = $this->drupalCreateUser([ + 'administer blocks', + 'administer software updates', + ]); + $this->drupalLogin($admin_user); + + $non_contextualized_title = 'Update'; + // Make sure the title shown is non-contextualized. + $this->drupalGet('admin/modules/update'); + $this->assertSession()->elementTextEquals('css', 'h1', $non_contextualized_title); + + // Configure title block to show contextualized title. + $this->drupalGet('admin/structure/block/manage/' . $this->defaultTheme . '_page_title'); + $this->assertSession()->fieldValueEquals('settings[base_route_title]', 0); + $this->submitForm(['settings[base_route_title]' => 1], 'Save block'); + + // Make sure the title shown is non-contextualized because the base route is + // not accessible. + // @see \Drupal\Core\Block\Plugin\Block\PageTitleBlock::getTitleBasedOnBaseRoute() + $this->drupalGet('admin/modules/update'); + $this->assertSession()->elementTextEquals('css', 'h1', $non_contextualized_title); + } + + /** + * Data provider for testContextualizeTitleOnNodeOperationPages(). + * + * @return array[][] + * The test cases. + */ + public function providerTestContextualizeTitleOnNodeOperationPages() : array { + return [ + 'node with random title' => [$this->randomMachineName(8)], + 'node with title set to 0' => ['0'], + ]; + } + + /** + * Tests if contextualized title displayed on all node operation pages. + * + * @dataProvider providerTestContextualizeTitleOnNodeOperationPages + */ + public function testContextualizeTitleOnNodeOperationPages($node_title): void { + $settings = [ + 'type' => 'article', + 'title' => $node_title, + ]; + $node = $this->drupalCreateNode($settings); + + // Make sure the non-contextualized title is shown on all node operation + // pages. + $this->drupalGet('node/' . $node->id()); + $this->assertSession()->elementTextEquals('xpath', '//h1', $node_title); + + $this->drupalGet('node/' . $node->id() . '/edit'); + $this->assertSession()->elementTextEquals('xpath', '//h1', "Edit Article $node_title"); + + $this->drupalGet('node/' . $node->id() . '/delete'); + $this->assertSession()->elementTextEquals('xpath', '//h1', "Are you sure you want to delete the content item $node_title?"); + + $this->drupalGet('node/' . $node->id() . '/revisions'); + $this->assertSession()->elementTextEquals('xpath', '//h1', "Revisions for $node_title"); + + // Configure title block to show contextualized title. + $this->drupalGet('admin/structure/block/manage/' . $this->defaultTheme . '_page_title'); + $this->submitForm(['settings[base_route_title]' => 1], 'Save block'); + + // Make sure the contextualized title is shown on all node operation pages. On all pages the title is same as before + // because we don't change the title if it's already overridden. + // @see \Drupal\Core\Block\Plugin\Block\PageTitleBlock::getTitleBasedOnBaseRoute() + $this->drupalGet('node/' . $node->id()); + $this->assertSession()->elementTextEquals('xpath', '//h1', $node_title); + + $this->drupalGet('node/' . $node->id() . '/edit'); + $this->assertSession()->elementTextEquals('xpath', '//h1', "Edit Article $node_title"); + + $this->drupalGet('node/' . $node->id() . '/delete'); + $this->assertSession()->elementTextEquals('xpath', '//h1', "Are you sure you want to delete the content item $node_title?"); + + $this->drupalGet('node/' . $node->id() . '/revisions'); + $this->assertSession()->elementTextEquals('xpath', '//h1', "Revisions for $node_title"); + } + +} diff --git a/core/modules/block/tests/src/Functional/Update/BlockTitleUpdateTest.php b/core/modules/block/tests/src/Functional/Update/BlockTitleUpdateTest.php new file mode 100644 index 00000000000..3d57a0c96b8 --- /dev/null +++ b/core/modules/block/tests/src/Functional/Update/BlockTitleUpdateTest.php @@ -0,0 +1,57 @@ +databaseDumpFiles = [ + __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-9.4.0.filled.standard.php.gz', + ]; + } + + /** + * Data provider for testPostUpdateAddContextualizePageTitle(). + * + * @return array[][] + * The test cases. + */ + public function providerTestPostUpdateAddContextualizePageTitle() : array { + return [ + 'Stark theme' => ['stark', FALSE], + // For claro the 'base_route_title' configuration is enabled by default. + 'Claro theme' => ['claro', TRUE], + 'Olivero theme' => ['olivero', FALSE], + ]; + } + + /** + * Tests that title block is configured properly after update. + * + * @dataProvider providerTestPostUpdateAddContextualizePageTitle + */ + public function testPostUpdateAddContextualizePageTitle(string $theme, bool $contextual_title_enabled): void { + $block_settings = $this->config('block.block.' . $theme . '_page_title')->get('settings'); + $this->assertArrayNotHasKey('base_route_title', $block_settings); + + $this->runUpdates(); + + $block_settings = $this->config('block.block.' . $theme . '_page_title')->get('settings'); + $this->assertArrayHasKey('base_route_title', $block_settings); + $this->assertSame($block_settings['base_route_title'], $contextual_title_enabled); + } + +} diff --git a/core/modules/block_content/tests/src/Functional/BlockContentListTest.php b/core/modules/block_content/tests/src/Functional/BlockContentListTest.php index c18b40352be..4271b54daaf 100644 --- a/core/modules/block_content/tests/src/Functional/BlockContentListTest.php +++ b/core/modules/block_content/tests/src/Functional/BlockContentListTest.php @@ -138,7 +138,7 @@ class BlockContentListTest extends BlockContentTestBase { $this->assertSession()->linkByHrefExists('admin/content/block/' . $block->id() . '/delete'); $this->clickLink('Delete'); $this->assertSession()->statusCodeEquals(200); - $this->assertSession()->titleEquals("Are you sure you want to delete the content block $new_label? | Drupal"); + $this->assertSession()->titleEquals("Are you sure you want to delete the content block $new_label? | $new_label | Drupal"); $this->submitForm([], 'Delete'); // Verify that the text of the label and machine name does not appear in diff --git a/core/modules/block_content/tests/src/Functional/BlockContentListViewsTest.php b/core/modules/block_content/tests/src/Functional/BlockContentListViewsTest.php index 666a8e5fcdd..f00a8147252 100644 --- a/core/modules/block_content/tests/src/Functional/BlockContentListViewsTest.php +++ b/core/modules/block_content/tests/src/Functional/BlockContentListViewsTest.php @@ -148,7 +148,7 @@ class BlockContentListViewsTest extends BlockContentTestBase { $this->assertSession()->linkByHrefExists('admin/content/block/' . $block->id() . '/delete'); $this->clickLink('Delete'); $this->assertSession()->statusCodeEquals(200); - $this->assertSession()->titleEquals("Are you sure you want to delete the content block $new_label? | Drupal"); + $this->assertSession()->titleEquals("Are you sure you want to delete the content block $new_label? | $new_label | Drupal"); $this->submitForm([], 'Delete'); // Verify that the text of the label and machine name does not appear in diff --git a/core/modules/field_ui/tests/src/Functional/FieldUIRouteTest.php b/core/modules/field_ui/tests/src/Functional/FieldUIRouteTest.php index e75e5714cc9..9f93f2143a0 100644 --- a/core/modules/field_ui/tests/src/Functional/FieldUIRouteTest.php +++ b/core/modules/field_ui/tests/src/Functional/FieldUIRouteTest.php @@ -45,7 +45,7 @@ class FieldUIRouteTest extends BrowserTestBase { $this->assertSession()->pageTextContains('No fields are present yet.'); $this->drupalGet('admin/config/people/accounts/fields'); - $this->assertSession()->titleEquals('Manage fields | Drupal'); + $this->assertSession()->titleEquals('Manage fields | Account settings | Drupal'); $this->assertLocalTasks(); // Test manage display tabs and titles. @@ -53,13 +53,13 @@ class FieldUIRouteTest extends BrowserTestBase { $this->assertSession()->statusCodeEquals(403); $this->drupalGet('admin/config/people/accounts/display'); - $this->assertSession()->titleEquals('Manage display | Drupal'); + $this->assertSession()->titleEquals('Manage display | Account settings | Drupal'); $this->assertLocalTasks(); $edit = ['display_modes_custom[compact]' => TRUE]; $this->submitForm($edit, 'Save'); $this->drupalGet('admin/config/people/accounts/display/compact'); - $this->assertSession()->titleEquals('Manage display | Drupal'); + $this->assertSession()->titleEquals('Manage display | Account settings | Drupal'); $this->assertLocalTasks(); // Test manage form display tabs and titles. @@ -67,14 +67,14 @@ class FieldUIRouteTest extends BrowserTestBase { $this->assertSession()->statusCodeEquals(403); $this->drupalGet('admin/config/people/accounts/form-display'); - $this->assertSession()->titleEquals('Manage form display | Drupal'); + $this->assertSession()->titleEquals('Manage form display | Account settings | Drupal'); $this->assertLocalTasks(); $edit = ['display_modes_custom[register]' => TRUE]; $this->submitForm($edit, 'Save'); $this->assertSession()->statusCodeEquals(200); $this->drupalGet('admin/config/people/accounts/form-display/register'); - $this->assertSession()->titleEquals('Manage form display | Drupal'); + $this->assertSession()->titleEquals('Manage form display | Account settings | Drupal'); $this->assertLocalTasks(); // Test that default secondary tab is in first position. $this->assertSession()->elementsCount('xpath', "//ul/li[1]/a[contains(text(), 'Default')]", 1); diff --git a/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTest.php b/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTest.php index 631159a8b3e..ac6b40b4e6f 100644 --- a/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTest.php +++ b/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTest.php @@ -531,10 +531,10 @@ class LayoutBuilderTest extends LayoutBuilderTestBase { // The original node title is available when viewing the node, but the // pending title is visible within the Layout Builder UI. $this->drupalGet('node/1'); - $assert_session->pageTextContains('The first node title'); + $assert_session->elementTextContains('css', 'body', 'The first node title'); $page->clickLink('Layout'); - $assert_session->pageTextNotContains('The first node title'); - $assert_session->pageTextContains('The pending title of the first node'); + $assert_session->elementTextNotContains('css', 'body', 'The first node title'); + $assert_session->elementTextContains('css', 'body', 'The pending title of the first node'); } /** diff --git a/core/modules/system/config/schema/system.schema.yml b/core/modules/system/config/schema/system.schema.yml index d9f36cc507f..d114cedbd18 100644 --- a/core/modules/system/config/schema/system.schema.yml +++ b/core/modules/system/config/schema/system.schema.yml @@ -380,6 +380,14 @@ system.advisories: type: integer label: 'How often to check for security advisories, in hours' +block.settings.page_title_block: + type: block_settings + label: 'Page title block' + mapping: + base_route_title: + type: boolean + label: 'Display section title instead of current title' + block.settings.system_branding_block: type: block_settings label: 'Branding block' diff --git a/core/modules/system/src/PathBasedBreadcrumbBuilder.php b/core/modules/system/src/PathBasedBreadcrumbBuilder.php index 749dad24f7c..2dd5048e87a 100644 --- a/core/modules/system/src/PathBasedBreadcrumbBuilder.php +++ b/core/modules/system/src/PathBasedBreadcrumbBuilder.php @@ -8,9 +8,8 @@ use Drupal\Core\Breadcrumb\Breadcrumb; use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Controller\TitleResolverInterface; +use Drupal\Core\DependencyInjection\DeprecatedServicePropertyTrait; 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; @@ -19,11 +18,7 @@ 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\HttpKernel\Exception\NotFoundHttpException; -use Symfony\Component\Routing\Exception\MethodNotAllowedException; -use Symfony\Component\Routing\Exception\ResourceNotFoundException; +use Drupal\Core\Utility\RequestGenerator; use Symfony\Component\Routing\Matcher\RequestMatcherInterface; /** @@ -32,7 +27,20 @@ use Symfony\Component\Routing\Matcher\RequestMatcherInterface; * @see \Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface */ class PathBasedBreadcrumbBuilder implements BreadcrumbBuilderInterface { + use StringTranslationTrait; + use DeprecatedServicePropertyTrait; + + /** + * Defines deprecated injected properties. + * + * @var array + */ + protected array $deprecatedProperties = [ + 'router' => 'router', + 'pathProcessor' => 'path_processor_manager', + 'currentPath' => 'path.current', + ]; /** * The router request context. @@ -48,20 +56,6 @@ class PathBasedBreadcrumbBuilder implements BreadcrumbBuilderInterface { */ 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. * @@ -83,13 +77,6 @@ class PathBasedBreadcrumbBuilder implements BreadcrumbBuilderInterface { */ protected $currentUser; - /** - * The current path service. - * - * @var \Drupal\Core\Path\CurrentPathStack - */ - protected $currentPath; - /** * The patch matcher service. * @@ -97,6 +84,13 @@ class PathBasedBreadcrumbBuilder implements BreadcrumbBuilderInterface { */ protected $pathMatcher; + /** + * The request generator. + * + * @var \Drupal\Core\Utility\RequestGenerator + */ + protected $requestGenerator; + /** * Constructs the PathBasedBreadcrumbBuilder. * @@ -104,31 +98,44 @@ class PathBasedBreadcrumbBuilder implements BreadcrumbBuilderInterface { * The router request context. * @param \Drupal\Core\Access\AccessManagerInterface $access_manager * The access check 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 + * @param \Drupal\Core\Config\ConfigFactoryInterface|\Symfony\Component\Routing\Matcher\RequestMatcherInterface $config_factory * The config factory service. - * @param \Drupal\Core\Controller\TitleResolverInterface $title_resolver + * @param \Drupal\Core\Controller\TitleResolverInterface|\Drupal\Core\PathProcessor\InboundPathProcessorInterface $title_resolver * The title resolver service. - * @param \Drupal\Core\Session\AccountInterface $current_user + * @param \Drupal\Core\Session\AccountInterface|\Drupal\Core\Config\ConfigFactoryInterface $current_user * The current user object. - * @param \Drupal\Core\Path\CurrentPathStack $current_path - * The current path. - * @param \Drupal\Core\Path\PathMatcherInterface $path_matcher + * @param \Drupal\Core\Path\PathMatcherInterface|\Drupal\Core\Controller\TitleResolverInterface $path_matcher * The path matcher service. + * @param \Drupal\Core\Utility\RequestGenerator|\Drupal\Core\Session\AccountInterface $request_generator + * The request generator. */ - 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) { + public function __construct( + RequestContext $context, + AccessManagerInterface $access_manager, + ConfigFactoryInterface|RequestMatcherInterface $config_factory, + TitleResolverInterface|InboundPathProcessorInterface $title_resolver, + AccountInterface|ConfigFactoryInterface $current_user, + PathMatcherInterface|TitleResolverInterface $path_matcher, + RequestGenerator|AccountInterface $request_generator, + ) { $this->context = $context; $this->accessManager = $access_manager; - $this->router = $router; - $this->pathProcessor = $path_processor; + if ($config_factory instanceof RequestMatcherInterface) { + @trigger_error('Calling PathBasedBreadcrumbBuilder::__construct() with the $router, $path_processor, $current_path arguments is deprecated in drupal:10.3.0 and is removed from drupal:11.0.0. See https://www.drupal.org/node/3397213', E_USER_DEPRECATED); + @trigger_error('Calling PathBasedBreadcrumbBuilder::__construct() without the $request_generator argument is deprecated in drupal:10.3.0 and will be required in drupal:11.0.0. See https://www.drupal.org/node/3397213', E_USER_DEPRECATED); + $config_factory = $current_user; + $this->titleResolver = $path_matcher; + $this->currentUser = $request_generator; + $this->requestGenerator = \Drupal::service('request_generator'); + $this->pathMatcher = func_get_arg(8) ?: \Drupal::service('path.matcher'); + } + else { + $this->titleResolver = $title_resolver; + $this->currentUser = $current_user; + $this->requestGenerator = $request_generator; + $this->pathMatcher = $path_matcher; + } $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'); } /** @@ -169,7 +176,7 @@ class PathBasedBreadcrumbBuilder implements BreadcrumbBuilderInterface { while (count($path_elements) > 1) { array_pop($path_elements); // Copy the path elements for up-casting. - $route_request = $this->getRequestForPath('/' . implode('/', $path_elements), $exclude); + $route_request = $this->requestGenerator->generateRequestForPath('/' . implode('/', $path_elements), $exclude); if ($route_request) { $route_match = RouteMatch::createFromRequest($route_request); $access = $this->accessManager->check($route_match, $this->currentUser, NULL, TRUE); @@ -195,40 +202,4 @@ class PathBasedBreadcrumbBuilder implements BreadcrumbBuilderInterface { 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 | ResourceNotFoundException | MethodNotAllowedException | AccessDeniedHttpException | NotFoundHttpException $e) { - return NULL; - } - } - } diff --git a/core/modules/system/system.services.yml b/core/modules/system/system.services.yml index b56f401f9f0..8b02ea0640a 100644 --- a/core/modules/system/system.services.yml +++ b/core/modules/system/system.services.yml @@ -25,7 +25,7 @@ services: Drupal\system\SystemManager: '@system.manager' system.breadcrumb.default: class: Drupal\system\PathBasedBreadcrumbBuilder - arguments: ['@router.request_context', '@access_manager', '@router', '@path_processor_manager', '@config.factory', '@title_resolver', '@current_user', '@path.current', '@path.matcher'] + arguments: ['@router.request_context', '@access_manager', '@config.factory', '@title_resolver', '@current_user', '@path.matcher', '@request_generator'] tags: - { name: breadcrumb_builder, priority: 0 } path_processor.files: diff --git a/core/modules/system/tests/src/Functional/Module/UninstallTest.php b/core/modules/system/tests/src/Functional/Module/UninstallTest.php index f7a126a2ad0..5905214acb3 100644 --- a/core/modules/system/tests/src/Functional/Module/UninstallTest.php +++ b/core/modules/system/tests/src/Functional/Module/UninstallTest.php @@ -72,7 +72,7 @@ class UninstallTest extends BrowserTestBase { $this->rebuildAll(); $this->drupalGet('admin/modules/uninstall'); - $this->assertSession()->titleEquals('Uninstall | Drupal'); + $this->assertSession()->titleEquals('Uninstall | Extend | Drupal'); // Check that the experimental module link was rendered correctly. $this->assertSession()->elementExists('xpath', "//a[contains(@aria-label, 'View information on the Experimental status of the module Experimental Test')]"); @@ -177,7 +177,7 @@ class UninstallTest extends BrowserTestBase { // Make sure confirmation page is accessible only during uninstall process. $this->drupalGet('admin/modules/uninstall/confirm'); $this->assertSession()->addressEquals('admin/modules/uninstall'); - $this->assertSession()->titleEquals('Uninstall | Drupal'); + $this->assertSession()->titleEquals('Uninstall | Extend | Drupal'); // Make sure the correct error is shown when no modules are selected. $edit = []; diff --git a/core/modules/system/tests/src/Unit/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php b/core/modules/system/tests/src/Unit/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php index d0fc897a117..f044a84de76 100644 --- a/core/modules/system/tests/src/Unit/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php +++ b/core/modules/system/tests/src/Unit/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php @@ -4,20 +4,28 @@ declare(strict_types=1); namespace Drupal\Tests\system\Unit\Breadcrumbs; +use Drupal\Core\Access\AccessManagerInterface; use Drupal\Core\Access\AccessResult; use Drupal\Core\Cache\Cache; +use Drupal\Core\Controller\TitleResolverInterface; use Drupal\Core\Link; use Drupal\Core\Access\AccessResultAllowed; use Drupal\Core\Path\PathMatcherInterface; +use Drupal\Core\PathProcessor\InboundPathProcessorInterface; +use Drupal\Core\Routing\RequestContext; +use Drupal\Core\Session\AccountInterface; use Drupal\Core\StringTranslation\TranslationInterface; use Drupal\Core\Url; use Drupal\Core\Utility\LinkGeneratorInterface; +use Drupal\Core\Utility\RequestGenerator; use Drupal\system\PathBasedBreadcrumbBuilder; use Drupal\Tests\UnitTestCase; use Drupal\Core\Routing\RouteObjectInterface; +use PHPUnit\Framework\MockObject\MockObject; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\HttpFoundation\InputBag; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestMatcherInterface; use Symfony\Component\Routing\Route; /** @@ -31,63 +39,63 @@ class PathBasedBreadcrumbBuilderTest extends UnitTestCase { * * @var \Drupal\system\PathBasedBreadcrumbBuilder */ - protected $builder; + protected PathBasedBreadcrumbBuilder $builder; /** * The mocked title resolver. * * @var \Drupal\Core\Controller\TitleResolverInterface|\PHPUnit\Framework\MockObject\MockObject */ - protected $titleResolver; + protected TitleResolverInterface|MockObject $titleResolver; /** * The mocked access manager. * * @var \Drupal\Core\Access\AccessManagerInterface|\PHPUnit\Framework\MockObject\MockObject */ - protected $accessManager; + protected AccessManagerInterface|MockObject $accessManager; /** * The request matching mock object. * * @var \Symfony\Component\Routing\Matcher\RequestMatcherInterface|\PHPUnit\Framework\MockObject\MockObject */ - protected $requestMatcher; + protected RequestMatcherInterface|MockObject $requestMatcher; /** * The mocked route request context. * * @var \Drupal\Core\Routing\RequestContext|\PHPUnit\Framework\MockObject\MockObject */ - protected $context; + protected RequestContext|MockObject $context; /** * The mocked current user. * * @var \Drupal\Core\Session\AccountInterface|\PHPUnit\Framework\MockObject\MockObject */ - protected $currentUser; + protected AccountInterface|MockObject $currentUser; /** * The mocked path processor. * * @var \Drupal\Core\PathProcessor\InboundPathProcessorInterface|\PHPUnit\Framework\MockObject\MockObject */ - protected $pathProcessor; - - /** - * The mocked current path. - * - * @var \Drupal\Core\Path\CurrentPathStack|\PHPUnit\Framework\MockObject\MockObject - */ - protected $currentPath; + protected InboundPathProcessorInterface|MockObject $pathProcessor; /** * The mocked path matcher service. * * @var \Drupal\Core\Path\PathMatcherInterface|\PHPUnit\Framework\MockObject\MockObject */ - protected $pathMatcher; + protected PathMatcherInterface|MockObject $pathMatcher; + + /** + * The request generator service. + * + * @var \Drupal\Core\Utility\RequestGenerator|\PHPUnit\Framework\MockObject\MockObject + */ + protected RequestGenerator|MockObject $requestGenerator; /** * {@inheritdoc} @@ -107,22 +115,24 @@ class PathBasedBreadcrumbBuilderTest extends UnitTestCase { $this->accessManager = $this->createMock('\Drupal\Core\Access\AccessManagerInterface'); $this->titleResolver = $this->createMock('\Drupal\Core\Controller\TitleResolverInterface'); $this->currentUser = $this->createMock('Drupal\Core\Session\AccountInterface'); - $this->currentPath = $this->getMockBuilder('Drupal\Core\Path\CurrentPathStack') - ->disableOriginalConstructor() - ->getMock(); $this->pathMatcher = $this->createMock(PathMatcherInterface::class); + $this->requestGenerator = new RequestGenerator( + $this->pathProcessor, + $this->getMockBuilder('Drupal\Core\Path\CurrentPathStack') + ->disableOriginalConstructor() + ->getMock(), + $this->requestMatcher + ); $this->builder = new TestPathBasedBreadcrumbBuilder( $this->context, $this->accessManager, - $this->requestMatcher, - $this->pathProcessor, $config_factory, $this->titleResolver, $this->currentUser, - $this->currentPath, - $this->pathMatcher + $this->pathMatcher, + $this->requestGenerator, ); $this->builder->setStringTranslation($this->getStringTranslationStub()); @@ -174,7 +184,6 @@ class PathBasedBreadcrumbBuilderTest extends UnitTestCase { * Tests the build method with two path elements. * * @covers ::build - * @covers ::getRequestForPath */ public function testBuildWithTwoPathElements() { $this->context->expects($this->once()) @@ -213,7 +222,6 @@ class PathBasedBreadcrumbBuilderTest extends UnitTestCase { * Tests the build method with three path elements. * * @covers ::build - * @covers ::getRequestForPath */ public function testBuildWithThreePathElements() { $this->context->expects($this->once()) @@ -269,7 +277,6 @@ class PathBasedBreadcrumbBuilderTest extends UnitTestCase { * Tests that exceptions during request matching are caught. * * @covers ::build - * @covers ::getRequestForPath * * @dataProvider providerTestBuildWithException */ @@ -312,7 +319,6 @@ class PathBasedBreadcrumbBuilderTest extends UnitTestCase { * Tests the build method with a non processed path. * * @covers ::build - * @covers ::getRequestForPath */ public function testBuildWithNonProcessedPath() { $this->context->expects($this->once()) @@ -349,7 +355,6 @@ class PathBasedBreadcrumbBuilderTest extends UnitTestCase { * Tests the breadcrumb for a user path. * * @covers ::build - * @covers ::getRequestForPath */ public function testBuildWithUserPath() { $this->context->expects($this->once()) diff --git a/core/profiles/demo_umami/config/install/block.block.umami_page_title.yml b/core/profiles/demo_umami/config/install/block.block.umami_page_title.yml index 048f9a69065..b5ac4d8191d 100644 --- a/core/profiles/demo_umami/config/install/block.block.umami_page_title.yml +++ b/core/profiles/demo_umami/config/install/block.block.umami_page_title.yml @@ -16,6 +16,7 @@ settings: label: 'Page title' label_display: '0' provider: core + base_route_title: false visibility: request_path: id: request_path diff --git a/core/profiles/minimal/config/install/block.block.stark_page_title.yml b/core/profiles/minimal/config/install/block.block.stark_page_title.yml index 7b7f098bbd3..bb95d0b64a9 100644 --- a/core/profiles/minimal/config/install/block.block.stark_page_title.yml +++ b/core/profiles/minimal/config/install/block.block.stark_page_title.yml @@ -14,4 +14,5 @@ settings: label: 'Page title' label_display: '0' provider: core + base_route_title: false visibility: { } diff --git a/core/profiles/testing_themes_blocks/themes/testing_theme_optional_blocks/config/optional/block.block.testing_theme_optional_blocks_page_title.yml b/core/profiles/testing_themes_blocks/themes/testing_theme_optional_blocks/config/optional/block.block.testing_theme_optional_blocks_page_title.yml index 1620400e448..104e2726cd1 100644 --- a/core/profiles/testing_themes_blocks/themes/testing_theme_optional_blocks/config/optional/block.block.testing_theme_optional_blocks_page_title.yml +++ b/core/profiles/testing_themes_blocks/themes/testing_theme_optional_blocks/config/optional/block.block.testing_theme_optional_blocks_page_title.yml @@ -14,4 +14,5 @@ settings: label: 'Page title' label_display: '0' provider: core + base_route_title: false visibility: { } diff --git a/core/tests/Drupal/KernelTests/Config/Schema/MappingTest.php b/core/tests/Drupal/KernelTests/Config/Schema/MappingTest.php index 61d8c31295d..5ecb85d0afb 100644 --- a/core/tests/Drupal/KernelTests/Config/Schema/MappingTest.php +++ b/core/tests/Drupal/KernelTests/Config/Schema/MappingTest.php @@ -183,6 +183,9 @@ class MappingTest extends KernelTestBase { 'block.settings.extra_field_block:*:*:*' => [ 'formatter', ], + 'block.settings.page_title_block' => [ + 'base_route_title', + ], 'block.settings.system_branding_block' => [ 'use_site_logo', 'use_site_name', diff --git a/core/tests/Drupal/Tests/Core/Utility/RequestGeneratorTest.php b/core/tests/Drupal/Tests/Core/Utility/RequestGeneratorTest.php new file mode 100644 index 00000000000..573fbcb1b6a --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Utility/RequestGeneratorTest.php @@ -0,0 +1,163 @@ +pathProcessor = $this->prophesize(InboundPathProcessorInterface::class); + $this->currentPath = $this->prophesize(CurrentPathStack::class); + $this->requestMatcher = $this->prophesize(RequestMatcherInterface::class); + $this->requestGenerator = new RequestGenerator( + $this->pathProcessor->reveal(), + $this->currentPath->reveal(), + $this->requestMatcher->reveal(), + ); + } + + /** + * Data provider for testGenerateRequestForPath(). + * + * @return \Generator + * The test cases. + */ + public function providerTestGenerateRequestForPath(): \Generator { + $path = '/any/path'; + + yield 'request for a path with no paths to skip' => [ + $path, + [], + ['processInbound' => 1, 'matchRequest' => 1], + TRUE, + ]; + + yield 'request for a path which needs to be skipped' => [ + $path, + [$path => TRUE], + ['processInbound' => 0, 'matchRequest' => 0], + FALSE, + ]; + + yield 'request for a path with other path which needs to be skipped' => [ + $path, + ['/other/path' => TRUE], + ['processInbound' => 1, 'matchRequest' => 1], + TRUE, + ]; + } + + /** + * Tests generateRequestForPath(). + * + * @dataProvider providerTestGenerateRequestForPath + * @covers ::generateRequestForPath + */ + public function testGenerateRequestForPath($path, $exclude, $methods_called, $request_generated): void { + $route = new Route($path); + $this->pathProcessor->processInbound($path, Argument::type(Request::class))->willReturnArgument(); + $this->requestMatcher->matchRequest(Argument::type(Request::class))->will(function ($arguments) use ($route, $path) { + [$request] = $arguments; + if ($request->getPathInfo() == $path) { + return [ + RouteObjectInterface::ROUTE_NAME => 'User Example', + RouteObjectInterface::ROUTE_OBJECT => $route, + '_raw_variables' => new InputBag([]), + ]; + } + }); + $request = $this->requestGenerator->generateRequestForPath($path, $exclude); + if ($request_generated) { + $this->assertNotNull($request); + $this->assertSame($request->getPathInfo(), $path); + } + else { + $this->assertNull($request); + } + } + + /** + * Data provider for testGenerateRequestForPathWithException(). + * + * @return \Generator + * The test cases. + */ + public function providerTestGenerateRequestForPathWithException(): \Generator { + yield 'ParamNotConvertedException' => [ParamNotConvertedException::class, '']; + yield 'ResourceNotFoundException' => [ResourceNotFoundException::class, '']; + yield 'MethodNotAllowedException' => [MethodNotAllowedException::class, []]; + yield 'AccessDeniedHttpException' => [AccessDeniedHttpException::class, '']; + yield 'NotFoundHttpException' => [NotFoundHttpException::class, '']; + } + + /** + * Tests generateRequestForPath() with exceptions during request matching. + * + * @dataProvider providerTestGenerateRequestForPathWithException + * @covers ::generateRequestForPath + */ + public function testGenerateRequestForPathWithException($exception_class, $exception_argument): void { + $path = '/example'; + $exclude = []; + $this->pathProcessor->processInbound($path, Argument::type(Request::class))->willReturnArgument(); + $this->requestMatcher->matchRequest(Argument::type(Request::class))->willThrow(new $exception_class($exception_argument)); + $request = $this->requestGenerator->generateRequestForPath($path, $exclude); + $this->assertNull($request); + } + +} diff --git a/core/tests/fixtures/config_install/multilingual.tar.gz b/core/tests/fixtures/config_install/multilingual.tar.gz index adf60af587f86584b50a2a67f431093f45aa70f5..7ea550dcba0e8df11ec0bcdb07c61f480ce08cb2 100644 GIT binary patch literal 30742 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 C^5TF1 literal 30577 zcmV)FK)=5qiwFP!000001MFSda@)GH&9lCOZ>l=AsgM#$Nq#z><2atS?QJ_REQkap z+7!ufur2SqtIluT`#I-JE(Ve!KuGc+$xcF4Dv2-?=w4_v8jaTHPb&`!3f-=b|5dxX zQvNp&pHxk68=7hux`E=m9ZmTp>8tPBr%!2|fJl-)`K~oL-=g%NG_*e7yYL}y;lEoC zAhAYvFl*fY$DI z6bs5uPw&W@+Uv*z*n_g78g{2+^#+QgHay@Dt-yvG67(Br)RHvbkdi2c4I75gx1n#j zFy3fL(m1eF590qxk}TN+FBoS3Ph%K0Oe=^;stp~GdI|dNJMPdNfq1kb-73lB!Q+kQ z-+jF>?bsS$TX#pYdJp``aOdvpCj4>=9=;zslP^jG_dO?C)V~4rHl(Zkch(Du5IE{% zY(;LExPiYRHUESb^b-knOd7*D216J(8wnVqq-J9PVrV8{fD0G3lSs#f9&XaL8@3yV z9+;FqgMKgWQWimeXZg&;O>liXf~3$(hpx{S3xn9DeN~ve0r`TMwk1hft>W*YSxBFY z0+Qqh$;kDG(+Jwk%jQ}0KX>>LQ)%+3k0O{Pw4RN{;ONqlMNJBH3gs4XH|56&T z$F6T?(|3S~p_E3)#>73e5UU2`Qd(A`*(}AuN6SkwjXJ=K%N+n-0;BRrXHjMXACruZ z%ZW6asT&bCxPzwW`ghg(QCC4Ss+0hOIPlU$&>`Gc)V>cqQCbAibX(AjL-cn%t2xs` zR9P8o)cMFAn-3#5fuy{sIQsZXXQRq+qCrMqYg!(N<4RH(LC1ZpM8~NkibgOygw-~E zuRVxF9UOy4(}VsH530hXJOq(#4uaITK{OFe9rQdmjNMp}G)BlXa{`M*Q#|=avT&wj zH0X%J!B99`ct(V?7(oPy_b@BS)j3MW0_uPs93yVexQQkIEGe5Xmu03rl`zh{;xvP3 z*0~&khaqZK2OlC3hM2`>x0Gj@*H*Di;5;H`fa5TkaD_*%4b3b;nBRPYQBX}I;3~26@$($v3xB$YdIe;XRDpWWvK{9{&>T6qH#lfR2HM#9V0lU|g;@ zD|KG&PK~6b^EHyzNLnN5l95zm;D|3yzLUdP9QP7@*Xj zAo7SuL6npd>A8)`UB2$m7afCEZ2>n49u%XrUc8Y zH1-TApMjM%l1pnw@|;2MLF8g!<-0=^4;&_Qy3q`fv8hK)EhaGbgDNm{+gZbWSN2-e^wR4Bys&=yiw(^BrqusR$8QhJFBsBDjJ%Xnh-cn423BXK52`gT^&4{?zmBm=_3|^>KM(Vo6$oILlA)^h?oJ&;?Q*dA(bbT zu3jK?XW5<*mgbg29QTs_373Bzo09-hKW(JCvYm+1>h+CwzFyzz^{rmtmb$)$XqR3> z5c*jN$=o7}vw0>_f`tNA{|24Cr$@Q0x<_^l3-5Tb1`i)YcOs$Pp)i{r-^yj{)E$>m3xOp zwz&BEbM#9ZOq!L9RJ23z3k8r((m-tG)ojKVpNcOx}HbS4*KpB2MvVSrbGJETPkP#u(NxhccQ^1tY68;k+_#<^A(e@Rmjp2A8LjG*)8hTZ8(rGcY(e@xG zh#;CslM(KZ1rppU^OMhtNNqAE(n*tB|1;V`ADr)x6@F)9r=oq38=Hh$L|;rv+4Y^^ zIZQln^24RF4)2E|Luso$VRJ{jzDwh3Sk#HW|5*OE#yCRA?w08Z1X~sXw(5 zHToy#o{xY%`S;JjAAwQ#vM^{?%d5g6!A#d!RAbSbu!xF6w0vo*r$oMaJiS@Q=aM9~ zoFPsoUS%Ue@N3=%g;a{KF!P;`f`>xq@%P`PI_aN(B=p~$5jb;zd@xD{E0Q_YaWg}c zU!fLUf~I0OCuo-7Tg;N#hx1ay)HG3OS2#725!kq7N45OPW~Q>{mCpGUcCO=9#t_^d z#{qcmu*wsxYMoD`8l(J(p-RYPryZiXckl78HF5(yZt@t~ZaS_EIo~ka3(<8@RB=A< zP3?z&67Y#1~g#fG1b~Jy>VmQc{BdunGt+IBPI9{lqqq2 zOQ7_|2b4%Ot|rhn>2X#`?kVrwN_|YL%D?nw%D;34%0JF(YQFkUyic*>I2YAO)$-K6 zkR;)AAIzv_aWx1ME-Lq>gU^Y58>{uEsYJDHj$s0@&00Q$WAG>wIZMtrphRQ)SXQ^n zQWJYfO3B$#{y#I*>^2u2qG4Q4K!e)CN_#hsOUL#MrGy2J zvWtZyP;BwO|`) zV*hUxnt2Cs)S+AM01mr*Yi0Jfn9j^Gs-@-QzY+-OlEAu*!)c8vZ!Pc`cNg^cpZure zt?Ms$>66kw*#=u@>dndd*PEN$iyP%)D?I+Ib9Lw=grTR5gv@h?ahuuC++Q4NoRul? zp>>8Me@KG~>aCxQTA9bV+-+OgZ5*znZ2MgH9_N2J?{P=?bG%7DFgXHdp5s{PeqUn^ zb*8aKIia@H_*E@6Hp9?k&n?P^8dX_(wU!$EM(14(H8?p(XrqA`p;BShM57$T*%Vk2 zBuada*|O;B_JYU3X@(38t0h-(6vf;FDb^MyZDx07-?U!7c|x^yp@{+=2;iq4$aTtS z^7CR-0|tK9%n1aMN!G3z6Ijae`neQWGH(vJF&0;lFa01p8OziM%wSoNuGq{tlU2@y zzEIV(IFX(u6ru zlnPIZ&zGyGlFK^UEu?R5T=V!>jI5yOyMnKvKcxxFcj{#Xo@YV8jBN8?|N7Ur&Ex&c z&HY`IR}G-F=EmYIRjLqLBUB8d7nvjQ7Xw~=^!$ALgUZ45YRnEQ*B{<$a4@^v8!ZlM zn}Mv8tF;*rp7WPCtkX|^`APrF4)hVTWVe#J`y_(%f@XF(4x?6cUOx-11~&TzQziE= zdB#vq`FINVx9D-3M3GC@O)Oud#_(J*6+J`OKJWtT&Qzxgoh+hJ9*Rt~+`>srLaxpk z6e-Wtxz}?4FCh27T)2O;{QhE$VmO<&TKbBT{d^f+elG|hTD1}a(BC2ec*&7ldmj70 zZ5p3*UYDuKf5jPl?^Brf3$4%Obf)B-tA#Je5|l%NbxhoLz41VB{`sGxbaedwld5T5 zy`y%zpHQl*b?WoKb%gBv4+0$xz9IJp{jRL(dSCAKfFnD4-x_q<-JYg^=f3|$Icoli zo&Hw{9*&~J)7{hUyWm8-2~UA?v9ov5wukP)7w6*1{hB;@Rh}+;M(^p{@wxYO80wr79p($Z|*~LpdZNyj;FDh7#9c< zkJ~5i?KgW%l`n_ak7s?L?5JjF_wFB$cSp{V|9Jh~>fPRqzTC!*>=Y^|@CI!xYbJt2 zs$FnxI6xbG;!mpa0Nvq;SgwRoaPJaT7x4;`@6wvI>n8ph{+T(a z7oW4ip0X>v<$7*$$Ujs5 zrxs4j!GMMGe~0IP9g$$o|LX{p|250*cU7Rsoo=^>dTb> zzjqJQ?aPNF^UA(BzW-(gUXosS%$_aBa&PbZ-NmtSI6QvXQ{d55{-=sTM(!tuYqRA2 z8+lwy9g9N7{{)=zoVpf8DB_PRw?cTMFtAYm?`Zt~zoXak|60O=x0vml?&hVET*_zh z?btne=v?*gAHtiXfxI=5W%KBKZ#ejIczJPigh0S?eYdPzeD80o!{Sih|6@xB;9G09 z|3~Y;qHz3=$HA!?K;t3x4XZ)Ieb+1&kXnKqW5r~-0r0}y{(=8Nt6c8 z)y3ByD;^XV)0_V$4#eD8veGDme&S7}6dy52WrCBgAbvI?^=xSXr2(++=qnQ5q$OR9 zfPW`Vf)pQh;eWBobmBr>u32Nu8$;_eImB({+m%(Q02iZ0Vq-IQf)9Fgh zo*z9F>pwNGpy#MzI`G_hl$l(-84?!pKVp22{|&?F*8IPYK==P$&31a0)|VZ_cF_Lc zQmFpVGBn-k89l|ex-ZcG2`=-^#~Cj^yBh?WfBRiNGlTuZ?bH41{j0s(t3CCgdln8i zjj^2cBkRv#ljs8kR9YwoPHFV?K1|8MaS$VS_+bwyVdLvOE=|A+f$*Y0^7 zKJ0snH#s>s9?UP>r{Vqn=IM^*{@6MGw)b##GO_?9NZi}4qSilHjO<8HmlK6^ug! zif_e)h3mh9*MEHGueH%HEdO_F`@i*sO#ZhzJr$@x?z9y{)~t>r1KWXe544?u1z^_z zFFF5b*Z*gD@aU@B=bMrF?fBFhrpAtOd!QX0d+Ool#T^)p;pB2}YxiRFY65!HgGZS< z^JRZK`^jIpqVMDE5(2-Nz640`$J745_awYyn8p7k?<}j||D(k|<9}7}>X`p^qn7{I z5-9(-9R)!yG~_Ol!(`1d26Ep~9oYZ?I0n%AcDKJu`Tyvt|KloB?=Ei+mGSMLoBKO) zq+g#Kr}xSCwg!E(>)VMOPOhL<$p5+Tpxk(d{#wlaq&qeDXMyqmHS~fw2GL~6n}GTJ z-%&c6+yJ7{l9zo1O*2EKL!U#{`BZs5x?0(XPIr7{^yB+LrH z`zz$#oG!y(Ay47C!@^g%BdlZ-`X$U$H>C1Z@vaPcFRiO_ScpTG#KCYrlboRL02fca zA0()8B+xbm70vvH{Pd{O1>!TA^#M&c3Mm%1m^H50LwLahla!!kH^u{km;f)Z{Jah# z2#_Q^ib&~l>Tlt7z3N|krq}h_>3Z#S{h8EKJ6*4xuK%`9*O&9S`jT9(;~;!sJ(Fj6 zv1{g==DGG(pyn(*(eag4X{jQ=l2M%ZNbu}Obzl76@a(Dts_9qPwxmaTh3_HHe7Seg zOT0ol%*l$brJ16tN?We)xqg)^W_|xGrPVL#sK3bn+{b_y3NNd86BzI(z^{1~7_Y|Q z7CbWa@$nufSi@Vi%W+^1k_3G^*$Ej98PC*Ytm5T`530#vB}R^L+B%z@EkTuKxV#8S|06Z4?TIxbsZalhUz@14 zBd^hpy#JdPMQW*T7hB$MiyQa+aK5scHORK*rJ-zqsi}w#x|9|*)1{IIj z?*C7(-2XS~`@ic6^!`r=ZIo=i)0LGz_Wx`5Rk_zO40)i#9)woU?zWfe|F=7dyes{w z|6~u_a4WU8+WV(Jt`EK(cu7>a*H;TJr4sIY56W*8 znESuPEShQ=LKuYJk~{(bKYLfUoVt##?|BN&RjRtSiD#QsovJ1w^E|F3b*d{XYE!-F>$oWk1Q$U>jSqft3Y>l}O!t#aObf=^rHh|MyR)e_b*RW#a!U%LxBp z*E#*KBoX>|B+*hGAPKhRID%?vqF|baBxt7UT9U0BP1Rf`{zvB-JbJCu;kF98vb5J~ z^e^qBrys3{qn-UTPpv*`;nhjwOusr56a0_NM2#8E1^E?w_6}G`fIY9{lmYy&4LVTj zmC5sEyb?hhU7c3>&FeoApkahvJk9Bs@y$wm{SMZ~>o;Bzlg=UcXis3=rPjcIp$zn* z47^vd^vC6rE}U}bO*le zA-KZWe&kVXHPwwEi+GkDr25zqg}w7uNjMQ&X{rKIb-IGnKR}k zF=p0bB;N@*ww)oqw>epj$SW7uW2?MPcATdaMAg|U*X+JGoKy=NzBdSYHCU@kDm!0J zG>G{;DhgiKq}8=0+$I~CM;YgI4;6G$a7z9BY#wi z4WoM>(b%cyK2=|*>N1D6o~xo${m8j7=f-8ujhDfVi>mh(*|F_=_6q?uC4fv@1kjDq z;xSTdgMo@jlz0Xp0CUx%ccycTo(+RHwsRcU?}J7JG9WS;dGH@3Cv=BUXyhS`k^H6? z_Ru`&%Up?$U&Ju0?Lfi>i}Ry zWvg5*rF(NRP|bT7s5GO5rLmeRy8b3m%}AZ}daz%J4jWf`23Rv;-AOhGU4SKeWU<0* zbW{1P0*zh&Sgm==gUdBU$^T}7#ERm*Lv|CyPF!FJb!^nMO8Nu+;Kl8D?-U0hh}KE5 z?|qt#oSo95g%C@9_j+drkIjeQB;Aq*+GM|82;G|5qwBeFoa4tB%ws!fTL^lI9yD@$ z?)lD^U+y$#zc&GkMbjF1nVq;8A$fzYLje{ptw+v+!i}l}B~9+oo`JRtX9OfeNL)$% zvID*tpxlCrEDJD~BA2y>%!NhaEF8Cq{zT~)(X@jtjEMnEer0U6g3&S(FN( zY4LVecJt@^x)!k3yeXLM|4o1I=V0MK!1d_upd|u;lO9t7zdwJYdR?M#BoG3WIsBvt@~N1lTv|p+F8ofOdH(z#O3jk*W3WHzn#`tD)F*jc zYYXC}R4)|hk;su?3A}a2$Ct%qL%KCzl*RI34M`=JuT_$+Za4!B`rc=c$0k^oG#{Jf z{3Hv@eQ*Wbi-qMx>C<@Fq&fxc>msk87RAJhzrHN~l{@U5i*YWt0xlMRr6{--ouALq zrZ0)7yyH_B$5ZOHnq@L`YP?44WQH`pcc3iJn+mf(#+wFkeGq~-n%_jXKf-3%M?cTd zw=-zFp_e|tOi|YV9?5SKv70yplYh8j{KMtDV(L znU`b+PD&S8n_zyq@%kw6&|;9y!ef#cPd0D&8gDXr+2ufdUY~QaoET@YE^?6lIFPVA z1jRPK%#L~(bYtU`$u-+Wd0%F!qZm~c*VFe}EPYIi1sI$8#Rhfkq{cQhT{sWCA$@54 zfvAK@kmi&PO*ZP^r?i7TLHli5(ha=;*)|A%@WxE@!UpBNb?sY}V^foD)ttlRxa-s^ z)~omnG-|>Y*QZV?PW)TaMdtAZ1zyd>SWUfYR*2Ov65rJB^%%8f68ba!5~7n?RLAz7 zpk5QLv`~1MOdp~bAzz5>Bk|n^@ZV50xBN`vSb;919PCdKqrYbZyWy-sm{yu&+;+B; ziRJzz?q)JA($)Bd5T>y~_ZkKETGKFaY*;{cHcg*v^A1_;a)lq>B(|-aXqg!gUQU3x zji`EPO)%;6s1Hsi&(ce?kAy>r-xP;$5v1IBbGZ6RMl``vQybQljkh^1++8Mq0o{;O zQhzjgkcbWjoqq2Rayif5M!TI5?a^05Ct{(lp!7h&Xl9`fmte1_0o70nW zJ-SOpdh4R_xglZ<4?7P;iC)MaF=e9siMz{|0BE$mxF- zDT@Cn!T%|?EZ8D21l0jmp$dR1D28n}tFodv`rPrK=Ar+Z|L%^*{hE2$*=~&v_p1l; zZT;w>r|j%k&BtebeATNTw|(pJt%m6zPsxPtqv%NFZkYt?$p(;{P=9;V*?fTF5RyNq z{hXAyARxQ*0aQrvC#Xg-M`1R9jdw95C8<^K`X4n@7JLl?kXpHZ*yNj@X zF?z7U+lzJV=r5Qb6wcCwN1VdU*hjD&7uqPr%L`becpX^$E>guo7@?GW$%xUB{Kw1C z-mj5)VVXt%P}`^Dzrz1>`d>wwPM2+Y|L#?iZfoavgR|DNITHI?^Yr{;@Z23;$HC}hd_?_ z_%PV_P+fdfQWQ)5CzqV9q4|Vc-wgxOh&f*JX?&%!iTk|L>jxnU0*$({Vg=54fs9}# z{mTlY|8x1jniT2(rYq})W($gr^nb&G`oG)M1qsFGb!5A$o9eI0e{zY)ZH6kayL}rN zujfBrf%gb58&^Bk-pPY@7}SruPuGe%G>p5$gX`K(`|cr81@MF|5koDqbBAwa45x4{ zBC`nuGk|tAplrW51#i~D97 z#?|)uvC;tdrx(4h*)@%pWsAz$u)U+Twol$KeB`QNZVQ=jC z54W1BUOb+k?OZpn#$vty@>o57Kd>~jgiMAbKt{YE{_K|VG*3$Q|FLbyGPM6W_Wzml zKUq=v`Tt7NjB~p2O|@s9-`cj{tesr#HoW%s`24ihf0U~3x&P|y>F{`UEi(PLA%t0 zk_r%`O$_M?tF4MkWKyJxYIM~M17t(DiMI4M;UiwBMC`EOJG#K`H`>7@umLduihO+< z*yRbbuM*>=idyyAgSCWI*^IYTD`dN*@;(|Zm=DZ8|7Z694Zi+gO(KLUS1m`?L{X4+ z+Z9yVRE4Ue83HgwOSE+hNB}HS|5vmfxB0{ByaR9Cw_6IZ-O=d1v41lhyd6G2IE}{} zyWPGL`uF7gAI`}{^*;;xWzfZT!KQ;;Gp?(baZ?3bl~oZo#9JxKfXA*3XdQ5N3Daa=ku%K zjk>3Wm!;>f+iJWHqOEa5<0SVxBv$?pI*aB3Gv&W(F!EpJ=YK0nk^Qf(>!R*xf~uLa zpju*806;VaSp$YFsc?yANQ;#Jkgw?e$dJeW#n=k!SyPsf5Fj78$*1bAa@GIzs$w|Tq%+N*nVL-8vwJ<|7iIys|J_Tz5UNoKMJiI=hX`yZ0*Dvio`>YdzR!QPPR@rPGWc9nU z^0|YoB7b(MV7I#s&=LP#H z4)QURfqX)2kEv2&dQ6Yl%p#Yycxiy?9CZGTfBfSgC$+Qv+uHtagGP~qc^g}^i*P3O z)tD3yYm`)Sx$G+!d>>=d`DtX$)d28EaX`{dq;^731B6V?(B4D#Ti*mQI4uxTbsVsf zKnHozKWECI!4jf=B61IMLP*Uag0j^~Ngt@zq7Bb%Wn;FsPz>bLA`Ik7T$daGe&@0<$5JL&!X3Y;9ANv88!yK3wV_RpWq+TPLdW@Kz1`wes79@$v_<9Qj&{u}%w zyD@c(!hWDqcyZbLqZ~BhN&>E{9oH#a0ZqH=T0evT9ije~$U-|UVB&~6 zSq}kH0{%!qbn#v6Jv5z>)PK~rjRR0?){fk;)*j!O+w!rXzm7b6Q0<5p_VHb}9ty&l zN*GCMf=7tG(Pu)pg*izNDlQfXiG@<)O(-ES-=T-Ms5%4RPr$=BNYxuSXb{0Y#xLI} zr44lPF-c*`Wb3_#etW$>0UVF-Pj=9TUe6-|UnjLmx-L?@B-~To*zVCCbwS5zAh_XZ zTbpd%;a7RYRr}lQb@0TdJpZpnD}dAazbZ*0^8eOkU6piISCIavD;)o2C5iBVr>VJW zwF(5giSQp~#Su|@(6CRtV!%pd=W;qE`@|25n`{_(KCUk_^cLVwSZj&{Rsv;TN< zJsK(gk=P7^!CkM_wXJP6?f(@khda^FDgIt`$h)uR_!V~nxk>wb;ilw9q)u$0@&b3K zI)Xg~D}3r_EU)sOU#6GFJl5`OWIn!BKfPr-)!oqF`kv#rNKY>OCVDEsdmotz*~dQD zVKZBoIlQ+bR)?SDo?P6ME8DN!lWUcpTr_L_UXEO3S~s#rM7YTjkApf}7I!tqFzkD) zQmA(unO=-mrPQc1J{>>jqVgeKhIw@M@Ln@t``*KA1%dB^v}JrgrzB=eTV=-CEzXME z;2O9dTjkqMcVPKm?};*|&K5>h+A3c#C0F3vwp_@A+pa^|k$>}fmUvswoM|a5xi}_q zIy;&OCYhVm7onQfqVnhbWW({AP5Rw#2hGr2w+1*l2mL{q zdgni}(d7{MjrIPj%N5IC!g+E zV#|GExsJFI#pZrFdh|RmkFQ}#<@p+xuVMKbcHy$m!9i)G(AKHET$GIFAmvh(htB|7 z(YqSRlLH02jST_SG|#4hGqf}@;8~NUCo*-<;Ggo}fBuJ|*r6AoBdCqP|NM9PBKk=? z&xdH$x$)P(%6sUNc2&fgC-6`Cum8q*iv;o5!~XOcQIkgTpoZi-f_RjJA>3@rT}-KF3kQI{=>IBvb3O1 zpP`khXb-vj^o!z3u#N>xsqg@GVcaR_+e>3k*|GStyotj;B$Zq?*IM>@!l&}CX-5?7 z-unz3tPN>dN>x6-40BkrD2^4ulC6kmF=MdCTr168s6m{?T#{pysKFm&HaVU3`!gJB zEF}6yV%EtXMNK*(pEOCzB~X;)!cP^Qkj?+Gl)&Q1Uq6ZbwM<V$aSv|Q+mqL;< zRUj9k4R?I%y{kZHiw*BGeL(qK6#F|Q5}4g+i<(5QaY^)a%9Jcwxa6r!u>nht@dzAk z2aaclIQhhv6bbom-;Ii-eq;-UK{r0*m|RosjEpYWwkF}@*b1`Ds@Rq- zXto`BFI$&}bVDydMlymQyyemM7Z{uzTe=T5#c|hZd?q~fx14v3-Zx?RU!pw|+4L2f zGm)dee`}B_K4iN}?+w~*Y&Ml$w&M!qGY}i(EnxDOHVZt{ zc;kh31@Fcg3xI5mZ9DOhZbqHDnbP19J_$XmHzIa{Cgr64x$fr08wD|ziXcs_zAzXH zS;EA~Fqv7AdI9`2-Ojjr9b|Xx|6IKaKma~aT|Un_e9Z5g^6n z)A+xdE^_>rRV0G{B030s$20}abTmP=C0D2#jw4uAS&}u;at%lO3H+CzhVe4T-G05P z{|N6#?${mPzN?)JZP*Nr?f&TUb!Ypu<8_|ypl2>k^*63tICq|*}sBsc~3gz zvST96NKWh|PP`x-pc05HCiyPD6UvsrL1gy#I*oyG&7i!Zy;u#&!Z67b9d0-U2CD=Drlgi?#W z27&Va>LryLoBYnsfHVK!fKXE^1`^v)dg-Po`M*L1w&K4ml+J%u77qOXUQ!bOLsWT# z=Q!4|C4-f!3df=>z;co#0b9}(0F__!|M)TbM_ofuu6hg6z8F6ar+3`rb4Ssg_S>tj zSbR7&rr~fo<`)aVW$3>FN;6{6Mn##86|I73zzXkEw*S3$=-?({VjlSqzT#NnKe;S+ zmphC4vr1dze@JWo|0Vu_|9eTc>%lk#eq_T?&onQHZ2U3GE)~&=s%>d`6E3{Q;QVfQ zXLdc?jGi86cC){HtxM)BKfJT-!9Nsr{t>Bs{u7@{G_oA|cL&G+C(i$}{I5!!c)-$Ew3rO+Z@{44r*$_0=yrCh)} zt^F2EKvDk3I&@V2Cr>{3A{7yb@E2Kvt@uy;zZ3ai-~{adUFF5Y{oh_va{mVr_pgXN zt7saM{}ox-Abc)7i@ zhPCsD#|N<$L3h&3@IQmjO6Dpf`Mq8ECqp_2|Nm9E|M%e_h+XjO=Y2VCkN;Ud|0P~I z#Q)h#+7kb-0dJc9rWQXak8jIS)Hl?&eRJl@i<`%%-n`b~=dfiypWhnoN(6~=zzNZL z3Bd@)sTmHjD8licD_AXU5@JIY;_{1n-KwPSw^HjFxKbR!?+Fm=8(6^zJM-9B;Rr|D zq3685B2tkX!&IXAeeru+%J_dX+%?)~X&e3vLizqr5XA%kznAne{&Vs@=kt1XdHKAY zH_m&HuZzpG$H~>gvEWhw%8L&A%_rCAq(&unu;<3WBYKUAE+KJSq|ZR;6Du2D6{;Lp zE+&CPQK7(T3M&2X75Tl=@gJ0z3Osn686fVM4s7TD%kh6Djz7fz+)JYTza$unEQk`T zah%0UhAOZ?RYg`aP1ArPlw_do^8No+Ubfm@>+1CFj5FHPo)tUtJv4^n@anWW(1r7- z%Qwd!UoC?f;rBDh%tZWJnnQT`qnrYO8&Q4b^ldVyw}{8XmOaq@-XX%%_;0Dc(>KKb z=H)g0KarCU{Qq9k7XJU`GHQkOG2eH(+}(6CzPvVXmaXpMVfqN?Be{Cj?_YN6fx0j& z?`fS!LwBqvvNpxY=*`n3ci;o;Vmi&7xI{c~QmYGKSuQmlC*~Ax(Q`1crocB5zrSv1 zl1?O3!CMs}Qb2rE<0u@MfVue9uh1^>Dhg-BDjqwtE_z4B1233;Y;>VLb0D4;FoJ1O zpC6m8=jsA)1{RU-xl^bo{bqe-PDrVZH1HRxQAdEIUiga&wFwE~EW~n7;$oIiILn=$ zF!kmGUd}N<r$aKI7JKiKoOBcgvq{EvkG!Jb+j8T}*Ue-!kOfL1fJ{ zfH};<03e#@`oxEBB_Vahm@KPjI)S_LS@PWr@tdai^goGIxOe?enUh4S|EV1GKlhTT z{HH+;Sejz9hHPnA|4U*Gv8u4DVVWYeESWd>pV$BV!~@Xiwt0>S$MfsRf9Qo;du-OF zvp%?L=;z}uUt82Iy&esh@rBb~JpdK-Kkrcvc1#{jKBxbAcmg6RyZ;TNFOmIl+J66A zmj48SJG}qyC4G#ReeY83bcT{LcI^3tANB6-*?>Rep5MSlbbkixlBZqW`nO*FzDOQ2 z--g+Va>5`8!}d`$z#z|6u=fuidbIN*x_b^TBIaR0lP zw8bo={Rq|PiF7N&I@`IoJCobmzitiRDb$l4T?T zpgsCZ`;V77_gd$#Jx7jTz{S;~I_bhYU1*<8wDWpfnh(@IxWBq@7{lhu{-Xe;nH*3j zxvR&W4OSmVRN6f$G;o5e9Q__jpSiTj{{#9Lc*(Ntf*aU||B6`F|4;<^Aph?rCH7yE zpjoB{S=kg7B>w}G)l8_drfES%6%EUV>R0-IxUucgEn7S7E#>(9^>OqN-KiZIP5ipw zaHrE?>|gn(uU_@K)5!UMD8T6_T|n^fx_~IO_Kp{bO?7gPAVL@WBcr~7n55R<+$Y4w z^MB%_!Ht4lu>f1of2Dl?uL{yZ{@+VV^uIN+3Jq0d`KrpZl3g_;S-&0M&b{Gc^y>Ssta+BR|13jk#sb{I zQ7~QEf7+-P{)7I-UR>Tc1YK+JH(&;m`~O@3>EjW!+{tK%w*Xt^f2Dl>OR}UM@P8kP z;=f&m5|m}YYO*Gv^B-1OU{`sTQ#mLLiY95+PW8Y1WlOd=LF&9dHJ|iq-|H)r^V&pH zMQ>yeN0&qI>Wmd!Ev(V=o`PhK$EWTHX*LOxepGV&G6pe@NR2E1U`Bsfe>@{fba~<) z?iPzFJO8n7!{g-rWZ(Lq{F?rksvhqD_L36&PpF8J31qekd4rWe6|hwqTC6ERQxGi4 zRK%U)e_KCws(cXD7x%Z)?18%&-0L0dwDUGxwySE?n!kcG>-<$4`HB7~h0BePeZ(tg z`Ngk=ebrLmyU=3dfEa_~QyuXIlfp}3N;ZDeH^5Eu{|H7A_BOgJGO$(tm)HD13i9Fn z-%FzSuL1#TRm)-(PPP!&FGy@v0RSt3bCM>iJSYCV{U<$Oe&$+0Zg;GqXph_1bA3KP z?VC%X&-3+`^whZLFXi6g(yN|E&(?WV%Uuhk;Jf)oz;OqThc5%5l~Wpj=T3kQJ!|ou zTY=5BR`&ulRw0v?f8s_US+YIc32fm1y}%#rlmpoQ{ zy7odJ0XGc?=m#;%!QGqKD8r+@;}B_y9mE(!(JTnDp+kgY1t!@^s64{Z)4+EnL6SN>zMdG+sX%vUhAK(BFpOj~a{y+R){1>D(`)@@! z@c(;BTl})Onk@e`n6+ZfdA{=_T{WwGPZ0%6xpJ(xr|U?X)*T2gltw`SuVnEG;XEdm zN|@(OAd?rW71yM-s2k@12l2rJ`GZOh5dq3SaHWIN&ex{o{I|Uzu)avWz@Zy%!y5+wA%_cDV^ zcdRG6zA}e@%v|3JW+#k@oji~OE-j|N(sF@6c!Aho%I^Oy*S5dO{|m3K`+wmM_`jF* zG5+(9z#oBzGk?^l?W!(c$C2g7uhll_YWJs?PXo^DD4ci`J+>=p$S$Je@54ystr|5Gc0qb@n5Vft|invy;u27d3t!MT||?I`uM7ky}&#Vn>VxT@V?s}ht+4Q z?^}S(l@FMFZpjY}fA9nIsg&S9zLWVX_J14xtBNd^@SjI{2mIeh+G3U~U5R(yU@8jT z#@poX`J!da#opMeUrgTSnx`+X;?C4{-mJ4hkvx>H^Y9Ja#CS!bK1SQ*-JaH=(Q%1K zI2gr@9fZt(Fi4&xk7F#3xA`XnV&*^CqvHwriC@c%z(TUd|FYygalAH5~^@ zm&_uVtkeh4Oc?`0pBVuQmijrSZ9E4PRJvgNV79RdG=BQgB=lG#bfNtxV=NfcbIl>* zc9__4Bg_E}88kaH7%y-p4n8^t5x#a~+?a8FG(OU*5e73nn@VnGv{x3j|0m7vbo-4H zrtiSwG6r^WWT;<1h|v;EVHmjvI^^l+{cJ>VK6XPGq5jb{AY-9v;*p`R96Yj_>v?F2 z#)!Z(u1y9?n?%F1lEzq;$uAlcLog~`SQa7R4x()`iN_ko3kIm`oWGqf1_3f0q7IUd zkaYMfAc3ZUmv6T_jy8kEx4bM_pxn-{whrw)gC%Y>RZsTnfrj_{KK{ppLb6f>2$FY#VvQfLwU+=nuND{V(b{D;b zA98{5z(h#4s+=rkp}g_O={t|`(iZ++GI8wNy5jjccLuxHO%p`(&ldxONm|}J2Mi=| z@ISD_j@)D^6EIka{&CI@_iY`hDi|Q#3KK};m>VkvsG&B`VxbXA|AVVl>*IOH>C_no z2UvYJBySdEuKrl{zy8uzMF$+y^RawF1N*Fwy(Ah3=-460-9DkR@|Ws$KAiLYAPlPO&-)urcZt??P1cN-v8Yet)Y6OKvN6;t0dm7B}RF)hIVI_3Qk*kQ1@i-?yN@ zRWMwA4*3@*mNAn)29WH6R3N#sFsuIo`VT-+CGzXk+q?9!%EL0ojBhVlGp@+Rp%2fH z5ZV{er)~Cie_MA1I9`{2RGZ25w9#Sp|NL>w;luaE_y9S1*t_AgV}<5`anGz$Y1jBhV$S285qg zYZ`sfvZ~0+405PfK

;DK(>kD-aPl`jng)=rnpJSz~eL>gt>{*=inua>8l-e56j5`*PVT zj&A$;ITqEJ`Mx;MkhL?#^T00H3%t`nvg>|I5~X>Q1~`yPvl+wHs`FE#;gt0LP1cE? z@J_)^nz8;$)aY4d;#Mxeo_@b^rpuc|%it#$^K{im>Jfu+|K__*iB4yc`|rmMs5WMU zwRGAgg;}9{{U9!|rSY#t;Y0;a^09OkrwYkyX7j%?JxPly{d8`GGB3Cx}}Ca?G?1ldgDQ$I!%NgSD?%|lpG9FR$rG`4NEdU4{r z?;%4{evL=NR*F%|n9?)CR&%B&!SQyX=PDV2x>v_0)!b;;R_;$4a(1cl?{WcYE&Q+h z3t0-&)nDa zy(u34nUSi65puI*suCK%fRvtZd4jfgMH~*RlNt9fP-}Fg#QhUh9j_A6ie)K+ZzB_> zbT>bpd}m}vv8i_H<^Ii212w&gpKw5bxQY1L4<4T%`PmJ0GpG`=_rH5EiQO)AQhpuk zxiXO-zD5UszFW77B*x*GsByI0O~#|`5c^?W>-St!cCcdW>JxzRaDy9auH7>uY&h7l z1KVLI{~Mw)=m~QKI7=OVD6LD5lt6(*B1@W^kVrJ|9tVCL=nomu`n`)Bo07QN67qrV zoa*merm@lM&8<@JgWZ3>V=1pFuYQDTZK+&6k5Hr*!r80(bz{O(i9KcVv>1G2FN|6j zO6^)#LFiQHk3y^R6^~O4!jsKeS|uzPJ&cc!Cm3}eat*k-l{%|azz^VN#QeDjqkaO)E}zM6`2 ztp>TkV9$uG!G6iPivF@45*JNY7Z?WGi0L-c@>;! zq47P;#{ZGZ{R8fCPoiq3mF2Xk7^85>=6TP%jK1tQ8O?k`&%?G-ELKqXvVy~T*`!wA z1srG`DBvTjB?z4*Sxz8I%e5c|Ac#|P2M(oYnpxAS8*0O!%e$brDFn;n^=ULL4jT6T zc{+nZB}YUGMjNc#x0wql+<)=z*Wciu>GA1>X^X^Mtx(HN`?{ROL1W11^#iLa$~mYS z|8TOKf9k8rS)9n?S_b^p-ue@T9-eMHZT!w$$(OZu`Oxyup3Xt;OH6A@(iAu-nn+~- zWy(WC7uYJk4yG_`Foc@2z9>cZ?lpt4Zg2F)d)s~%)oK!88kfISj&-_FH>OZ0KxRasV{pT3ikNsIJ( zcZc2i_crLrJ@k6H^yb`PsTL#_Lk44$ZsUvL7UnATJE{G3pn%&+K+Bn9Za1GwKraT6 zA0EkV`#g>Na=2s!sS~;fnu>WcS$Mx;!ht6QMtkmWMgMXLFC5ggbjzP?sX zuGfrTIvKSpHjEP)!>33-S5iKYr@2*6U-vyMdL(+&3*j|WY>!~S)%SH6ebR$nh>>C* z%?m){NKq?!u(@{v^hnk)^Sb?CD;y2gjHe*)DhJQi97dkQx*zIAHT?vU=&|Fgf|i8L z;cw?0`r8d(P58@8;A>N%z(@AOl)YizD|0t>xVqy&|6iZp-lhyNbVl0@Qq{l?WKMY| z`3}Egrt$|s{W_3a`sTtH^cRyauE_gjp2rUrVccT#&Uh$9@bev06~tSM^q1T``yc*l z_dySL?g}8=m`g2~`@(o6U-jyR5E4EQBP~hC^nKaU`U}rKT31szsjaw9^o3XN{$sZr z5`ZogpzChb{(4BlHKlopCZ@r31w_ojaaia>O-_IQ11y4HKt`IVZ(ybB!b;OYoB6NF zpJ=o!waJ2DR2mr3kk~6%{#@rH@C5uMnt{@vEzF%m?}&Du4E8GLB>F#PktFWgIj zxO%AQ>5}Rms88X^RK@)7GHb)ze(EN4>&^k|s;o@6tI?ADJnGl%54*A-UB7H&|L!KQ zlXL^YA5jwREtO;8P0o?0k-N+vR^u>CA~9o(e-`gZ5pE;0=@bo3g+ceiP=Yz{%!WLh zEw%i@RtX(2G@el&M6haij!)bNO@=$o@BhItn^;=3T+O8V5A>g6k@&ole-2_D13(M z+A5}f=OO&sauQ>DgHO%>Oh;{}Vw}>SU)|4!4ZJS3=!aHOd@;JXP$Kc0>=0Pv0*i@| zUi$IJR?XCS_?eM2*|hpJrVju?p^8Bui3Riw6W#?WDN=S%q!%Rl@KBU1&I##-?Muvw z=w3}vQOUq=+%cv9owAW{_y)M5c2yJ!B6ewOaX~Qs*|7eWE@g~r%o>t66gJ?H--i@_ zU%%vEPI5JrJufgmr39(zQuqqloH5cXu?)IyfvJudXW~2H)P8@x^&Q(fo1P{uaOTWV zFUsVPMlOk%Y#2t9a?^~V8IiX}IDSP!Fc&9HwuV1IqG_$ZFWpHscwdrRO|2jN>;2>`d%P??rOJ*cWqsb0yU~Sogv3v0x z`}@OW?*AA?S2PkdPdFzVlE0|d$|OAQQvb0n*kVp)NO@;??bV)9kDx7F{tz=te}^jF zjM5!I`UVP~d^a(&)0#oq?HgH6vA14CM!ACe`H4Ch;h;_#K?Ns8!{aX^&!A|rucyWn z>s{SuH81*)&{O`zS#niF;dGMItVhqDd7;+2%3!q3Mke=W+DATJ{AXr9pNQdI^(zddR+^Zg1pfwNgSJw~#WDUn2u8}2 z1&YMSfiUTFvGnAkCnw1s{WccaRApyq6MCE-szLHhhiopMo=OWbg!@hmUV69i1-Yhz z@f9}LOh#qZUA2y+-@opBO+M(bWvXuPbRWs_8kAYg|UVX1PS@ z_J;WgSJ=zpEPMvWQDRf4HCW%F*tDQ$uJWcaH?+-`4u45hn}lAA16E}9wD9i znUZANn?Zc6AnEAX;-BIAxw=fZWerREKa=DlJzb<>V%=wb$TU9HYw1&|sH>JMExxFN zb|#0zcCcW%|3ApeAsIp5zeJIj;HRCREiys%{C5uz4?8J= z!FhOG7CpOQ(O`Y}#2Dk|iTVYvJRL15qg}>^B zZEIXGR3KjnO)gM-2Bt@TefR1#?Od{c&1!NFwndQK2{U%#_6jE}Fu{Tdp6v7H_qJvF z+@tKv+8-VR>_N3=v*=XbNyf9dUN5NbD$#|tdR}tA3k(1vMeEj#?*2UDo5Ek+kV&?^ z1$TdPZj;uV(>gGDHO2Y2>dXI_X-G*^zAlvN7UlpwF8)u%7Ucvy;#LDoI~~hHCG!iv z69~?`$gz}cFBRG~88})hO`*+59XeK;V!LRV<;0peuPWd3@a`?`4)ljxg?qJzE#OCs zDBqAQ=YwfzMHB-U2baBSf2Ubx$*!2A0c<7VL6O6X5GNXo2^?mQ! zB(v_dT1Te}7aO$0!}P|e(2hR+olRj=Uh6MD;_>=xiN<$Uh&ETd@kmvRlB%sPhxhk; z)Nly9Qhxn*<=(;oFuFXmZ^E-xGTrZ+<|9z#*-<7d%kr3P3zlW`U}y3y)!!)=jCxF~ zn$pa30rH1I;!`dYBOoP=ymUzj^3*&D)c{Bf*ql%x1PT?>h7G%i2pxJRQB;0S^3ZBo zqj!8;^jy~dyZi=DlP^Fr`dVUntP1HVPR3qc-A2Jp@gLtQ|-?sy8UZ-r)zc8 zCWSCW#*gBRij#-5W|^=!&|ew`W6oJe=xoUk9?|)t!q3wpOc#P6f^?G*%S=MV5q7`O zN9cJb5Pf%oB&0+RF$0owie~6aDn8R^Mt?aSouJdwSxPls_gIDk2>Xo4ybb3zh7tYx3`@Uw?D|E=&+oQ^!Z0t zqTJ{MoKCm8&jUedk@$RNN8;Ukbf`GZu8Vec9|+s`i)jwKId_HL(#{P8yLlV zqOaMHC?iB@w39vk=9xG`luk`p8MIw$iBX)4QO3t1idS)>weLcnk-0g3+|f@UU1>wl zo#iJ8aTD#*o9>6CqX_ME=n}X|gRGs|eA_+RV;2_8yV140!e>|0JvvS^rPubO%A0@v zy9i&GqrS+pFp$TDGiygQB0d0zDB(=?2&L1#aOW!JTTv985qab_+KlHh`h27DnRKo^ z?BAwq+F#SE8&9F{KOJe(XQqlHYP?i_fYfw|M zv?VR0%R}@ELI{^caU5)X>}2&=s5KK?7so|s>%;XWJ~h56^CmfnTf_(S-Xe;tVmQQy zG}&w*9+au1Of40gFQv)!3dhlBnT(3Jqa#n_89^q+im*!e82zSebkNBi+`cx1so|xtPn(*q{XR&o zzqU8U#BP=mlhYiMX<@m~F-EK_TNLR@!%W}AG1;7f&NVBNOrHp?w|Q}2S=R97LJ!Dk zdaa}0cSYy1^s`lNnHJ|kUy1u-eqIOO3TFgv^R+)}3d-+qy`}_D)01X!u75;jE!C3I zFHisPkup=4yH2sN>0d4;Z)AYn3u0+srY2&opsm;A1bfyx@{sx$k=6EDcaP3ZL%Gw% z?mu>KI)uMRzKS-zmb-88s!Z4P`yx{$hVL!#V&b$eqc@~zlvdN^8arFlg0X< z@|OoTxJUDfps`%l(6d*K2Am%bziSS zT5w?vg4|2dM}rWTgh$ue&fKh+jng$CN;FvQ$}s75U9qD>FkIql^16P*aB|FcvB&J$ zDV4)CX&9H^HRj4YYtMWsK=U(?^^48W>!^7lISDyl<`K>3C^UIiIC1;{M4WaxSRSg* z)p28$VYZf=sl7#xZ4Jcx`FrY2P3JZOWO3yR|GAj|km=?dR-gCtzg{O<>0yu&Q4zG9 zX9psu#fXlZ1mfFKHwOPsoX;jl^U~JOZ1C(oA2`)7-HPiP6LPvoI%xNhY3@nV=5bd? ztUr5CcFJKK^!0l;WB;kS(32ODa;IT#Sa8jOJvsL4>P!4exKHzVV!AZgK;MLnHJ7_1 zI6q%Rw)}^Uzk#}uMq#g>ZY;Z$qph|rwp*Z^jOPHT{!bA*!X4LQkZKIqrh@COdq-f#VKY;)o5lr#xX*N@72A`*J~9*cGZ ziggi05#Nh8&s;A5cCDfFdU)Dl-zBk;NC{YMrC8({IYhK_#)hdm{`z<49z={)FsZdg zgQ%*xwYL_S?CH|3NN5y{0{Vcg=xmtrD8i;B(x7)ijIJ#&EkkFJ(V{8llG3k+QZ&b+e+l=!{B2(7G3H6uGZficEwx z1(oQpy^A!g3oI`3X~wajanM`pA4lFxYASLYJ$h)|qxJS-L?zx;y5*<+C4!&?T(Mxd zJ-qQ_tRI=H#57oGcG}HIM_iln4zI}tLZ)hH5_7dBFkY}H2zyBMH^tby{?2H}e4>6ISL*uZKk? ziEnje_^)LO^TaVD#wyQT)spw=t~Ocxw^0aUf>7a!g;NfwFg=RqugO5Lero5`6ywJY}_ zf}-UyB(Jx(_ebmFkyu8~o%5~j+Vk7b%aKl5iH%)aB1G>xUqLYu29v-X#osRX*%`m# ztWoA)?f2~urmC377`B<^c{%h*K{PH$cDk z+x~Ar_8WAO@r|9@?0HjNPzT#9&J07s0%5^2)B4x4CAtKj^yC(z3fBQ65Z`qWyH4z% ztkyDtU&qrmYQp!?a=Y2j&opn%KlwuB)V8grjr=WJ8ahaunF=gY+T=j$jC2`Dvhpfh zzGVFSzUSljnIxd%#$-$WnSJ5?mb5CRTF|Gf)q_mv`OeeUnYWXYb+_uHUVBaSo4<)@ zw@{~Z>qVOrSnR@;fcB^a*Ttck!r^P5iqkBahq7Sgi~9)M=IE$gz@x}Hx>pCJpF z=dJ;0v0?J>gql$VI2B?xsGQ8GHszGeZ6o|@>NPU$S@Yj9hAPLi{yvD&$0Df3Z%geD z-hfE+bg2V&NZOAD@XGY% zOVcEOBD}9fMznJ~lIh5Sv32svX)|)~USo?<=eXf1kS|ouqagNST+G;ss0z1fgfu}XppLce z)%p=_XChU3d+g2;O{l*~OYe7i_>5EgE$v3=BTInujLch%vG2JcBoTL3Y()0!3_q+x z#o@<4SnT(MYo^3+Xc$RQnGf{{ihz>59<+&=og~MWQ)L3-CZB*L#z9Z$S4E>ffv1kS z)$#BR1zQDU5i~+{eMJD=@`z}$DT*!9-?P(owtO)k{GYFvo>)7_zSP0eP1X$}_B~=1 z&jNbR*4bDz$ZIQ&6#!PdI`>;+EqfNdQB#~!v@>~NgNlpCOLbbUmb(&n$CAiwJfe9K z!e|_@q~Jp=8B;6$0{sGi98Szt7!|wz^u0)Pa?RUwo7w`x3uHbe4L?^VF;M#3bozAq zydXnA!buFuvv`&{7qWL|dx~yi(2V7-r^Gx_L`U&-yt$u$2~Y$~>Qaq?`&OoB@JAHr zChefHB69G^#Ya0{x2Bd^N?~DPjGm@d*o{RY?ez{-AYZN&Iql)#`m;}~&-GwaGW2w& zNq1$r;#unPT4~qr^s7%h$VKwc+oeIyrc7pbJ+-1y1->9J8CN3YI);gRr)T#!&$kgZ z&)HpnLv>34(`d)^LEIP$GJVYfz%>fcI`|*}!_TAqr|G;?w$K&ei~^~%la-qFsR>*@ z{w{0dxqolM!l8Q&gB}k&?B0fVog9ac+91navQdy-Lhh!i4+rJe=`WFSDr2o3#A_bL z7IDY}5`3d`VTz25TkhF6ggW&j(F&fbmoTsX!Ku(dTKe!P#~nuNuwwBHh{%oB4qN4I z8>HoA?(5Dp-eV>T{hD&PKN|op$j+jx!2BsVD;Ahpshk7s`^}MjTYOHs#J-2Ks~Y4U z)&cG)nmbqbn3wsa-|0ugg>6Pktn$FQ-|oI~w+{Dw_rs~^Y#APmmT{N0WDdS3{kfw; zE_P+iSAyw(V?05K=KAs9HWjT1SsIBk;|>302g$L=x0rOX(8p zvI(~gHA7rhw*%~nufQ9jpgFc9xGVcQ)?MIxpdJvpaN>TS>n=rd;YR!tSIRq9zv`G_ z?IX|IGkbxdxB%*RXBLJDtII?hxIhw@-%ww!TTgAal^ieCFy@;=a814*zr<8D1ydnO$M)>|muM_)+Lg4;5GJR4fc8B7}>{!mGb{4HuB3m*R)$m{K zY642MiQ(`&N!;I)1aw)lSBP^w%vE+@3%%g?crx*Go**~fVf zKd1`dn~qdJ&2)FFLYpr+lUD;^<4|L5Dr0erZFCp6BW^Z6N_P+*FluFN9z&?{-_>+y z*&^JL58V1LtTEF0_LS*LToUFeVqNXbPp)Kr#%WJI#iRUc3_O}W1JS~Mpv#~eG;;*m z&ya|qhs6@@NWaq2Jvg*3v?6{x>Ej%Yj9^&mZ`^{C6>RhQKY!NJ{!D9)W+;f*h<>u^ zZ~2gyB|DV=36~uK!!ZNj{|AFHFcZ}{SvyRt*8r`0hUBZ((eHxnCECkIY_hF%Kx}Dd z^LRxgT+4oJhxA5i)%N?C-$J9g^OtD9HuAZyuN=lkt=+R4`My$}=_hB7h4cALk=6h1 z<0UKMEVEG zk4NmU-uT)y!1Qb~^$hY}kJ|%;63}f?Ec>-iIweg$=cT}TkWN$sIO@Qt{TkqCmw9h; z{&WAu1asgXi12n@-xM!?7K=fo*IS?X9H$WuAg8^)M?D&n5dp?%1FyvEvdN{^CVKN~;!?&r7{VJpa z|59%D-3t={tqxOYUu*X@Q-|sN130e*pam8~80=BYmID$ae0!3#_+e8am%_|S0gBK| zw7%J?C$=i>5f!JJz}yFqP+k zP8#sl_y)v^@&qJi7}E**4%VXsx2CT|vQU7`P+t&LOM>{Z`Bxd#HJsO1%U4Wqk^` zyC~_61H@R*Al4`nfkYa`I(X!dt-}X~A`(jDOQL)F&Pi?b-X5^4NxK8)g588F z(hVOmDAM54nFz*Fn!S~2zM>E+ceiwol0NPB#4Glr-zwYOstrk4e6sk`{;j@Or0^(q zc&7UA*l&-m)k`iuRfqHQvT6}4Ns&_)v!GPMUcA-(k5~WG6j@o4{rWk?TsrwGCF90 zU9%LNprZ)?GrA?TSaOsF=dum=YJjw>%0!6L8Sz=>ao(5uf=f2Gw(rA4tCCI=GSzm9 znG9^r-JM8$*PqgFCfdv=Wv?4-g!vZuZ6>j+Rcv0@X#NsUsCrt?14b+PE(_Uz#Jo?h zJA7?QMjHF`z&($o&C7Oi~u&h|RPB;*dOt<0y&5?}D$V#jWOAEU@OeQ4R^ z%gaBg`h2T(a8;=`tUJwowG;M$#!Vl^_}y_n9=H)rip&;bN2YCHm(3sT>7mVJQeE??N~ z*1Nqe7CNo!;v&son=C^yS>G>M40iq-Pc2qI-KO#$L%NB=k`8nzJ{;ohi1D^8CcS`I zBW}Q+7iB!F{20+sCUfdLpS~9nFX55&Y2SeO7lD%ZHT&fK)0Ox@tozxWaG|iiyf|{l ztb}Y0jj|N|uw98^5pbEHnE{4Ns3*D%B(_eTri_5}R=QiTy+m_rXw&I`n)~)2q_Y6T zGVW}?$ibZG54UyHaLoq5)G)}Fl?c#B8@Lx^2rR`yt~ypJhOPgs*9oi|yn)VhfI8+k zPz&V^a6KMY(b&rW_8ptI;2&TUw9o&msu|Q91%Rt2?!ov&GSM}W`|<=^rDmDXDYoqT zuhQZz0Q(|f>@)$nG*F)e%F#@PZ9l=%7yx=<+UOVfOgCWqxn{`k&Reke@{5x(5uk^t9Gg8X{_T}1720yUZkYNg-3+sp2E5KCkfLfC4fVtFXlNQSFAEa`h>GQf0JrHmY zgS+PFw&N(BI$*{n-CVjO(F(+c>}cr&R6+bZc>^F($_u4!~|Mt^FX4JfdKg>LX;b-v;XZv$y+sLqtA^F^NdawjIMGg~vBz=R75P}|xZusN5 zEKta%7M|k>3-zsew7BUO@K^B^4)hJ%f_PSG&4tY9#z4ejq*-O07eJ|czo!0{0( zuJqNRq0|>~3~|^rN=jilu|Gp5;~yA&lSer0DJM5zJiAzg5rNfe)uTuaB%m0iIQa;6=~d2W6e22 zV(~nwbx*loMC>$7?XQ&6C<*A>gIebIr}lJKgjkfM8Nr!OQNxTotOL>6Do@${Ap_N^ zJ$71x7)R)@vhlxV3Ya6blrw&^_=#;vk68a_S9RSixyPBKZvh<*vHHE|FBl5@b(h+i zZYag~LNP1XwxM}r^7z0YGo$y;dqp`K>$?H>q|#%n^b7~Nr(4=0p3ZZBVD6EU5VT?Pa#9dGC=^=iOu(cOngBd2rA|EhayV`9u4 zWL@}@kK0ewPjcqMPX(Lfe~@<*_FRj-B9BNVFq{V4aTBw=uMKB;MqB=qGKpKo)p3t_ z-9;f1$2UuY6dqxlm#e6K_ou75=ctv~c`55vwu}|D?Xo%t_8S?HS_2eRG(wd`qc*K${&&;Ch-66DX?1^`E$(t2-fmG!V2Fo zo_CTj8INWMYR!rs?~GoDcHrbDFp;3Z#n3DU4cr;dWQW zo)#dw^z{V4>Wo|5?#>;x9#V`LYCHoZK#hlB!ITt&IHb1>?+G2pxN)c3DlWXvSp>He z=^;tM!oUOBnTVJ&2~NLsgaeUK#$a}9#>G3ryb~rHQ@_TP4?(5J5S-o*ZB|9`B*C58 z)!#lgVQXl*7eC-!Ah5KZ3v&iCfj)s`DPRU|Lw{wYWDQ_3(9O#f>q@q4Z=cD-kqi(f zkS=}}zI_$`psFgQSd{n)_1jCY6}#u`jpB}XgWH=NCXbZG8iCQR7pg=HH2*K~Z>141 z5tTmzxZx)7h3M-GgfAWtv1OD6)5)FR(fnSZEvJ>rk#L~Z3uq_s{sH(i0(x-!`pz8W zM?n)v1=(S!ubY8josWoBTcDPCv{SI#ecZ+fB)*kx_kBkh0}^}d1ipw)(E5eqgY&ug zBM6>$v<^Tcf8x#UPwMvu+f3#|@>d8>xwi;--`JojQ#>DraU*g4{nM9NjK&X?;)?|E z;QQtN^ekxwshf2uuOVt*01@ZSsi-g@cPO~@!4eqt#+nDPyGm^VzkNTlh@TMV&r3MM z;!nxixR)^hAnc@lfBT+-TZUcR4ZV2D>V*J$5@1uSi|ZZsU+I*MKYUQ;;%MJTaFi`6 z^N>>i23Sby{0AW~XoQAmz5fIwfQ;e20Tprz{2*5$MDXZLv(VErPfvwzaR!IkVvw$S zXj=LPGTZ~`#s`1uUk_K_#Ijbt+nj=$tsruuD9>WHbfoi7O5D(Q;$2x&HxMnEcRhA! zS*lJWw6JydeA5{}N?v$pcuhC=#)x!f;j`Y3L_HkqMaT8IXrT(N7JZ~Z%|WqUt*G zICqD03u&_dmhkt-UeP~i*urT zW#JqZHaipYd<_orX7x%U@$nJVR7Rv>c5NZAsOd*y{d?9dZKwk$a zy@JURs5~}#M+hA=^&cr-$VWnqXs~<4*$@wFoy6$9NnG4)HCC0Y#pScsoD#A8`#mCX zC+0G7s;>T=<0vBudb_*T=L)Siu|=zjea3l*o-$$)AsIn9m`{^Rt}V)LdhwsvSo%-q zDX>Z05cUy*NsZ0f1#r^faeHv%1k0zT%jo_cH<>U!Ja~nM1X|JYpbDJDFx6&q%n}d0 zOsWhHm1OXLb=ziCqxm&c!C&06?$>e}X=O23`}DY4^i$ME#F`HmCucF!0>fF9F_wpI z=UAp;#D(qWRSpk5KQebcJwtKB$5F`h7U7{1Y)f0 z1kX*)J2tuA%I#K~Qd$k6-+VQ`Jt55$?yRB>Q9vB9x&3dW?KVQ9KD*ZDPC-f6hG&Od-Dy06WKTJy#!N# zE&}sD&rmVfw>m_HOKk?UJS!z1#Y|@#jb*@T{{l2|2b@~(f;egPZa^3j4}eRSmxEIy zqG=~;`5BDoV|@WY@ZYo11d{1bh)F9{sjO=JDc=C=0+4z2S!2~Df8-w&g<)@?h_J%6 z5-exO&o=}l5E^{*w9Dgwsahb5=@xV%q1|=38D~eNt>d}Gp?w073mPgs1G8L^{OflT z-^z9>wh)v6VEXN3DXg1t0kQ=hba`q;QNaD#OP%|CTr*%FcxiZ$_m9ZU-LP1Sn2yr` z!`P`RR-&zj-#d3B!PYSqyQeJ_EfjXIp-i2g0i~zRRD@U$O^+c@C`_hB1cG@_3ou%OF&ff84n?u&olxIq+v%fc zeGUI*m`_Va#kXA>Ez&T$*PyO&NS((vMa!b5Akg-XKjHd;n!**jbDgdR?&&}P`CQe4 zgQqh#uyd8{tkp1Ug$VyvEZ)f27$#@giETYhcs{IaD!w$9!a?^jue)WF0_=3wN{4jH zGj`Q=7=9SPHY*Q9=@k|Og4|e**ubt$Y4Lp&u*x4w>Z2UVKngl7l{1n2{~bHt*MP!* Le-U7o5Mcff2&BP< diff --git a/core/tests/fixtures/config_install/testing_config_install.tar.gz b/core/tests/fixtures/config_install/testing_config_install.tar.gz index 1475170eaa6937323acd65a229fd06d66f633f28..b5d16b17e9510f97ac04dd565bc5f2464cad10ac 100644 GIT binary patch delta 300 zcmV+{0n`45Rf<<7ABzYGk4#f#00VSob98BLXJ2D)Zf0p`UukZ0bYX04E_7jX0I>>b zDu0JfQRDWnlE1Nqo1g^&y+0}b)A(PEsL@cT0nf`4;5aA)m8T6LYKBZ349&4f{0;fPV{777`tQW<^L;rZ zExfQ~2tB_)M+-FVE;*;kPYquzg!9%*v3*c;OlS1ORPO0vJRaER)QjYf>AeWxh-G)G z*w%dtByq~0E!%iT8D;cGqW=$HxwB3HUI75*tBoW8 delta 39 vcmeAQZwuv;@8)2*&?uYAz`Rj4Q%iXNBF!u}$<&gxU;o_`w2m=EF){!E1*;86 diff --git a/core/themes/claro/config/optional/block.block.claro_page_title.yml b/core/themes/claro/config/optional/block.block.claro_page_title.yml index 8ee18548b0a..ed6acbc259a 100644 --- a/core/themes/claro/config/optional/block.block.claro_page_title.yml +++ b/core/themes/claro/config/optional/block.block.claro_page_title.yml @@ -14,4 +14,5 @@ settings: label: 'Page title' label_display: '0' provider: core + base_route_title: true visibility: { } diff --git a/core/themes/olivero/config/optional/block.block.olivero_page_title.yml b/core/themes/olivero/config/optional/block.block.olivero_page_title.yml index 9caf4dba2e9..7ca0de24176 100644 --- a/core/themes/olivero/config/optional/block.block.olivero_page_title.yml +++ b/core/themes/olivero/config/optional/block.block.olivero_page_title.yml @@ -14,4 +14,5 @@ settings: label: 'Page title' label_display: '0' provider: core + base_route_title: false visibility: { }