.../modules/system/src/MessagesPostRenderCache.php | 22 +++++--------- .../src/Plugin/Block/SystemPageMessagesBlock.php | 34 +++++++++++++++++----- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/core/modules/system/src/MessagesPostRenderCache.php b/core/modules/system/src/MessagesPostRenderCache.php index 2cfc912..a8b1a57 100644 --- a/core/modules/system/src/MessagesPostRenderCache.php +++ b/core/modules/system/src/MessagesPostRenderCache.php @@ -12,19 +12,16 @@ /** * Defines a service for messages post render cache callbacks. + * + * 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 \Drupal\system\Plugin\Block\SystemPageMessagesBlock::build() */ class MessagesPostRenderCache { /** - * The messages. - * - * They are stored in a property so they can appear in multiple blocks. - * - * @var string[] - */ - protected $messages; - - /** * The renderer. * * @var \Drupal\Core\Render\RendererInterface @@ -53,15 +50,10 @@ public function __construct(RendererInterface $renderer) { * A renderable array containing the messages. */ public function renderMessages(array $element, array $context) { - // Collect the messages. - if (!isset($this->messages)) { - $this->messages = drupal_get_messages(); - } - // Render the messages. $messages = [ '#theme' => 'status_messages', - '#message_list' => $this->messages, + '#message_list' => drupal_get_messages(), '#status_headings' => [ 'status' => t('Status message'), 'error' => t('Error message'), diff --git a/core/modules/system/src/Plugin/Block/SystemPageMessagesBlock.php b/core/modules/system/src/Plugin/Block/SystemPageMessagesBlock.php index 7dfc02f..ed494a5 100644 --- a/core/modules/system/src/Plugin/Block/SystemPageMessagesBlock.php +++ b/core/modules/system/src/Plugin/Block/SystemPageMessagesBlock.php @@ -7,10 +7,12 @@ namespace Drupal\system\Plugin\Block; +use Drupal\Component\Utility\Crypt; use Drupal\Core\Block\BlockBase; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Site\Settings; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -70,15 +72,27 @@ public function defaultConfiguration() { /** * {@inheritdoc} + * + * Ensure the same token is used for all instances, hence resulting in the + * same placeholder for all message blocks. This ensures we can put the + * rendered messages in all message blocks in one go. + * Also ensure the same context key is used for the #post_render_cache + * property, this ensures that if multiple messages blocks exists, their + * individual (but identical!) #post_render_cache properties are merged, + * ensuring the callback is only invoked once. + * + * @see \Drupal\system\MessagesPostRenderCache() */ public function build() { $callback = 'system.messages_post_render_cache:renderMessages'; - $context = []; + $context = [ + 'token' => Crypt::hmacBase64($this->getPluginId(), Settings::getHashSalt()), + ]; $placeholder = drupal_render_cache_generate_placeholder($callback, $context); return [ '#post_render_cache' => array( $callback => array( - $context, + $this->getPluginId() => $context, ), ), '#markup' => $placeholder, @@ -92,9 +106,12 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta $form = parent::buildConfigurationForm($form, $form_state); // @see ::isCacheable() - $form['cache']['#disabled'] = TRUE; - $form['cache']['#description'] = $this->t('This block is never cacheable, it is not configurable.'); - $form['cache']['max_age']['#value'] = 0; + $form['cache']['#description'] = $this->t('This block is cacheable forever, it is not configurable.'); + $form['cache']['max_age']['#value'] = -1; + $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; } @@ -103,9 +120,10 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta * {@inheritdoc} */ public function isCacheable() { - // The page messages block is never cacheable, because its contents are - // session-specific and caching is useless. - return FALSE; + // 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; } }