diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 9a1794a2ad8936b4e254b5819407461e5a0fd5c2..5d14f31d1f359645a4ebecbe3febc2b2a699d503 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -500,25 +500,6 @@ function template_preprocess_datetime_wrapper(&$variables) { $variables['content'] = $element['#children']; } -/** - * Prepares variables for status message templates. - * - * Default template: status-messages.html.twig. - * - * @param array $variables - * An associative array containing: - * - display: (optional) May have a value of 'status' or 'error' when only - * displaying messages of that specific type. - */ -function template_preprocess_status_messages(&$variables) { - $variables['message_list'] = drupal_get_messages($variables['display']); - $variables['status_headings'] = array( - 'status' => t('Status message'), - 'error' => t('Error message'), - 'warning' => t('Warning message'), - ); -} - /** * Prepares variables for links templates. * @@ -1376,7 +1357,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['show_messages'] = $variables['page']['#show_messages']; $variables['title'] = $variables['page']['#title']; foreach (system_region_list(\Drupal::theme()->getActiveTheme()->getName()) as $region_key => $region_name) { @@ -1414,13 +1394,6 @@ function template_preprocess_page(&$variables) { if ($node = \Drupal::routeMatch()->getParameter('node')) { $variables['node'] = $node; } - - // Prepare render array for messages. drupal_get_messages() is called later, - // when this variable is rendered in a theme function or template file. - $variables['messages'] = array( - '#theme' => 'status_messages', - '#access' => $variables['show_messages'], - ); } /** @@ -1763,7 +1736,7 @@ function drupal_common_theme() { 'render element' => 'element', ), 'status_messages' => array( - 'variables' => array('display' => NULL), + 'variables' => ['status_headings' => [], 'message_list' => NULL], ), 'links' => array( 'variables' => array('links' => array(), 'attributes' => array('class' => array('links')), 'heading' => array(), 'set_active_class' => FALSE), diff --git a/core/lib/Drupal/Core/Ajax/CommandWithAttachedAssetsTrait.php b/core/lib/Drupal/Core/Ajax/CommandWithAttachedAssetsTrait.php index e3f31b961b25247456b183bf05b26df0f0e2ca39..fe117e173083807db1af21f27b564b5ce7d3e179 100644 --- a/core/lib/Drupal/Core/Ajax/CommandWithAttachedAssetsTrait.php +++ b/core/lib/Drupal/Core/Ajax/CommandWithAttachedAssetsTrait.php @@ -35,7 +35,7 @@ trait CommandWithAttachedAssetsTrait { protected function getRenderedContent() { $this->attachedAssets = new AttachedAssets(); if (is_array($this->content)) { - $html = \Drupal::service('renderer')->render($this->content); + $html = \Drupal::service('renderer')->renderRoot($this->content); $this->attachedAssets = AttachedAssets::createFromRenderArray($this->content); return $html; } diff --git a/core/lib/Drupal/Core/Block/MessagesBlockPluginInterface.php b/core/lib/Drupal/Core/Block/MessagesBlockPluginInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..8435960f92b89736a2b5ced2ae70402331043dfa --- /dev/null +++ b/core/lib/Drupal/Core/Block/MessagesBlockPluginInterface.php @@ -0,0 +1,20 @@ +<?php + +/** + * @file + * Contains \Drupal\Core\Block\MessagesBlockPluginInterface. + */ + +namespace Drupal\Core\Block; + +/** + * The interface for "messages" (#type => status_messages) blocks. + * + * @see drupal_set_message() + * @see drupal_get_message() + * @see \Drupal\Core\Render\Element\StatusMessages + * @see \Drupal\block\Plugin\DisplayVariant\BlockPageVariant + * + * @ingroup block_api + */ +interface MessagesBlockPluginInterface extends BlockPluginInterface { } diff --git a/core/lib/Drupal/Core/Display/Annotation/PageDisplayVariant.php b/core/lib/Drupal/Core/Display/Annotation/PageDisplayVariant.php index bc07297c57ef67321613798f3a7bd4b06a319710..1d5905d4208cd0556eb611a35b003600ff7c768d 100644 --- a/core/lib/Drupal/Core/Display/Annotation/PageDisplayVariant.php +++ b/core/lib/Drupal/Core/Display/Annotation/PageDisplayVariant.php @@ -11,7 +11,10 @@ * Defines a page display variant annotation object. * * Page display variants are a specific type of display variant, intended to - * render the main content of a page. + * render entire pages. They must render the crucial parts of a page, which are: + * - the title + * - the main content + * - any messages (#type => status_messages) * * @see \Drupal\Core\Display\VariantInterface * @see \Drupal\Core\Display\PageVariantInterface diff --git a/core/lib/Drupal/Core/Extension/ThemeHandler.php b/core/lib/Drupal/Core/Extension/ThemeHandler.php index 9529c8a04d1528ec112d957b1b48574bc338796d..0513eb512a2c90f1c6dc658812b2bb715fb7e696 100644 --- a/core/lib/Drupal/Core/Extension/ThemeHandler.php +++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php @@ -465,6 +465,7 @@ public function rebuildThemeData() { 'secondary_menu' => 'Secondary menu', 'footer' => 'Footer', 'highlighted' => 'Highlighted', + 'messages' => 'Messages', 'help' => 'Help', 'page_top' => 'Page top', 'page_bottom' => 'Page bottom', diff --git a/core/lib/Drupal/Core/Render/BareHtmlPageRenderer.php b/core/lib/Drupal/Core/Render/BareHtmlPageRenderer.php index f528409750c87efcf26800b61ccaee10b0dbb0d8..bf05a3df28343504e2930ad59bee76f7609be8a1 100644 --- a/core/lib/Drupal/Core/Render/BareHtmlPageRenderer.php +++ b/core/lib/Drupal/Core/Render/BareHtmlPageRenderer.php @@ -49,6 +49,12 @@ public function renderBarePage(array $content, $title, $page_theme_property, arr ] + $page_additions, ]; + // For backwards compatibility. + // @todo In Drupal 9, add a $show_messages function parameter. + if (!isset($page_additions['#show_messages']) || $page_additions['#show_messages'] === TRUE) { + $html['page']['messages'] = ['#type' => 'status_messages']; + } + // We must first render the contents of the html.html.twig template, see // \Drupal\Core\Render\MainContent\HtmlRenderer::renderResponse() for more // information about this; the exact same pattern is used there and diff --git a/core/lib/Drupal/Core/Render/Element/Page.php b/core/lib/Drupal/Core/Render/Element/Page.php index b65446a4729413fc462a5710e40f12cb097c67f1..b73c808d301d29ea1deeba0a314937c9345e4207 100644 --- a/core/lib/Drupal/Core/Render/Element/Page.php +++ b/core/lib/Drupal/Core/Render/Element/Page.php @@ -21,7 +21,6 @@ class Page extends RenderElement { */ public function getInfo() { return array( - '#show_messages' => TRUE, '#theme' => 'page', '#title' => '', ); diff --git a/core/lib/Drupal/Core/Render/Element/StatusMessages.php b/core/lib/Drupal/Core/Render/Element/StatusMessages.php new file mode 100644 index 0000000000000000000000000000000000000000..febf01816d8caef1ace575ffa85ce84500ad9fcc --- /dev/null +++ b/core/lib/Drupal/Core/Render/Element/StatusMessages.php @@ -0,0 +1,136 @@ +<?php + +/** + * @file + * Contains \Drupal\Core\Render\Element\StatusMessages. + */ + +namespace Drupal\Core\Render\Element; + +use Drupal\Component\Utility\Crypt; +use Drupal\Core\Site\Settings; + +/** + * Provides a messages element. + * + * @RenderElement("status_messages") + */ +class StatusMessages extends RenderElement { + + /** + * {@inheritdoc} + * + * Generate the placeholder in a #pre_render callback, because the hash salt + * needs to be accessed, which may not yet be available when this is called. + */ + public function getInfo() { + return [ + // May have a value of 'status' or 'error' when only displaying messages + // of that specific type. + '#display' => NULL, + '#pre_render' => [ + get_class() . '::generatePlaceholder', + ], + ]; + } + + /** + * #pre_render callback to generate a placeholder. + * + * Ensures the same token is used for all instances, hence resulting in the + * same placeholder for all places rendering the status messages for this + * request (e.g. in multiple blocks). This ensures we can put the rendered + * messages in all placeholders in one go. + * Also ensures the same context key is used for the #post_render_cache + * property, this ensures that if status messages are rendered multiple times, + * their individual (but identical!) #post_render_cache properties are merged, + * ensuring the callback is only invoked once. + * + * @see ::renderMessages() + + * @param array $element + * A renderable array. + * + * @return array + * The updated renderable array containing the placeholder. + */ + public static function generatePlaceholder(array $element) { + $plugin_id = 'status_messages'; + + $callback = get_class() . '::renderMessages'; + try { + $hash_salt = Settings::getHashSalt(); + } + catch (\RuntimeException $e) { + // Status messages are also shown during the installer, at which time no + // hash salt is defined yet. + $hash_salt = Crypt::randomBytes(8); + } + $key = $plugin_id . $element['#display']; + $context = [ + 'display' => $element['#display'], + 'token' => Crypt::hmacBase64($key, $hash_salt), + ]; + $placeholder = static::renderer()->generateCachePlaceholder($callback, $context); + $element['#post_render_cache'] = [ + $callback => [ + $key => $context, + ], + ]; + $element['#markup'] = $placeholder; + + return $element; + } + + /** + * #post_render_cache callback; replaces placeholder with messages. + * + * Note: this is designed to replace all #post_render_cache placeholders for + * messages in a single #post_render_cache callback; hence all placeholders + * must be identical. + * + * @see ::getInfo() + * + * @param array $element + * The renderable array that contains the to be replaced placeholder. + * @param array $context + * An array with any context information. + * + * @return array + * A renderable array containing the messages. + */ + public static function renderMessages(array $element, array $context) { + $renderer = static::renderer(); + + // Render the messages. + $messages = [ + '#theme' => 'status_messages', + // @todo Improve when https://www.drupal.org/node/2278383 lands. + '#message_list' => drupal_get_messages($context['display']), + '#status_headings' => [ + 'status' => t('Status message'), + 'error' => t('Error message'), + 'warning' => t('Warning message'), + ], + ]; + $markup = $renderer->render($messages); + + // Replace the placeholder. + $callback = get_class() . '::renderMessages'; + $placeholder = $renderer->generateCachePlaceholder($callback, $context); + $element['#markup'] = str_replace($placeholder, $markup, $element['#markup']); + $element = $renderer->mergeBubbleableMetadata($element, $messages); + + return $element; + } + + /** + * Wraps the renderer. + * + * @return \Drupal\Core\Render\RendererInterface + */ + protected static function renderer() { + return \Drupal::service('renderer'); + } + +} diff --git a/core/lib/Drupal/Core/Render/MainContent/AjaxRenderer.php b/core/lib/Drupal/Core/Render/MainContent/AjaxRenderer.php index 6cddc0ceac8586304579be01f0c0a064aae846c3..6900b6e857f36103855f544aa7a879c1aa26451c 100644 --- a/core/lib/Drupal/Core/Render/MainContent/AjaxRenderer.php +++ b/core/lib/Drupal/Core/Render/MainContent/AjaxRenderer.php @@ -71,7 +71,7 @@ public function renderResponse(array $main_content, Request $request, RouteMatch // replace the element making the Ajax call. The default 'replaceWith' // behavior can be changed with #ajax['method']. $response->addCommand(new InsertCommand(NULL, $html)); - $status_messages = array('#theme' => 'status_messages'); + $status_messages = array('#type' => 'status_messages'); $output = $this->drupalRenderRoot($status_messages); if (!empty($output)) { $response->addCommand(new PrependCommand(NULL, $output)); diff --git a/core/lib/Drupal/Core/Render/Plugin/DisplayVariant/SimplePageVariant.php b/core/lib/Drupal/Core/Render/Plugin/DisplayVariant/SimplePageVariant.php index 7772875b14eae84452fcdd290e5988e4e385ac96..66f908605c4b086771eeabcdc3a5786b3916f5e9 100644 --- a/core/lib/Drupal/Core/Render/Plugin/DisplayVariant/SimplePageVariant.php +++ b/core/lib/Drupal/Core/Render/Plugin/DisplayVariant/SimplePageVariant.php @@ -39,7 +39,13 @@ public function setMainContent(array $main_content) { */ public function build() { $build = [ - 'content' => $this->mainContent, + 'content' => [ + 'main_content' => $this->mainContent, + 'messages' => [ + '#type' => 'status_messages', + '#weight' => -1000, + ], + ], ]; return $build; } diff --git a/core/modules/block/src/Plugin/DisplayVariant/BlockPageVariant.php b/core/modules/block/src/Plugin/DisplayVariant/BlockPageVariant.php index bf91a04fd4e947e1e944932d36326ae77c05a105..798805e6840b64b07a797af9fa3ac432772238b1 100644 --- a/core/modules/block/src/Plugin/DisplayVariant/BlockPageVariant.php +++ b/core/modules/block/src/Plugin/DisplayVariant/BlockPageVariant.php @@ -11,6 +11,7 @@ use Drupal\block\Event\BlockContextEvent; use Drupal\block\Event\BlockEvents; use Drupal\Core\Block\MainContentBlockPluginInterface; +use Drupal\Core\Block\MessagesBlockPluginInterface; use Drupal\Core\Display\PageVariantInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\EntityViewBuilderInterface; @@ -22,6 +23,14 @@ /** * Provides a page display variant that decorates the main content with blocks. * + * To ensure essential information is displayed, each essential part of a page + * has a corresponding block plugin interface, so that BlockPageVariant can + * automatically provide a fallback in case no block for each of these + * interfaces is placed. + * + * @see \Drupal\Core\Block\MainContentBlockPluginInterface + * @see \Drupal\Core\Block\MessagesBlockPluginInterface + * * @PageDisplayVariant( * id = "block_page", * admin_label = @Translation("Page with blocks") @@ -110,8 +119,9 @@ public function setMainContent(array $main_content) { * {@inheritdoc} */ public function build() { - // Track whether a block that shows the main content is displayed or not. + // Track whether blocks showing the main content and messages are displayed. $main_content_block_displayed = FALSE; + $messages_block_displayed = FALSE; $build = [ '#cache' => [ @@ -128,6 +138,9 @@ public function build() { $block_plugin->setMainContent($this->mainContent); $main_content_block_displayed = TRUE; } + elseif ($block_plugin instanceof MessagesBlockPluginInterface) { + $messages_block_displayed = TRUE; + } $build[$region][$key] = $this->blockViewBuilder->view($block); } if (!empty($build[$region])) { @@ -144,6 +157,14 @@ public function build() { $build['content']['system_main'] = $this->mainContent; } + // If no block displays status messages, still render them. + if (!$messages_block_displayed) { + $build['content']['messages'] = [ + '#weight' => -1000, + '#type' => 'status_messages', + ]; + } + return $build; } diff --git a/core/modules/block/tests/src/Unit/Plugin/DisplayVariant/BlockPageVariantTest.php b/core/modules/block/tests/src/Unit/Plugin/DisplayVariant/BlockPageVariantTest.php index c39d744a8c07185e31321cd4a120afbd5ea067a2..bb5390ce2e0b0401d324fa09af7162f898ad0264 100644 --- a/core/modules/block/tests/src/Unit/Plugin/DisplayVariant/BlockPageVariantTest.php +++ b/core/modules/block/tests/src/Unit/Plugin/DisplayVariant/BlockPageVariantTest.php @@ -70,23 +70,28 @@ public function setUpDisplayVariant($configuration = array(), $definition = arra public function providerBuild() { $blocks_config = array( 'block1' => array( - 'top', FALSE, + // region, is main content block, is messages block + 'top', FALSE, FALSE, ), // Test multiple blocks in the same region. 'block2' => array( - 'bottom', FALSE, + 'bottom', FALSE, FALSE, ), 'block3' => array( - 'bottom', FALSE, + 'bottom', FALSE, FALSE, ), // Test a block implementing MainContentBlockPluginInterface. 'block4' => array( - 'center', TRUE, + 'center', TRUE, FALSE, + ), + // Test a block implementing MessagesBlockPluginInterface. + 'block5' => array( + 'center', FALSE, TRUE, ), ); $test_cases = []; - $test_cases[] = [$blocks_config, 4, + $test_cases[] = [$blocks_config, 5, [ '#cache' => [ 'tags' => [ @@ -100,6 +105,7 @@ public function providerBuild() { // The main content was rendered via a block. 'center' => [ 'block4' => [], + 'block5' => [], '#sorted' => TRUE, ], 'bottom' => [ @@ -109,6 +115,37 @@ public function providerBuild() { ], ], ]; + unset($blocks_config['block5']); + $test_cases[] = [$blocks_config, 4, + [ + '#cache' => [ + 'tags' => [ + 'config:block_list', + ], + ], + 'top' => [ + 'block1' => [], + '#sorted' => TRUE, + ], + 'center' => [ + 'block4' => [], + '#sorted' => TRUE, + ], + 'bottom' => [ + 'block2' => [], + 'block3' => [], + '#sorted' => TRUE, + ], + // The messages are rendered via the fallback in case there is no block + // rendering the main content. + 'content' => [ + 'messages' => [ + '#weight' => -1000, + '#type' => 'status_messages', + ], + ], + ], + ]; unset($blocks_config['block4']); $test_cases[] = [$blocks_config, 3, [ @@ -126,10 +163,14 @@ public function providerBuild() { 'block3' => [], '#sorted' => TRUE, ], - // The main content was rendered via the fallback in case there is no - // block rendering the main content. + // The main content & messages are rendered via the fallback in case + // there are no blocks rendering them. 'content' => [ 'system_main' => ['#markup' => 'Hello kittens!'], + 'messages' => [ + '#weight' => -1000, + '#type' => 'status_messages', + ], ], ], ]; @@ -150,11 +191,12 @@ public function testBuild(array $blocks_config, $visible_block_count, array $exp $blocks = ['top' => [], 'center' => [], 'bottom' => []]; $block_plugin = $this->getMock('Drupal\Core\Block\BlockPluginInterface'); $main_content_block_plugin = $this->getMock('Drupal\Core\Block\MainContentBlockPluginInterface'); + $messages_block_plugin = $this->getMock('Drupal\Core\Block\MessagesBlockPluginInterface'); foreach ($blocks_config as $block_id => $block_config) { $block = $this->getMock('Drupal\block\BlockInterface'); $block->expects($this->atLeastOnce()) ->method('getPlugin') - ->willReturn($block_config[1] ? $main_content_block_plugin : $block_plugin); + ->willReturn($block_config[1] ? $main_content_block_plugin : ($block_config[2] ? $messages_block_plugin : $block_plugin)); $blocks[$block_config[0]][$block_id] = $block; } @@ -187,6 +229,10 @@ public function testBuildWithoutMainContent() { ], 'content' => [ 'system_main' => [], + 'messages' => [ + '#weight' => -1000, + '#type' => 'status_messages', + ], ], ]; $this->assertSame($expected, $display_variant->build()); diff --git a/core/modules/editor/src/Form/EditorImageDialog.php b/core/modules/editor/src/Form/EditorImageDialog.php index e3d592ad9c0954440f173a5501b026bc56ba47d8..b2fed135f1b6aca4a9c6d8b862dbc2fbbdc8d021 100644 --- a/core/modules/editor/src/Form/EditorImageDialog.php +++ b/core/modules/editor/src/Form/EditorImageDialog.php @@ -227,7 +227,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) { if ($form_state->getErrors()) { unset($form['#prefix'], $form['#suffix']); $form['status_messages'] = [ - '#theme' => 'status_messages', + '#type' => 'status_messages', '#weight' => -10, ]; $response->addCommand(new HtmlCommand('#editor-image-dialog-form', $form)); diff --git a/core/modules/editor/src/Form/EditorLinkDialog.php b/core/modules/editor/src/Form/EditorLinkDialog.php index feb1d1fced884cd57d15290b08f1e8f4d6fced51..c5763894da05a23281e702738f231633e2b12b8e 100644 --- a/core/modules/editor/src/Form/EditorLinkDialog.php +++ b/core/modules/editor/src/Form/EditorLinkDialog.php @@ -86,7 +86,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) { if ($form_state->getErrors()) { unset($form['#prefix'], $form['#suffix']); $form['status_messages'] = [ - '#theme' => 'status_messages', + '#type' => 'status_messages', '#weight' => -10, ]; $response->addCommand(new HtmlCommand('#editor-link-dialog-form', $form)); diff --git a/core/modules/file/src/Controller/FileWidgetAjaxController.php b/core/modules/file/src/Controller/FileWidgetAjaxController.php index e449caf159136ebfec44d59b7bd6947ee1f81e9e..90176f8da07ef7881918b9a56775661bae38ba6f 100644 --- a/core/modules/file/src/Controller/FileWidgetAjaxController.php +++ b/core/modules/file/src/Controller/FileWidgetAjaxController.php @@ -38,8 +38,8 @@ public function upload(Request $request) { // Invalid request. drupal_set_message(t('An unrecoverable error occurred. The uploaded file likely exceeded the maximum file size (@size) that this server supports.', array('@size' => format_size(file_upload_max_size()))), 'error'); $response = new AjaxResponse(); - $status_messages = array('#theme' => 'status_messages'); - return $response->addCommand(new ReplaceCommand(NULL, drupal_render($status_messages))); + $status_messages = array('#type' => 'status_messages'); + return $response->addCommand(new ReplaceCommand(NULL, $this->renderer->renderRoot($status_messages))); } try { @@ -53,8 +53,8 @@ public function upload(Request $request) { // Invalid form_build_id. drupal_set_message(t('An unrecoverable error occurred. Use of this form has expired. Try reloading the page and submitting again.'), 'error'); $response = new AjaxResponse(); - $status_messages = array('#theme' => 'status_messages'); - return $response->addCommand(new ReplaceCommand(NULL, drupal_render($status_messages))); + $status_messages = array('#type' => 'status_messages'); + return $response->addCommand(new ReplaceCommand(NULL, $this->renderer->renderRoot($status_messages))); } // Get the current element and count the number of files. @@ -76,9 +76,9 @@ public function upload(Request $request) { $form['#suffix'] .= '<span class="ajax-new-content"></span>'; } - $status_messages = array('#theme' => 'status_messages'); - $form['#prefix'] .= drupal_render($status_messages); - $output = drupal_render($form); + $status_messages = array('#type' => 'status_messages'); + $form['#prefix'] .= $this->renderer->renderRoot($status_messages); + $output = $this->renderer->renderRoot($form); $response = new AjaxResponse(); $response->setAttachments($form['#attached']); diff --git a/core/modules/quickedit/src/QuickEditController.php b/core/modules/quickedit/src/QuickEditController.php index 3747609e33fb141c2217f5f406ccc2cbde51508c..383f61b8151b534ea025f35bf6c9dacd02839341 100644 --- a/core/modules/quickedit/src/QuickEditController.php +++ b/core/modules/quickedit/src/QuickEditController.php @@ -9,6 +9,7 @@ use Drupal\Core\Controller\ControllerBase; use Drupal\Core\Form\FormState; +use Drupal\Core\Render\RendererInterface; use Drupal\user\PrivateTempStoreFactory; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\JsonResponse; @@ -47,6 +48,13 @@ class QuickEditController extends ControllerBase { */ protected $editorSelector; + /** + * The renderer. + * + * @var \Drupal\Core\Render\RendererInterface + */ + protected $renderer; + /** * Constructs a new QuickEditController. * @@ -56,11 +64,14 @@ class QuickEditController extends ControllerBase { * The in-place editing metadata generator. * @param \Drupal\quickedit\EditorSelectorInterface $editor_selector * The in-place editor selector. + * @param \Drupal\Core\Render\RendererInterface $renderer + * The renderer. */ - public function __construct(PrivateTempStoreFactory $temp_store_factory, MetadataGeneratorInterface $metadata_generator, EditorSelectorInterface $editor_selector) { + public function __construct(PrivateTempStoreFactory $temp_store_factory, MetadataGeneratorInterface $metadata_generator, EditorSelectorInterface $editor_selector, RendererInterface $renderer) { $this->tempStoreFactory = $temp_store_factory; $this->metadataGenerator = $metadata_generator; $this->editorSelector = $editor_selector; + $this->renderer = $renderer; } /** @@ -70,7 +81,8 @@ public static function create(ContainerInterface $container) { return new static( $container->get('user.private_tempstore'), $container->get('quickedit.metadata.generator'), - $container->get('quickedit.editor.selector') + $container->get('quickedit.editor.selector'), + $container->get('renderer') ); } @@ -204,7 +216,7 @@ public function fieldForm(EntityInterface $entity, $field_name, $langcode, $view $response->addCommand(new FieldFormSavedCommand($output, $other_view_modes)); } else { - $output = drupal_render($form); + $output = $this->renderer->renderRoot($form); // When working with a hidden form, we don't want its CSS/JS to be loaded. if ($request->request->get('nocssjs') !== 'true') { $response->setAttachments($form['#attached']); @@ -214,9 +226,9 @@ public function fieldForm(EntityInterface $entity, $field_name, $langcode, $view $errors = $form_state->getErrors(); if (count($errors)) { $status_messages = array( - '#theme' => 'status_messages' + '#type' => 'status_messages' ); - $response->addCommand(new FieldFormValidationErrorsCommand(drupal_render($status_messages))); + $response->addCommand(new FieldFormValidationErrorsCommand($this->renderer->renderRoot($status_messages))); } } @@ -263,7 +275,7 @@ protected function renderField(EntityInterface $entity, $field_name, $langcode, $output = $this->moduleHandler()->invoke($module, 'quickedit_render_field', $args); } - return drupal_render($output); + return $this->renderer->renderRoot($output); } /** diff --git a/core/modules/simpletest/src/KernelTestBase.php b/core/modules/simpletest/src/KernelTestBase.php index 89930adbb4ab323174471cb923a44450f598ba49..14b839b19beb25c9c769a5d09ba0bb3ae43069f3 100644 --- a/core/modules/simpletest/src/KernelTestBase.php +++ b/core/modules/simpletest/src/KernelTestBase.php @@ -560,7 +560,7 @@ protected function registerStreamWrapper($scheme, $class, $type = StreamWrapperI * The rendered string output (typically HTML). */ protected function render(array &$elements) { - $content = drupal_render($elements); + $content = $this->container->get('renderer')->renderRoot($elements); drupal_process_attached($elements); $this->setRawContent($content); $this->verbose('<pre style="white-space: pre-wrap">' . String::checkPlain($content)); diff --git a/core/modules/system/core.api.php b/core/modules/system/core.api.php index 407e439ee7dd3653e26ad8ea97ca3949ac6a4b1d..090d9142a782cc05dc820766e1092fa045663aaf 100644 --- a/core/modules/system/core.api.php +++ b/core/modules/system/core.api.php @@ -1991,7 +1991,7 @@ function hook_display_variant_plugin_alter(array &$definitions) { * 'wrapper' method and return HTML markup. This is not the case if you return * commands, but if you would like to show status messages, you can add * @code - * array('#theme' => 'status_messages') + * array('#type' => 'status_messages') * @endcode * to a render array, use drupal_render() to render it, and add a command to * place the messages in an appropriate location. diff --git a/core/modules/system/form.api.php b/core/modules/system/form.api.php index 9ec09e0dfd1b3686e35196fbcbe5ea5de08eceb8..765ce6094e4231522180570a820f0d5edab35e28 100644 --- a/core/modules/system/form.api.php +++ b/core/modules/system/form.api.php @@ -153,8 +153,8 @@ function callback_batch_finished($success, $results, $operations) { */ function hook_ajax_render_alter(array &$data) { // Inject any new status messages into the content area. - $status_messages = array('#theme' => 'status_messages'); - $command = new \Drupal\Core\Ajax\PrependCommand('#block-system-main .content', drupal_render($status_messages)); + $status_messages = array('#type' => 'status_messages'); + $command = new \Drupal\Core\Ajax\PrependCommand('#block-system-main .content', \Drupal::service('renderer')->renderRoot($status_messages)); $data[] = $command->render(); } diff --git a/core/modules/system/src/Controller/FormAjaxController.php b/core/modules/system/src/Controller/FormAjaxController.php index 8f122e9c5c59c61c13984d214ccb8be023efdab9..f1a39f6054b16d211b1d80324dcc89fe939cb143 100644 --- a/core/modules/system/src/Controller/FormAjaxController.php +++ b/core/modules/system/src/Controller/FormAjaxController.php @@ -10,6 +10,7 @@ use Drupal\Core\Ajax\UpdateBuildIdCommand; use Drupal\Core\Form\FormState; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; +use Drupal\Core\Render\RendererInterface; use Drupal\system\FileAjaxForm; use Drupal\Core\Form\FormBuilderInterface; use Psr\Log\LoggerInterface; @@ -37,18 +38,27 @@ class FormAjaxController implements ContainerInjectionInterface { */ protected $formBuilder; + /** + * The renderer. + * + * @var \Drupal\Core\Render\RendererInterface + */ + protected $renderer; + /** * Constructs a FormAjaxController object. * * @param \Psr\Log\LoggerInterface $logger * A logger instance. - * * @param \Drupal\Core\Form\FormBuilderInterface $form_builder * The form builder. + * @param \Drupal\Core\Render\RendererInterface $renderer + * The renderer. */ - public function __construct(LoggerInterface $logger, FormBuilderInterface $form_builder) { + public function __construct(LoggerInterface $logger, FormBuilderInterface $form_builder, RendererInterface $renderer) { $this->logger = $logger; $this->formBuilder = $form_builder; + $this->renderer = $renderer; } /** @@ -57,7 +67,8 @@ public function __construct(LoggerInterface $logger, FormBuilderInterface $form_ public static function create(ContainerInterface $container) { return new static( $container->get('logger.factory')->get('ajax'), - $container->get('form_builder') + $container->get('form_builder'), + $container->get('renderer') ); } diff --git a/core/modules/system/src/Plugin/Block/SystemMessagesBlock.php b/core/modules/system/src/Plugin/Block/SystemMessagesBlock.php new file mode 100644 index 0000000000000000000000000000000000000000..b7247801f3710168433fbba15755db6149e69a0c --- /dev/null +++ b/core/modules/system/src/Plugin/Block/SystemMessagesBlock.php @@ -0,0 +1,70 @@ +<?php + +/** + * @file + * Contains \Drupal\system\Plugin\Block\SystemMessagesBlock. + */ + +namespace Drupal\system\Plugin\Block; + +use Drupal\Core\Block\BlockBase; +use Drupal\Core\Block\MessagesBlockPluginInterface; +use Drupal\Core\Cache\Cache; +use Drupal\Core\Form\FormStateInterface; + +/** + * Provides a block to display the messages. + * + * @see drupal_set_message() + * + * @Block( + * id = "system_messages_block", + * admin_label = @Translation("Messages") + * ) + */ +class SystemMessagesBlock extends BlockBase implements MessagesBlockPluginInterface { + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return array( + 'label_display' => FALSE, + ); + } + + /** + * {@inheritdoc} + */ + public function build() { + return ['#type' => 'status_messages']; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $form = parent::buildConfigurationForm($form, $form_state); + + // @see ::isCacheable() + $form['cache']['#description'] = $this->t('This block is cacheable forever, it is not configurable.'); + $form['cache']['max_age']['#value'] = Cache::PERMANENT; + $form['cache']['max_age']['#disabled'] = TRUE; + // Don't allow cache contexts to be configured, this block is globally + // cacheable. + $form['cache']['contexts']['#access'] = FALSE; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function isCacheable() { + // The messages are session-specific and hence aren't cacheable, but the + // block itself *is* cacheable because it uses a #post_render_cache callback + // and hence the block has a globally cacheable render array. + return TRUE; + } + +} diff --git a/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php b/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php index da74be826d6853211040a73f3c5e93a74528f0dd..f4d30eceec39b4fe37e6d95558d7f4019a761396 100644 --- a/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php +++ b/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php @@ -91,6 +91,7 @@ function testPageCacheTags() { 'config:block.block.bartik_powered', 'config:block.block.bartik_main_menu', 'config:block.block.bartik_account_menu', + 'config:block.block.bartik_messages', 'node_view', 'node:' . $node_1->id(), 'user:' . $author_1->id(), @@ -116,6 +117,7 @@ function testPageCacheTags() { 'config:block.block.bartik_powered', 'config:block.block.bartik_main_menu', 'config:block.block.bartik_account_menu', + 'config:block.block.bartik_messages', 'node_view', 'node:' . $node_2->id(), 'user:' . $author_2->id(), diff --git a/core/modules/system/src/Tests/Theme/MessageTest.php b/core/modules/system/src/Tests/Theme/MessageTest.php index 3e8da82744ae8fd9e4e0549b9be0f19f6a83d0a8..db27d2a2f48e14b848fa368259102b270b0e9979 100644 --- a/core/modules/system/src/Tests/Theme/MessageTest.php +++ b/core/modules/system/src/Tests/Theme/MessageTest.php @@ -32,7 +32,7 @@ function testMessages() { drupal_set_message('An error occurred', 'error'); drupal_set_message('But then something nice happened'); $messages = array( - '#theme' => 'status_messages', + '#type' => 'status_messages', ); $this->render($messages); $this->assertRaw('messages messages--error'); diff --git a/core/modules/system/system.module b/core/modules/system/system.module index cf175ca251c00fe8195ddcea280c3e6ef7572330..d73ba633c220646e81788a50fd5b84ddf6c0ebca 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -168,6 +168,9 @@ function system_theme() { 'render element' => 'elements', 'base hook' => 'block', ), + 'block__system_messages_block' => array( + 'base hook' => 'block', + ), 'block__system_menu_block' => array( 'render element' => 'elements', 'base hook' => 'block', diff --git a/core/modules/system/templates/block--system-messages-block.html.twig b/core/modules/system/templates/block--system-messages-block.html.twig new file mode 100644 index 0000000000000000000000000000000000000000..f35af143810977472fd6d8d8501583e6345c9330 --- /dev/null +++ b/core/modules/system/templates/block--system-messages-block.html.twig @@ -0,0 +1,15 @@ +{# +/** + * @file + * Default theme implementation for the messages block. + * + * Removes wrapper elements from block so that empty block does not appear when + * there are no messages. + * + * Available variables: + * - content: The content of this block. + * + * @ingroup themeable + */ +#} +{{ content }} diff --git a/core/modules/system/templates/install-page.html.twig b/core/modules/system/templates/install-page.html.twig index 24d17faa865614be145cde859de5b016efda4fcc..d362d22c79c7629e5fea797c7ab4237d00124c69 100644 --- a/core/modules/system/templates/install-page.html.twig +++ b/core/modules/system/templates/install-page.html.twig @@ -30,7 +30,7 @@ {% if title %} <h1>{{ title }}</h1> {% endif %} - {{ messages }} + {{ page.messages }} {{ page.content }} </main> diff --git a/core/modules/system/templates/maintenance-page.html.twig b/core/modules/system/templates/maintenance-page.html.twig index cd0cfd02517f3b75bacde40020d6a7e77dad1e94..f2706210f300a3e7958cfcaec8ae286162012804 100644 --- a/core/modules/system/templates/maintenance-page.html.twig +++ b/core/modules/system/templates/maintenance-page.html.twig @@ -41,7 +41,7 @@ <h1>{{ title }}</h1> {% endif %} - {{ messages }} + {{ page.messages }} {{ page.content }} </main> diff --git a/core/modules/system/templates/page.html.twig b/core/modules/system/templates/page.html.twig index 627d7c7aeff604ba5ded5eab2a6abeb2cf24854d..eb5c7bf213a67ac4871e4cdd3c5c1c13f16f10e9 100644 --- a/core/modules/system/templates/page.html.twig +++ b/core/modules/system/templates/page.html.twig @@ -46,6 +46,7 @@ * - page.primary_menu: Items for the primary menu region. * - page.secondary_menu: Items for the secondary menu region. * - page.highlighted: Items for the highlighted content region. + * - page.messages: Status messages block can be placed in messages region. * - page.help: Dynamic help text, mostly for admin pages. * - page.content: The main content of the current page. * - page.sidebar_first: Items for the first sidebar. @@ -96,7 +97,7 @@ {{ page.breadcrumb }} - {{ messages }} + {{ page.messages }} {{ page.help }} diff --git a/core/modules/views/src/Plugin/views/area/Messages.php b/core/modules/views/src/Plugin/views/area/Messages.php index e00739fd2dc24e819ae684aad857aea7fb76e498..56bd4e4a2c5c4008be25901fb5f5e3751f353ebe 100644 --- a/core/modules/views/src/Plugin/views/area/Messages.php +++ b/core/modules/views/src/Plugin/views/area/Messages.php @@ -32,7 +32,7 @@ protected function defineOptions() { public function render($empty = FALSE) { if (!$empty || !empty($this->options['empty'])) { return array( - '#theme' => 'status_messages', + '#type' => 'status_messages', ); } return array(); diff --git a/core/modules/views/tests/src/Unit/Plugin/area/MessagesTest.php b/core/modules/views/tests/src/Unit/Plugin/area/MessagesTest.php index 63800213bee54bf333c99dc49c07b68450a3c770..3c18e79966de9934fd1dd0c1e1661ff2582118df 100644 --- a/core/modules/views/tests/src/Unit/Plugin/area/MessagesTest.php +++ b/core/modules/views/tests/src/Unit/Plugin/area/MessagesTest.php @@ -48,13 +48,13 @@ protected function setUp() { public function testRender() { // The handler is configured to show with empty views by default, so should // appear. - $this->assertSame(array('#theme' => 'status_messages'), $this->messagesHandler->render()); + $this->assertSame(array('#type' => 'status_messages'), $this->messagesHandler->render()); // Turn empty off, and make sure it isn't rendered. $this->messagesHandler->options['empty'] = FALSE; // $empty parameter passed to render will still be FALSE, so should still // appear. - $this->assertSame(array('#theme' => 'status_messages'), $this->messagesHandler->render()); + $this->assertSame(array('#type' => 'status_messages'), $this->messagesHandler->render()); // Should now be empty as both the empty option and parameter are empty. $this->assertSame(array(), $this->messagesHandler->render(TRUE)); } diff --git a/core/modules/views_ui/src/Form/Ajax/ViewsFormBase.php b/core/modules/views_ui/src/Form/Ajax/ViewsFormBase.php index 6dd609e3c8766d750a0d85f443841e5eb9fff393..17a31c1810ec6fa355f415362030fd91f65c680f 100644 --- a/core/modules/views_ui/src/Form/Ajax/ViewsFormBase.php +++ b/core/modules/views_ui/src/Form/Ajax/ViewsFormBase.php @@ -191,6 +191,9 @@ public function getForm(ViewEntityInterface $view, $display_id, $js) { * #markup array. */ protected function ajaxFormWrapper($form_class, FormStateInterface &$form_state) { + /** @var \Drupal\Core\Render\RendererInterface $renderer */ + $renderer = \Drupal::service('renderer'); + // This won't override settings already in. if (!$form_state->has('rerender')) { $form_state->set('rerender', FALSE); @@ -203,7 +206,7 @@ protected function ajaxFormWrapper($form_class, FormStateInterface &$form_state) $form_state->disableCache(); $form = \Drupal::formBuilder()->buildForm($form_class, $form_state); - $output = drupal_render($form); + $output = $renderer->renderRoot($form); drupal_process_attached($form); // These forms have the title built in, so set the title here: @@ -220,8 +223,8 @@ protected function ajaxFormWrapper($form_class, FormStateInterface &$form_state) $response->setAttachments($form['#attached']); $display = ''; - $status_messages = array('#theme' => 'status_messages'); - if ($messages = drupal_render($status_messages)) { + $status_messages = array('#type' => 'status_messages'); + if ($messages = $renderer->renderRoot($status_messages)) { $display = '<div class="views-messages">' . $messages . '</div>'; } $display .= $output; diff --git a/core/profiles/minimal/config/install/block.block.stark_messages.yml b/core/profiles/minimal/config/install/block.block.stark_messages.yml new file mode 100644 index 0000000000000000000000000000000000000000..838d8c046981bb10eb710299896142370e0e6bac --- /dev/null +++ b/core/profiles/minimal/config/install/block.block.stark_messages.yml @@ -0,0 +1,17 @@ +id: stark_messages +theme: stark +weight: 0 +status: true +langcode: en +region: messages +plugin: system_messages_block +settings: + id: system_messages_block + label: 'Status messages' + provider: system + label_display: '0' +dependencies: + module: + - system + theme: + - stark diff --git a/core/profiles/standard/config/install/block.block.bartik_messages.yml b/core/profiles/standard/config/install/block.block.bartik_messages.yml new file mode 100644 index 0000000000000000000000000000000000000000..1d817dda3fa45740f61b2ab9898f204e406c44aa --- /dev/null +++ b/core/profiles/standard/config/install/block.block.bartik_messages.yml @@ -0,0 +1,17 @@ +id: bartik_messages +theme: bartik +weight: 0 +status: true +langcode: en +region: messages +plugin: system_messages_block +settings: + id: system_messages_block + label: 'Status messages' + provider: system + label_display: '0' +dependencies: + module: + - system + theme: + - bartik diff --git a/core/profiles/standard/config/install/block.block.seven_messages.yml b/core/profiles/standard/config/install/block.block.seven_messages.yml new file mode 100644 index 0000000000000000000000000000000000000000..4a15ee9624c56f060a1c61245555aea870139f82 --- /dev/null +++ b/core/profiles/standard/config/install/block.block.seven_messages.yml @@ -0,0 +1,17 @@ +id: seven_messages +theme: seven +weight: 0 +status: true +langcode: en +region: messages +plugin: system_messages_block +settings: + id: system_messages_block + label: 'Status messages' + provider: system + label_display: '0' +dependencies: + module: + - system + theme: + - seven diff --git a/core/tests/Drupal/Tests/Core/Controller/AjaxRendererTest.php b/core/tests/Drupal/Tests/Core/Controller/AjaxRendererTest.php index 069d370c8933b5d46f2c0c23174e59e55563212a..e959fbae89d2520c997111dfa96edd616893aa0b 100644 --- a/core/tests/Drupal/Tests/Core/Controller/AjaxRendererTest.php +++ b/core/tests/Drupal/Tests/Core/Controller/AjaxRendererTest.php @@ -74,8 +74,8 @@ protected function drupalRenderRoot(&$elements, $is_root_call = FALSE) { if (isset($elements['#markup'])) { return $elements['#markup']; } - elseif (isset($elements['#theme'])) { - return $elements['#theme']; + elseif (isset($elements['#type'])) { + return $elements['#type']; } else { return 'Markup'; diff --git a/core/tests/Drupal/Tests/Core/Render/ElementInfoManagerTest.php b/core/tests/Drupal/Tests/Core/Render/ElementInfoManagerTest.php index e275309ff19e1d7175f305a97daebe8bec35663e..75b59ca7b8c538573c21341a95a10c48736127df 100644 --- a/core/tests/Drupal/Tests/Core/Render/ElementInfoManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Render/ElementInfoManagerTest.php @@ -125,12 +125,10 @@ public function providerTestGetInfo() { 'page', array( '#type' => 'page', - '#show_messages' => TRUE, '#theme' => 'page', '#defaults_loaded' => TRUE, ), array('page' => array( - '#show_messages' => TRUE, '#theme' => 'page', )), ); @@ -141,7 +139,6 @@ public function providerTestGetInfo() { '#defaults_loaded' => TRUE, ), array('page' => array( - '#show_messages' => TRUE, '#theme' => 'page', )), ); @@ -150,13 +147,11 @@ public function providerTestGetInfo() { 'page', array( '#type' => 'page', - '#show_messages' => TRUE, '#theme' => 'page', '#number' => 597219, '#defaults_loaded' => TRUE, ), array('page' => array( - '#show_messages' => TRUE, '#theme' => 'page', )), function ($alter_name, array &$info) { @@ -188,7 +183,6 @@ public function testGetInfoElementPlugin($plugin_class, $expected_info) { $plugin->expects($this->once()) ->method('getInfo') ->willReturn(array( - '#show_messages' => TRUE, '#theme' => 'page', )); @@ -225,7 +219,6 @@ public function providerTestGetInfoElementPlugin() { 'Drupal\Core\Render\Element\ElementInterface', array( '#type' => 'page', - '#show_messages' => TRUE, '#theme' => 'page', '#defaults_loaded' => TRUE, ), @@ -235,7 +228,6 @@ public function providerTestGetInfoElementPlugin() { 'Drupal\Core\Render\Element\FormElementInterface', array( '#type' => 'page', - '#show_messages' => TRUE, '#theme' => 'page', '#input' => TRUE, '#value_callback' => array('TestElementPlugin', 'valueCallback'), diff --git a/core/themes/bartik/bartik.info.yml b/core/themes/bartik/bartik.info.yml index 37a2c68896477d2b62d5db19b6dd9b82a85365f1..c96aae7d0ae7e888873534661488e794c7a671f7 100644 --- a/core/themes/bartik/bartik.info.yml +++ b/core/themes/bartik/bartik.info.yml @@ -19,6 +19,7 @@ regions: help: Help page_top: 'Page top' page_bottom: 'Page bottom' + messages: Messages featured_top: 'Featured top' breadcrumb: Breadcrumb content: Content diff --git a/core/themes/bartik/templates/maintenance-page.html.twig b/core/themes/bartik/templates/maintenance-page.html.twig index 3e41387f80d548a491d8db351909f1d63ff7172a..c4eae0113a637eb98fe53097b38a830319137fbf 100644 --- a/core/themes/bartik/templates/maintenance-page.html.twig +++ b/core/themes/bartik/templates/maintenance-page.html.twig @@ -39,13 +39,7 @@ <h1 class="title" id="page-title">{{ title }}</h1> {% endif %} {{ page.content }} - {% if messages %} - <div id="messages"> - <div class="section clearfix"> - {{ messages }} - </div> - </div> - {% endif %} + {{ page.messages }} </section> </main> </div> diff --git a/core/themes/bartik/templates/page.html.twig b/core/themes/bartik/templates/page.html.twig index 8429b447189b9cd7600b3dfe0f6715c1f360ed3f..04ffe94c576bc813a484338259b96de51b5a96aa 100644 --- a/core/themes/bartik/templates/page.html.twig +++ b/core/themes/bartik/templates/page.html.twig @@ -38,7 +38,6 @@ * - 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. * - tabs: Tabs linking to any sub-pages beneath the current page (e.g., the * view and edit tabs when displaying a node). * - action_links: Actions local to the page, such as "Add menu" on the menu @@ -50,6 +49,7 @@ * * Regions: * - page.header: Items for the header region. + * - page.messages: Status and error messages. Should be displayed prominently. * - page.primary_menu: Items for the primary menu region. * - page.secondary_menu: Items for the secondary menu region. * - page.featured_top: Items for the featured top region. @@ -109,11 +109,7 @@ {{ page.primary_menu }} </div> </header> - {% if messages %} - <div id="messages"> - <div class="section clearfix">{{ messages }}</div> - </div> - {% endif %} + {{ page.messages }} {% if page.featured_top %} <div class="featured-top"> <aside class="featured-top__inner clearfix" role="complementary"> diff --git a/core/themes/bartik/templates/status-messages.html.twig b/core/themes/bartik/templates/status-messages.html.twig new file mode 100644 index 0000000000000000000000000000000000000000..a3b5d49bedf8bfb19d1551cbab65b0afe40d3344 --- /dev/null +++ b/core/themes/bartik/templates/status-messages.html.twig @@ -0,0 +1,33 @@ +{% extends "@classy/misc/status-messages.html.twig" %} +{# +/** + * @file + * Default theme implementation for status messages. + * + * Displays status, error, and warning messages, grouped by type. + * + * An invisible heading identifies the messages for assistive technology. + * Sighted users see a colored box. See http://www.w3.org/TR/WCAG-TECHS/H69.html + * for info. + * + * Add an ARIA label to the contentinfo area so that assistive technology + * user agents will better describe this landmark. + * + * Available variables: + * - message_list: List of messages to be displayed, grouped by type. + * - status_headings: List of all status types. + * - display: (optional) May have a value of 'status' or 'error' when only + * displaying messages of that specific type. + * + * @see template_preprocess_status_messages() + */ +#} +{% block messages %} + {% if message_list is not empty %} + <div id="messages"> + <div class="section clearfix"> + {{ parent() }} + </div> + </div> + {% endif %} +{% endblock messages %} diff --git a/core/themes/classy/templates/layout/maintenance-page.html.twig b/core/themes/classy/templates/layout/maintenance-page.html.twig index fd780d457e8db6259929892691675e0c884be805..eaae5ab4b0a9661474e31f1b8147e08db447d150 100644 --- a/core/themes/classy/templates/layout/maintenance-page.html.twig +++ b/core/themes/classy/templates/layout/maintenance-page.html.twig @@ -41,7 +41,7 @@ <h1>{{ title }}</h1> {% endif %} - {{ messages }} + {{ page.messages }} {{ page.content }} </main> diff --git a/core/themes/classy/templates/layout/page.html.twig b/core/themes/classy/templates/layout/page.html.twig index 627d7c7aeff604ba5ded5eab2a6abeb2cf24854d..437a11c10f52eedde4acfca9b0a1337d6cd36706 100644 --- a/core/themes/classy/templates/layout/page.html.twig +++ b/core/themes/classy/templates/layout/page.html.twig @@ -31,7 +31,6 @@ * - 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. * - tabs: Tabs linking to any sub-pages beneath the current page (e.g., the * view and edit tabs when displaying a node). * - action_links: Actions local to the page, such as "Add menu" on the menu @@ -43,6 +42,7 @@ * * Regions: * - page.header: Items for the header region. + * - page.messages: Status and error messages. Should be displayed prominently. * - page.primary_menu: Items for the primary menu region. * - page.secondary_menu: Items for the secondary menu region. * - page.highlighted: Items for the highlighted content region. @@ -96,7 +96,7 @@ {{ page.breadcrumb }} - {{ messages }} + {{ page.messages }} {{ page.help }} diff --git a/core/themes/classy/templates/misc/status-messages.html.twig b/core/themes/classy/templates/misc/status-messages.html.twig index a2f36311ff8463fd0624eb008ee5625e8860c330..fb1ebe33de5a653c79b19ef6e9a145a836dd6f4e 100644 --- a/core/themes/classy/templates/misc/status-messages.html.twig +++ b/core/themes/classy/templates/misc/status-messages.html.twig @@ -25,6 +25,7 @@ * @ingroup themeable */ #} +{% block messages %} {% for type, messages in message_list %} {% set classes = [ @@ -55,3 +56,4 @@ {# Remove type specific classes. #} {{ attributes.removeClass(classes) }} {% endfor %} +{% endblock messages %} diff --git a/core/themes/seven/seven.info.yml b/core/themes/seven/seven.info.yml index 945ceacd95fbcc94f7baad003f77b2c6fb01f0be..0f5248c84fc46671100b0d998ea85a94c5cddc44 100644 --- a/core/themes/seven/seven.info.yml +++ b/core/themes/seven/seven.info.yml @@ -19,6 +19,7 @@ quickedit_stylesheets: - css/components/quickedit.css regions: content: Content + messages: Messages help: Help page_top: 'Page top' page_bottom: 'Page bottom' diff --git a/core/themes/seven/templates/install-page.html.twig b/core/themes/seven/templates/install-page.html.twig index 9f4db8d39142c8052b38d9a6a048486c8bfaac77..0e22c947a25490e27067e0b7c63a2a6e5a882c03 100644 --- a/core/themes/seven/templates/install-page.html.twig +++ b/core/themes/seven/templates/install-page.html.twig @@ -27,7 +27,7 @@ {% if title %} <h1>{{ title }}</h1> {% endif %} - {{ messages }} + {{ page.messages }} {{ page.content }} </main> diff --git a/core/themes/seven/templates/maintenance-page.html.twig b/core/themes/seven/templates/maintenance-page.html.twig index f2bc53be302e76eb9c91663a47c5eaef0d423755..1bfea5a738b47d8682c29829d7d11550997e30fb 100644 --- a/core/themes/seven/templates/maintenance-page.html.twig +++ b/core/themes/seven/templates/maintenance-page.html.twig @@ -27,7 +27,7 @@ {% if title %} <h1>{{ title }}</h1> {% endif %} - {{ messages }} + {{ page.messages }} {{ page.content }} </main> diff --git a/core/themes/seven/templates/page.html.twig b/core/themes/seven/templates/page.html.twig index fc3c7d4a0e88c568e5ad7ece3e73be527d07e921..4110987f0f6818926d90bb86bc892adb272ebc94 100644 --- a/core/themes/seven/templates/page.html.twig +++ b/core/themes/seven/templates/page.html.twig @@ -32,7 +32,6 @@ * - 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. * - tabs: Tabs linking to any sub-pages beneath the current page (e.g., the * view and edit tabs when displaying a node). * - action_links: Actions local to the page, such as "Add menu" on the menu @@ -45,6 +44,7 @@ * Regions: * - page.page_top: Items for the header region. * - page.highlighted: Items for the highlighted content region. + * - page.messages: Status and error messages. Should be displayed prominently. * - page.help: Dynamic help text, mostly for admin pages. * - page.content: The main content of the current page. * - page.sidebar_first: Items for the first sidebar. @@ -79,7 +79,7 @@ <main class="page-content clearfix" role="main"> <div class="visually-hidden"><a id="main-content" tabindex="-1"></a></div> - {{ messages }} + {{ page.messages }} {% if page.help %} <div class="help"> {{ page.help }}