diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 5300d76..ced9c4c 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -514,10 +514,12 @@ function watchdog_exception($type, Exception $exception, $message = NULL, $varia * drupal_set_message(t('An error occurred and processing did not complete.'), 'error'); * @endcode * - * @param string|\Drupal\Component\Utility\SafeStringInterface $message + * @param string|array|\Drupal\Component\Utility\SafeStringInterface $message * (optional) The translated message to be displayed to the user. For * consistency with other messages, it should begin with a capital letter and - * end with a period. + * end with a period. A render array may also be provided for markup, but + * #attached is not supported. Additionally, any cache tags and contexts are + * ignored, however this is okay because page caching is disabled. * @param string $type * (optional) The message's type. Defaults to 'status'. These values are * supported: @@ -566,6 +568,18 @@ function drupal_set_message($message = NULL, $type = 'status', $repeat = FALSE) $message = \Drupal\Core\Render\SafeString::create($message); } + // If the message is a render array, render it to a string now to avoid + // storing render arrays in $_SESSION, because rendering across requests + // may have unpredictable results and some render array functionality + // (like assets) should not be supported. + if (is_array($message)) { + $rendered_message = \Drupal::service('renderer')->renderPlain($message); + // The rendering will have all attachments bubbled to #attached in the top + // level of $message. Assert that there are none. + assert('empty($message[\'#attached\']);', 'Render arrays passed to drupal_set_message() cannot have attachments.'); + $message = $rendered_message; + } + // Do not use strict type checking so that equivalent string and // SafeStringInterface objects are detected. if ($repeat || !in_array($message, $_SESSION['messages'][$type])) { diff --git a/core/modules/system/src/Tests/Bootstrap/DrupalSetMessageTest.php b/core/modules/system/src/Tests/Bootstrap/DrupalSetMessageTest.php index 1b63324..e54e30a 100644 --- a/core/modules/system/src/Tests/Bootstrap/DrupalSetMessageTest.php +++ b/core/modules/system/src/Tests/Bootstrap/DrupalSetMessageTest.php @@ -37,6 +37,15 @@ function testDrupalSetMessage() { $this->assertUniqueText('Non Duplicated message'); $this->assertNoUniqueText('Duplicated message'); + // Ensure render arrays are rendered. + $this->assertRaw('Render array with markup!'); + $this->assertUniqueText('Render array with markup!'); + $this->assertRaw('Render array 2 with markup!'); + + // Ensure render arrays with objects are rendered. + $this->assertRaw('Render array with SafeString markup!'); + $this->assertUniqueText('Render array with SafeString markup!'); + // Ensure SafeString objects are rendered as expected. $this->assertRaw('SafeString with markup!'); $this->assertUniqueText('SafeString with markup!'); @@ -45,9 +54,13 @@ function testDrupalSetMessage() { // Ensure when the same message is of different types it is not duplicated. $this->assertUniqueText('Non duplicate SafeString / string.'); $this->assertNoUniqueText('Duplicate SafeString / string.'); + $this->assertUniqueText('Duplicate render array / string'); // Ensure that strings that are not marked as safe are escaped. $this->assertEscaped('Thismarkup will be escaped.'); + + // Ensure the runtime assertion fires on cue. + $this->assertUniqueText('Correct runtime assertion thrown when render array contains #attached.'); } } diff --git a/core/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php b/core/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php index 15ab478..f4a566f 100644 --- a/core/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php +++ b/core/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php @@ -114,6 +114,18 @@ public function drupalSetMessageTest() { drupal_set_message('Duplicated message', 'status', TRUE); drupal_set_message('Duplicated message', 'status', TRUE); + // Add a render array message. + drupal_set_message(['#markup' => 'Render array with markup!']); + // Test duplicate render arrays. + drupal_set_message(['#markup' => 'Render array with markup!']); + // Ensure that multiple render array messages work. + drupal_set_message(['#markup' => 'Render array 2 with markup!']); + + // Add a render array message containing objects. + drupal_set_message(['#markup' => SafeString::create('Render array with SafeString markup!')]); + // Test duplicate render array messages. + drupal_set_message(['#markup' => SafeString::create('Render array with SafeString markup!')]); + // Add a SafeString message. drupal_set_message(SafeString::create('SafeString with markup!')); // Test duplicate SafeString messages. @@ -126,10 +138,41 @@ public function drupalSetMessageTest() { drupal_set_message('Non duplicate SafeString / string.'); drupal_set_message(SafeString::create('Duplicate SafeString / string.'), 'status', TRUE); drupal_set_message('Duplicate SafeString / string.', 'status', TRUE); + drupal_set_message(['#markup' => 'Duplicate render array / string']); + drupal_set_message('Duplicate render array / string'); // Test auto-escape of non safe strings. drupal_set_message('Thismarkup will be escaped.'); + // Test setting messages with a render array containing #attached. + + // @todo Remove this after https://www.drupal.org/node/2536560 is finished. + assert_options(ASSERT_ACTIVE, TRUE); + assert_options(ASSERT_CALLBACK, function($file, $line, $code, $message) { + throw new \Exception($message); + }); + // End block to be removed. + + $array_with_attachments['foo']['bar']['#markup'] = t('Render array with assets'); + $array_with_attachments['foo']['bar']['#attached']['drupalSettings'] = ['pecary' => TRUE]; + + try { + drupal_set_message($array_with_attachments); + } + catch (\Exception $e) { + // @todo Switch to next line when #2536560 is finished. + # catch (\AssertionError $e) { + + // If the exception is the not the one we expect, rethrow causing a test + // failure. + if ($e->getMessage() !== 'Render arrays passed to drupal_set_message() cannot have attachments.') { + throw $e; + } + else { + drupal_set_message('Correct runtime assertion thrown when render array contains #attached.'); + } + } + return []; }