diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 6f05be0..e206764 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -1299,7 +1299,6 @@ function template_preprocess_page(&$variables) {
$site_config = \Drupal::config('system.site');
// Move some variables to the top level for themer convenience and template cleanliness.
- $variables['title'] = $variables['page']['#title'];
foreach (\Drupal::theme()->getActiveTheme()->getRegions() as $region) {
if (!isset($variables['page'][$region])) {
diff --git a/core/lib/Drupal/Core/Block/MainContentBlockPluginInterface.php b/core/lib/Drupal/Core/Block/MainContentBlockPluginInterface.php
index 2516348..38da7b3 100644
--- a/core/lib/Drupal/Core/Block/MainContentBlockPluginInterface.php
+++ b/core/lib/Drupal/Core/Block/MainContentBlockPluginInterface.php
@@ -10,7 +10,7 @@
/**
* The interface for "main page content" blocks.
*
- * A main page content block represents the content returns by the controller.
+ * A main page content block represents the content returned by the controller.
*
* @ingroup block_api
*/
diff --git a/core/lib/Drupal/Core/Block/TitleBlockPluginInterface.php b/core/lib/Drupal/Core/Block/TitleBlockPluginInterface.php
new file mode 100644
index 0000000..f4deff0
--- /dev/null
+++ b/core/lib/Drupal/Core/Block/TitleBlockPluginInterface.php
@@ -0,0 +1,27 @@
+ 'Page top',
'page_bottom' => 'Page bottom',
'breadcrumb' => 'Breadcrumb',
+ 'page_title' => 'Page title',
),
'description' => '',
'features' => $this->defaultFeatures,
diff --git a/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php b/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php
index 5cfa312..c9ae172 100644
--- a/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php
+++ b/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php
@@ -192,11 +192,19 @@ public function renderResponse(array $main_content, Request $request, RouteMatch
* If the selected display variant does not implement PageVariantInterface.
*/
protected function prepare(array $main_content, Request $request, RouteMatchInterface $route_match) {
+ // Determine the title: use the title provided by the main content if any,
+ // otherwise get it from the routing information.
+ $get_title = function (array $main_content) use ($request, $route_match) {
+ $this_title = isset($main_content['#title']) ? $main_content['#title'] : $this->titleResolver->getTitle($request, $route_match->getRouteObject());
+ return isset($this_title['#markup']) ? $this->renderer->renderPlain($this_title) : $this_title;
+ };
+
// If the _controller result already is #type => page,
// we have no work to do: The "main content" already is an entire "page"
// (see html.html.twig).
if (isset($main_content['#type']) && $main_content['#type'] === 'page') {
$page = $main_content;
+ $title = $get_title($page);
}
// Otherwise, render it as the main content of a #type => page, by selecting
// page display variant to do that and building that page display variant.
@@ -228,6 +236,8 @@ protected function prepare(array $main_content, Request $request, RouteMatchInte
];
}
+ $title = $get_title($main_content);
+
// Instantiate the page display, and give it the main content.
$page_display = $this->displayVariantManager->createInstance($variant_id);
if (!$page_display instanceof PageVariantInterface) {
@@ -235,6 +245,7 @@ protected function prepare(array $main_content, Request $request, RouteMatchInte
}
$page_display
->setMainContent($main_content)
+ ->setTitle($title)
->setConfiguration($event->getPluginConfiguration());
// Generate a #type => page render array using the page display variant,
@@ -258,10 +269,6 @@ protected function prepare(array $main_content, Request $request, RouteMatchInte
// Allow hooks to add attachments to $page['#attached'].
$this->invokePageAttachmentHooks($page);
- // Determine the title: use the title provided by the main content if any,
- // otherwise get it from the routing information.
- $title = isset($main_content['#title']) ? $main_content['#title'] : $this->titleResolver->getTitle($request, $route_match->getRouteObject());
-
return [$page, $title];
}
diff --git a/core/lib/Drupal/Core/Render/Plugin/DisplayVariant/SimplePageVariant.php b/core/lib/Drupal/Core/Render/Plugin/DisplayVariant/SimplePageVariant.php
index baef325..0f0fd64 100644
--- a/core/lib/Drupal/Core/Render/Plugin/DisplayVariant/SimplePageVariant.php
+++ b/core/lib/Drupal/Core/Render/Plugin/DisplayVariant/SimplePageVariant.php
@@ -28,6 +28,13 @@ class SimplePageVariant extends VariantBase implements PageVariantInterface {
protected $mainContent;
/**
+ * The page title.
+ *
+ * @var string
+ */
+ protected $title = '';
+
+ /**
* {@inheritdoc}
*/
public function setMainContent(array $main_content) {
@@ -38,8 +45,17 @@ public function setMainContent(array $main_content) {
/**
* {@inheritdoc}
*/
+ public function setTitle($title) {
+ $this->title = $title;
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
public function build() {
$build = [
+ 'title' => ['#markup' => '
' . $this->title . '
'],
'content' => [
'main_content' => $this->mainContent,
'messages' => [
diff --git a/core/modules/block/src/Plugin/DisplayVariant/BlockPageVariant.php b/core/modules/block/src/Plugin/DisplayVariant/BlockPageVariant.php
index 650cf78..5785f8d 100644
--- a/core/modules/block/src/Plugin/DisplayVariant/BlockPageVariant.php
+++ b/core/modules/block/src/Plugin/DisplayVariant/BlockPageVariant.php
@@ -9,6 +9,7 @@
use Drupal\block\BlockRepositoryInterface;
use Drupal\Core\Block\MainContentBlockPluginInterface;
+use Drupal\Core\Block\TitleBlockPluginInterface;
use Drupal\Core\Block\MessagesBlockPluginInterface;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Display\PageVariantInterface;
@@ -64,6 +65,13 @@ class BlockPageVariant extends VariantBase implements PageVariantInterface, Cont
protected $mainContent = [];
/**
+ * The page title.
+ *
+ * @var string
+ */
+ protected $title = '';
+
+ /**
* Constructs a new BlockPageVariant.
*
* @param array $configuration
@@ -111,9 +119,19 @@ public function setMainContent(array $main_content) {
/**
* {@inheritdoc}
*/
+ public function setTitle($title) {
+ $this->title = $title;
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
public function build() {
- // Track whether blocks showing the main content and messages are displayed.
+ // Track whether blocks showing the main content, title and messages are
+ // displayed.
$main_content_block_displayed = FALSE;
+ $title_block_displayed = FALSE;
$messages_block_displayed = FALSE;
$build = [
@@ -131,6 +149,10 @@ public function build() {
$block_plugin->setMainContent($this->mainContent);
$main_content_block_displayed = TRUE;
}
+ elseif ($block_plugin instanceof TitleBlockPluginInterface) {
+ $block_plugin->setTitle($this->title);
+ $title_block_displayed = TRUE;
+ }
elseif ($block_plugin instanceof MessagesBlockPluginInterface) {
$messages_block_displayed = TRUE;
}
@@ -165,6 +187,13 @@ public function build() {
];
}
+ // Analogously for the page title.
+ if (!$title_block_displayed) {
+ $build['title'] = [
+ '#markup' => $this->title,
+ ];
+ }
+
// The access results' cacheability is currently added to the top level of the
// render array. This is done to prevent issues with empty regions being
// displayed.
diff --git a/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php b/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php
index f19ac70..b4178d6 100644
--- a/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php
+++ b/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php
@@ -105,6 +105,7 @@ function testPageCacheTags() {
'config:block.block.bartik_messages',
'config:block.block.bartik_local_actions',
'config:block.block.bartik_local_tasks',
+ 'config:block.block.bartik_page_title',
'node_view',
'node:' . $node_1->id(),
'user:' . $author_1->id(),
@@ -141,6 +142,7 @@ function testPageCacheTags() {
'config:block.block.bartik_messages',
'config:block.block.bartik_local_actions',
'config:block.block.bartik_local_tasks',
+ 'config:block.block.bartik_page_title',
'node_view',
'node:' . $node_2->id(),
'user:' . $author_2->id(),
diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module
index be83839..49caca8 100644
--- a/core/modules/shortcut/shortcut.module
+++ b/core/modules/shortcut/shortcut.module
@@ -311,7 +311,7 @@ function shortcut_preprocess_page(&$variables) {
$query = array(
'link' => $link,
- 'name' => $variables['title'],
+ 'name' => \Drupal::service('title_resolver')->getTitle(\Drupal::request(), $route_match->getRouteObject()),
);
$shortcut_set = shortcut_current_displayed_set();
diff --git a/core/modules/system/src/Plugin/Block/SystemPageTitleBlock.php b/core/modules/system/src/Plugin/Block/SystemPageTitleBlock.php
new file mode 100644
index 0000000..e0bd82c
--- /dev/null
+++ b/core/modules/system/src/Plugin/Block/SystemPageTitleBlock.php
@@ -0,0 +1,77 @@
+title = $title;
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function defaultConfiguration() {
+ return ['label_display' => FALSE];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function build() {
+ return [
+ 'title' => $this->title,
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+ $form = parent::buildConfigurationForm($form, $form_state);
+
+ // The 'Page title' block is never cacheable, because it may be dynamic.
+ $form['cache']['#disabled'] = TRUE;
+ $form['cache']['#description'] = t('This block is never cacheable, it is not configurable.');
+ $form['cache']['max_age']['#value'] = 0;
+
+ return $form;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isCacheable() {
+ // The 'Page title' block is never cacheable, because it may be dynamic.
+ return FALSE;
+ }
+
+}
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index c12a2d4..50ebd3c 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -163,6 +163,10 @@ function system_theme() {
'render element' => 'elements',
'base hook' => 'block',
),
+ 'block__system_page_title_block' => array(
+ 'base hook' => 'block',
+ 'template' => 'block--system-page-title-block',
+ ),
'block__system_messages_block' => array(
'base hook' => 'block',
),
@@ -809,6 +813,15 @@ function system_preprocess_block(&$variables) {
case 'system_powered_by_block':
$variables['attributes']['role'] = 'complementary';
break;
+
+ case 'system_page_title_block':
+ $variables['title'] = '';
+ if(property_exists($variables['content']['title'], 'markup') && $variables['content']['title']['#markup']) {
+ $variables['title'] = $variables['content']['title']['#markup'];
+ } else if ($variables['content']['title']) {
+ $variables['title'] = $variables['content']['title'];
+ }
+ break;
}
}
diff --git a/core/modules/system/templates/block--system-page-title-block.html.twig b/core/modules/system/templates/block--system-page-title-block.html.twig
new file mode 100644
index 0000000..aca427c
--- /dev/null
+++ b/core/modules/system/templates/block--system-page-title-block.html.twig
@@ -0,0 +1,23 @@
+{% extends "@block/block.html.twig" %}
+{#
+/**
+ * @file
+ * Default theme implementation for a page title.
+ *
+ * Available variables:
+ * - title_prefix: Additional output populated by modules, intended to be
+ * displayed in front of the main title tag that appears in the template.
+ * - title: The page title, for use in the actual content.
+ * - title_suffix: Additional output populated by modules, intended to be
+ * displayed after the main title tag that appears in the template.
+ *
+ * @ingroup themeable
+ */
+#}
+{% block content %}
+ {{ title_prefix }}
+ {% if title %}
+ {{ title }}
+ {% endif %}
+ {{ title_suffix }}
+{% endblock %}
diff --git a/core/modules/system/templates/page.html.twig b/core/modules/system/templates/page.html.twig
index b80310e..a8d7483 100644
--- a/core/modules/system/templates/page.html.twig
+++ b/core/modules/system/templates/page.html.twig
@@ -28,7 +28,6 @@
* Page content (in order of occurrence in the default page.html.twig):
* - title_prefix: Additional output populated by modules, intended to be
* displayed in front of the main title tag that appears in the template.
- * - title: The page title, for use in the actual content.
* - title_suffix: Additional output populated by modules, intended to be
* displayed after the main title tag that appears in the template.
* - messages: Status and error messages. Should be displayed prominently.
@@ -48,6 +47,7 @@
* - page.sidebar_second: Items for the second sidebar.
* - page.footer: Items for the footer region.
* - page.breadcrumb: Items for the breadcrumb region.
+ * - page.title: Items for the page title region.
*
* @see template_preprocess_page()
* @see html.html.twig
@@ -68,7 +68,7 @@
{# Use h1 when the content title is empty #}
- {% if title %}
+ {% if page.title %}
{{ site_name }}
@@ -102,9 +102,7 @@
{{ title_prefix }}
- {% if title %}
-
{{ title }}
- {% endif %}
+ {{ page.title }}
{{ title_suffix }}
{{ page.content }}
{# /.layout-content #}
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
new file mode 100644
index 0000000..8263474
--- /dev/null
+++ b/core/profiles/minimal/config/install/block.block.stark_page_title.yml
@@ -0,0 +1,17 @@
+id: stark_page_title
+theme: stark
+weight: 10
+status: true
+langcode: en
+region: title
+plugin: system_page_title_block
+settings:
+ id: system_page_title_block
+ label: 'Page title'
+ provider: system
+ label_display: '0'
+dependencies:
+ module:
+ - system
+ theme:
+ - stark
diff --git a/core/profiles/standard/config/install/block.block.bartik_page_title.yml b/core/profiles/standard/config/install/block.block.bartik_page_title.yml
new file mode 100644
index 0000000..ff73d59
--- /dev/null
+++ b/core/profiles/standard/config/install/block.block.bartik_page_title.yml
@@ -0,0 +1,18 @@
+id: bartik_page_title
+theme: bartik
+weight: 10
+status: true
+langcode: en
+region: title
+plugin: system_page_title_block
+settings:
+ id: system_page_title_block
+ label: Page title
+ provider: system
+ label_display: '0'
+dependencies:
+ module:
+ - system
+ theme:
+ - bartik
+visibility: { }
diff --git a/core/profiles/standard/config/install/block.block.seven_page_title.yml b/core/profiles/standard/config/install/block.block.seven_page_title.yml
new file mode 100644
index 0000000..ae1cb30
--- /dev/null
+++ b/core/profiles/standard/config/install/block.block.seven_page_title.yml
@@ -0,0 +1,18 @@
+id: page_title
+theme: seven
+weight: 0
+status: true
+langcode: en
+region: title
+plugin: system_page_title_block
+settings:
+ id: system_page_title_block
+ label: Page Title
+ provider: system
+ label_display: '0'
+dependencies:
+ module:
+ - system
+ theme:
+ - seven
+visibility: { }
diff --git a/core/themes/bartik/bartik.info.yml b/core/themes/bartik/bartik.info.yml
index 8b52f48..4bebcd5 100644
--- a/core/themes/bartik/bartik.info.yml
+++ b/core/themes/bartik/bartik.info.yml
@@ -22,6 +22,7 @@ regions:
highlighted: Highlighted
featured_top: 'Featured top'
breadcrumb: Breadcrumb
+ title: Title
content: Content
sidebar_first: 'Sidebar first'
sidebar_second: 'Sidebar second'
diff --git a/core/themes/bartik/bartik.theme b/core/themes/bartik/bartik.theme
index 1b82230..ba98b80 100644
--- a/core/themes/bartik/bartik.theme
+++ b/core/themes/bartik/bartik.theme
@@ -45,7 +45,7 @@ function bartik_preprocess_page(&$variables) {
// Since the title and the shortcut link are both block level elements,
// positioning them next to each other is much simpler with a wrapper div.
- if (!empty($variables['title_suffix']['add_or_remove_shortcut']) && $variables['title']) {
+ if (!empty($variables['title_suffix']['add_or_remove_shortcut']) && $variables['page.title']) {
// Add a wrapper div using the title_prefix and title_suffix render
// elements.
$variables['title_prefix']['shortcut_wrapper'] = array(
diff --git a/core/themes/bartik/templates/block--system-page-title-block.html.twig b/core/themes/bartik/templates/block--system-page-title-block.html.twig
new file mode 100644
index 0000000..9a36994
--- /dev/null
+++ b/core/themes/bartik/templates/block--system-page-title-block.html.twig
@@ -0,0 +1,25 @@
+{% extends "@block/block.html.twig" %}
+{#
+/**
+ * @file
+ * Bartik's theme implementation for a page title.
+ *
+ * Available variables:
+ * - title_prefix: Additional output populated by modules, intended to be
+ * displayed in front of the main title tag that appears in the template.
+ * - title: The page title, for use in the actual content.
+ * - title_suffix: Additional output populated by modules, intended to be
+ * displayed after the main title tag that appears in the template.
+ *
+ * @ingroup themeable
+ */
+#}
+{% block content %}
+ {{ title_prefix }}
+ {% if title %}
+
+ {{ title }}
+
+ {% endif %}
+ {{ title_suffix }}
+{% endblock %}
diff --git a/core/themes/bartik/templates/page.html.twig b/core/themes/bartik/templates/page.html.twig
index 4c27d8f..09a90f3 100644
--- a/core/themes/bartik/templates/page.html.twig
+++ b/core/themes/bartik/templates/page.html.twig
@@ -33,11 +33,6 @@
* added to make the site slogan visually hidden, but still accessible.
*
* Page content (in order of occurrence in the default page.html.twig):
- * - title_prefix: Additional output populated by modules, intended to be
- * displayed in front of the main title tag that appears in the template.
- * - title: The page title, for use in the actual content.
- * - title_suffix: Additional output populated by modules, intended to be
- * displayed after the main title tag that appears in the template.
* - node: Fully loaded node, if there is an automatically-loaded node
* associated with the page and the node ID is the second argument in the
* page's path (e.g. node/12345 and node/12345/revisions, but not
@@ -61,6 +56,7 @@
* - page.footer_fourth: Items for the fourth footer column.
* - page.footer_fifth: Items for the fifth footer column.
* - page.breadcrumb: Items for the breadcrumb region.
+ * - page.title: Items for the page title region.
*
* @see template_preprocess_page()
* @see bartik_preprocess_page()
@@ -124,13 +120,7 @@
- {{ title_prefix }}
- {% if title %}
-
- {{ title }}
-
- {% endif %}
- {{ title_suffix }}
+ {{ page.title }}
{{ page.content }}
diff --git a/core/themes/classy/templates/layout/page.html.twig b/core/themes/classy/templates/layout/page.html.twig
index 8c81950..29dc472 100644
--- a/core/themes/classy/templates/layout/page.html.twig
+++ b/core/themes/classy/templates/layout/page.html.twig
@@ -26,11 +26,6 @@
* slogan has been disabled in theme settings.
*
* Page content (in order of occurrence in the default page.html.twig):
- * - title_prefix: Additional output populated by modules, intended to be
- * displayed in front of the main title tag that appears in the template.
- * - title: The page title, for use in the actual content.
- * - title_suffix: Additional output populated by modules, intended to be
- * displayed after the main title tag that appears in the template.
* - node: Fully loaded node, if there is an automatically-loaded node
* associated with the page and the node ID is the second argument in the
* page's path (e.g. node/12345 and node/12345/revisions, but not
@@ -48,6 +43,7 @@
* - page.sidebar_second: Items for the second sidebar.
* - page.footer: Items for the footer region.
* - page.breadcrumb: Items for the breadcrumb region.
+ * - page.title: Items for the page title region.
*
* @see template_preprocess_page()
* @see html.html.twig
@@ -99,12 +95,7 @@
{{ page.highlighted }}
-
- {{ title_prefix }}
- {% if title %}
-
{{ title }}
- {% endif %}
- {{ title_suffix }}
+ {{ page.title }}
{{ page.content }}
{# /.layout-content #}
diff --git a/core/themes/seven/seven.info.yml b/core/themes/seven/seven.info.yml
index 1427896..bcb9d7c 100644
--- a/core/themes/seven/seven.info.yml
+++ b/core/themes/seven/seven.info.yml
@@ -23,5 +23,6 @@ regions:
page_top: 'Page top'
page_bottom: 'Page bottom'
sidebar_first: 'First sidebar'
+ title: Title
regions_hidden:
- sidebar_first
diff --git a/core/themes/seven/templates/block--system-page-title-block.html.twig b/core/themes/seven/templates/block--system-page-title-block.html.twig
new file mode 100644
index 0000000..8314b3b
--- /dev/null
+++ b/core/themes/seven/templates/block--system-page-title-block.html.twig
@@ -0,0 +1,25 @@
+{% extends "@block/block.html.twig" %}
+{#
+/**
+ * @file
+ * Bartik's theme implementation for a page title.
+ *
+ * Available variables:
+ * - title_prefix: Additional output populated by modules, intended to be
+ * displayed in front of the main title tag that appears in the template.
+ * - title: The page title, for use in the actual content.
+ * - title_suffix: Additional output populated by modules, intended to be
+ * displayed after the main title tag that appears in the template.
+ *
+ * @ingroup themeable
+ */
+#}
+{% block content %}
+ {{ title_prefix }}
+ {% if title %}
+
+ {{ title }}
+
+ {% endif %}
+ {{ title_suffix }}
+{% endblock %}
diff --git a/core/themes/seven/templates/page.html.twig b/core/themes/seven/templates/page.html.twig
index ac26a51..92caec2 100644
--- a/core/themes/seven/templates/page.html.twig
+++ b/core/themes/seven/templates/page.html.twig
@@ -27,11 +27,6 @@
* slogan has been disabled in theme settings.
*
* Page content (in order of occurrence in the default page.html.twig):
- * - title_prefix: Additional output populated by modules, intended to be
- * displayed in front of the main title tag that appears in the template.
- * - title: The page title, for use in the actual content.
- * - title_suffix: Additional output populated by modules, intended to be
- * displayed after the main title tag that appears in the template.
* - node: Fully loaded node, if there is an automatically-loaded node
* associated with the page and the node ID is the second argument in the
* page's path (e.g. node/12345 and node/12345/revisions, but not
@@ -44,6 +39,7 @@
* - page.highlighted: Items for the highlighted region.
* - page.help: Dynamic help text, mostly for admin pages.
* - page.content: The main content of the current page.
+ * - page.title: Items for the page title region.
*
* @see template_preprocess_page()
* @see seven_preprocess_page()
@@ -52,11 +48,9 @@
#}