diff --git a/core/lib/Drupal/Core/Entity/Controller/EntityViewController.php b/core/lib/Drupal/Core/Entity/Controller/EntityViewController.php index c5ee7caf98d3b01b4e3cdab8c2b82f8a5de6f973..37d919e3bd7ee2d19a09e4b53e7e5ade64f1314b 100644 --- a/core/lib/Drupal/Core/Entity/Controller/EntityViewController.php +++ b/core/lib/Drupal/Core/Entity/Controller/EntityViewController.php @@ -55,6 +55,19 @@ public static function create(ContainerInterface $container) { /** * Pre-render callback to build the page title. * + * There are two possibilities, depending on the value of the additional + * entity type property 'enable_page_title_template'. + * - FALSE (default): use the output of the related field formatter if it + * exists. This approach only works correctly for the node entity type and + * with the 'string' formatter. In other cases it likely produces illegal + * markup and possibly incorrect display. This option has been retained for + * backward-compatibility to support sites that expect attributes set on + * the field to propagate to the page title. + * - TRUE: use the output from the entity_page_title template. This approach + * works correctly in all cases, without relying on a particular field + * formatter or special templates and is the preferred option for the + * future. + * * @param array $page * A page render array. * @@ -64,12 +77,31 @@ public static function create(ContainerInterface $container) { public function buildTitle(array $page) { $entity_type = $page['#entity_type']; $entity = $page['#' . $entity_type]; - // If the entity's label is rendered using a field formatter, set the - // rendered title field formatter as the page title instead of the default - // plain text title. This allows attributes set on the field to propagate - // correctly (e.g. in-place editing). + + // If the entity has a label field, build the page title based on it. if ($entity instanceof FieldableEntityInterface) { $label_field = $entity->getEntityType()->getKey('label'); + $template_enabled = $entity->getEntityType()->get('enable_page_title_template'); + if ($label_field && $template_enabled) { + // Set page title to the output from the entity_page_title template. + $page_title = [ + '#theme' => 'entity_page_title', + '#title' => $entity->label(), + '#entity' => $entity, + '#view_mode' => $page['#view_mode'], + ]; + $page['#title'] = $this->renderer->render($page_title); + + // Prevent output of the label field in the main content. + $page[$label_field]['#access'] = FALSE; + return $page; + } + + // Set page title to the rendered title field formatter instead of + // the default plain text title. + // + // @todo https://www.drupal.org/project/drupal/issues/3015623 + // Eventually delete this code and always use the first approach. if (isset($page[$label_field])) { // Allow templates and theme functions to generate different markup // for the page title, which must be inline markup as it will be placed diff --git a/core/modules/node/tests/modules/node_display_configurable_test/node_display_configurable_test.module b/core/modules/node/tests/modules/node_display_configurable_test/node_display_configurable_test.module index a06318dbeb858cc67dabb7504ccfb961fdf454bd..df645c8b8c3af329de9756ba8bdd83e2f287ae99 100644 --- a/core/modules/node/tests/modules/node_display_configurable_test/node_display_configurable_test.module +++ b/core/modules/node/tests/modules/node_display_configurable_test/node_display_configurable_test.module @@ -25,4 +25,5 @@ function node_display_configurable_test_entity_base_field_info_alter(&$base_fiel function node_display_configurable_test_entity_type_build(array &$entity_types) { // Allow skipping of extra preprocessing for configurable display. $entity_types['node']->set('enable_base_field_custom_preprocess_skipping', TRUE); + $entity_types['node']->set('enable_page_title_template', TRUE); } diff --git a/core/modules/node/tests/src/Functional/NodeDisplayConfigurableTest.php b/core/modules/node/tests/src/Functional/NodeDisplayConfigurableTest.php index c85042d625c3807e98c780c811873156ce750587..f44bc25168c9b6b93dbefe737b5ed7098bfde5ed 100644 --- a/core/modules/node/tests/src/Functional/NodeDisplayConfigurableTest.php +++ b/core/modules/node/tests/src/Functional/NodeDisplayConfigurableTest.php @@ -69,7 +69,7 @@ public function testDisplayConfigurable(string $theme, string $metadata_region, // Check the node with Drupal default non-configurable display. $this->drupalGet($node->toUrl()); - $this->assertNodeHtml($node, $user, TRUE, $metadata_region, $field_classes); + $this->assertNodeHtml($node, $user, TRUE, $metadata_region, $field_classes, $field_classes); // Enable module to make base fields' displays configurable. \Drupal::service('module_installer')->install(['node_display_configurable_test']); @@ -82,12 +82,13 @@ public function testDisplayConfigurable(string $theme, string $metadata_region, 'label' => 'above', 'settings' => ['link' => FALSE], ]) + ->removeComponent('title') ->save(); // Recheck the node with configurable display. $this->drupalGet($node->toUrl()); - $this->assertNodeHtml($node, $user, FALSE, $metadata_region, $field_classes); + $this->assertNodeHtml($node, $user, FALSE, $metadata_region, $field_classes, FALSE); $assert->elementExists('css', 'div[rel="schema:author"]'); @@ -113,15 +114,17 @@ public function testDisplayConfigurable(string $theme, string $metadata_region, * @param string $metadata_region * The region of the node html content where meta data is expected. * @param bool $field_classes - * If TRUE, check for field--name-XXX classes. + * If TRUE, check for field--name-XXX classes on created/uid fields. + * @param bool $title_classes + * If TRUE, check for field--name-XXX classes on title field. * * @internal */ - protected function assertNodeHtml(NodeInterface $node, UserInterface $user, bool $is_inline, string $metadata_region, bool $field_classes): void { + protected function assertNodeHtml(NodeInterface $node, UserInterface $user, bool $is_inline, string $metadata_region, bool $field_classes, bool $title_classes): void { $assert = $this->assertSession(); $html_element = $is_inline ? 'span' : 'div'; - $title_selector = 'h1 span' . ($field_classes ? '.field--name-title' : ''); + $title_selector = 'h1 span' . ($title_classes ? '.field--name-title' : ''); $assert->elementTextContains('css', $title_selector, $node->getTitle()); // With field classes, the selector can be very specific. diff --git a/core/modules/quickedit/quickedit.module b/core/modules/quickedit/quickedit.module index 1a86f979c3e26d2ff2362e18987a01fafcc0c15a..028fe20143516ecaf3741e6e330fa5fe03d42f1d 100644 --- a/core/modules/quickedit/quickedit.module +++ b/core/modules/quickedit/quickedit.module @@ -128,6 +128,23 @@ function quickedit_preprocess_page_title(&$variables) { } } +/** + * Implements hook_preprocess_entity_page_title(). + */ +function quickedit_preprocess_entity_page_title(&$variables) { + $variables['#cache']['contexts'][] = 'user.permissions'; + $entity = $variables['entity']; + if (!\Drupal::currentUser()->hasPermission('access in-place editing')) { + return; + } + if (($entity instanceof RevisionableInterface) && !$entity->isLatestRevision()) { + return; + } + + $label_field = $entity->getEntityType()->getKey('label'); + $variables['attributes']['data-quickedit-field-id'] = $entity->getEntityTypeId() . '/' . $entity->id() . '/' . $label_field . '/' . $entity->language()->getId() . '/' . $variables['view_mode']; +} + /** * Implements hook_preprocess_HOOK() for field templates. */ diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 8e67827c4dc4c83bbd8a44af8edbca9341f89ad2..f40a4bad4bd4f7a638f1ea5301554fc4201d7722 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -253,6 +253,14 @@ function system_theme() { 'file' => 'system.theme.inc', 'variables' => ['error_message' => []], ], + 'entity_page_title' => [ + 'variables' => [ + 'attributes' => [], + 'title' => NULL, + 'entity' => NULL, + 'view_mode' => NULL, + ], + ], ]); } diff --git a/core/modules/system/templates/entity-page-title.html.twig b/core/modules/system/templates/entity-page-title.html.twig new file mode 100644 index 0000000000000000000000000000000000000000..59a8902a22d94494cbfff48fe62b277ff843255d --- /dev/null +++ b/core/modules/system/templates/entity-page-title.html.twig @@ -0,0 +1,26 @@ +{# +/** + * @file + * Default theme implementation for entity page title. + * + * This output from this template is nested within the page-title template as + * the title variable. This allows a further refinement of the page title + * specific to an entity. + * + * This template is only used if the additional entity type property + * 'enable_page_title_template' is set to TRUE. + * + * Available variables: + * - attributes: HTML attributes for the containing span element. + * - title: Entity label. + * - entity: Entity having a label field. + * - view_mode: View mode; for example, "teaser" or "full". + * + * @see \Drupal\Core\Entity\Controller\EntityViewController::buildTitle() + * + * @ingroup themeable + */ +#} +<span{{ attributes }}> + {{ title }} +</span> diff --git a/core/themes/stable/templates/field/entity-page-title.html.twig b/core/themes/stable/templates/field/entity-page-title.html.twig new file mode 100644 index 0000000000000000000000000000000000000000..216343da58cac3d2ec2aee066a44d2a7d2fd7e90 --- /dev/null +++ b/core/themes/stable/templates/field/entity-page-title.html.twig @@ -0,0 +1,22 @@ +{# +/** + * @file + * Theme override for entity page title. + * + * This output from this template is nested within the page-title template as + * the title variable. This allows a further refinement of the page title + * specific to an entity. + * + * This template is only used if the additional entity type property + * 'enable_page_title_template' is set to TRUE. + * + * Available variables: + * - attributes: HTML attributes for the containing span element. + * - title: Entity label. + * - entity: Entity having a label field. + * - view_mode: View mode; for example, "teaser" or "full". + */ +#} +<span{{ attributes }}> + {{ title }} +</span> diff --git a/core/themes/stable9/templates/field/entity-page-title.html.twig b/core/themes/stable9/templates/field/entity-page-title.html.twig new file mode 100644 index 0000000000000000000000000000000000000000..216343da58cac3d2ec2aee066a44d2a7d2fd7e90 --- /dev/null +++ b/core/themes/stable9/templates/field/entity-page-title.html.twig @@ -0,0 +1,22 @@ +{# +/** + * @file + * Theme override for entity page title. + * + * This output from this template is nested within the page-title template as + * the title variable. This allows a further refinement of the page title + * specific to an entity. + * + * This template is only used if the additional entity type property + * 'enable_page_title_template' is set to TRUE. + * + * Available variables: + * - attributes: HTML attributes for the containing span element. + * - title: Entity label. + * - entity: Entity having a label field. + * - view_mode: View mode; for example, "teaser" or "full". + */ +#} +<span{{ attributes }}> + {{ title }} +</span>