diff --git a/core/lib/Drupal/Core/Render/HtmlResponseAttachmentsProcessor.php b/core/lib/Drupal/Core/Render/HtmlResponseAttachmentsProcessor.php
index f777a53..5d189af 100644
--- a/core/lib/Drupal/Core/Render/HtmlResponseAttachmentsProcessor.php
+++ b/core/lib/Drupal/Core/Render/HtmlResponseAttachmentsProcessor.php
@@ -120,70 +120,51 @@ public function processAttachments(AttachmentsInterface $response) {
throw new \InvalidArgumentException('\Drupal\Core\Render\HtmlResponse instance expected.');
}
- $attached = $response->getAttachments();
+ // Since mergeBubbleableMetadata() needs full render arrays, we must set one
+ // up here.
+ $attached = [];
+ $attached['#attached'] = $response->getAttachments();
// Get the placeholders from attached and then remove them.
- $placeholders = $attached['html_response_placeholders'];
- unset($attached['html_response_placeholders']);
+ $placeholders = $attached['#attached']['html_response_placeholders'];
+ unset($attached['#attached']['html_response_placeholders']);
- $variables = $this->processAssetLibraries($attached, $placeholders);
+ $variables = $this->processAssetLibraries($attached['#attached'], $placeholders);
// Since we can only replace content in the HTML head section if there's a
// placeholder for it, we can safely avoid processing the render array if
// it's not present.
if (!empty($placeholders['head'])) {
// 'feed' is a special case of 'html_head_link'. We create an
- // 'html_head_link' for the RSS feed.
- if (!empty($attached['feed'])) {
- // @todo: Determine if feed is supposed to be a single structure or an
- // array of structures, and then refactor this array manipulation into
- // a separate method if needed.
- $feed = $attached['feed'];
-
- if (is_array($feed[0])) {
- foreach ($feed as $item) {
- $feed_link = [
- 'href' => $item[0],
- 'rel' => 'alternate',
- 'title' => empty($feed[1]) ? '' : $item[1],
- 'type' => 'application/rss+xml',
- ];
- $attached['html_head_link'][] = [$feed_link];
- }
- }
- else {
- $feed_link = [
- 'href' => $feed[0],
- 'rel' => 'alternate',
- 'title' => empty($feed[1]) ? '' : $feed[1],
- 'type' => 'application/rss+xml',
- ];
- $attached['html_head_link'][] = $feed_link;
- }
+ // 'html_head_link' for the RSS feed and merge it.
+ if (!empty($attached['#attached']['feed'])) {
+ // Since mergeBubbleableMetadata() requires a complete render array, we
+ // have to add the '#attached' key.
+ $attached = $this->renderer->mergeBubbleableMetadata(
+ $attached,
+ ['#attached' => $this->processFeed($attached['#attached']['feed'])]
+ );
}
-
// 'html_head_link' is a special case of 'html_head' which can be present
// as a head element, but also as a Link: HTTP header depending on
// settings in the render array. Processing it can add to both the
// 'html_head' and 'http_header' keys of '#attached', so we must address
- // it first.
- if (!empty($attached['html_head_link'])) {
+ // it before 'html_head'.
+ if (!empty($attached['#attached']['html_head_link'])) {
// Merge the processed 'html_head_link' into $attached so that its
// 'html_head' and 'http_header' values are present for further
// processing. The merge requires a full render array, so we have to add
// '#attached' keys.
$attached = $this->renderer->mergeBubbleableMetadata(
- ['#attached' => $attached],
- ['#attached' => $this->processHtmlHeadLink($attached['html_head_link'])]
+ $attached,
+ ['#attached' => $this->processHtmlHeadLink($attached['#attached']['html_head_link'])]
);
- // Unwrap the '#attached' key.
- $attached = $attached['#attached'];
}
// Now we can process 'html_head', which contains both 'feed' and
// 'html_head_link'.
- if (!empty($attached['html_head'])) {
- $html_head = $this->processHtmlHead($attached['html_head']);
+ if (!empty($attached['#attached']['html_head'])) {
+ $html_head = $this->processHtmlHead($attached['#attached']['html_head']);
// Invoke hook_html_head_alter().
$this->moduleHandler->alter('html_head', $html_head);
// Store the result in $variables so it can be inserted into the
@@ -196,8 +177,8 @@ public function processAttachments(AttachmentsInterface $response) {
$this->renderPlaceholders($response, $placeholders, $variables);
// Set the HTTP headers on the response if any bubbled.
- if (!empty($attached['http_header'])) {
- $this->setHeaders($response, $attached['http_header']);
+ if (!empty($attached['#attached']['http_header'])) {
+ $this->setHeaders($response, $attached['#attached']['http_header']);
}
return $response;
@@ -289,7 +270,8 @@ protected function setHeaders(HtmlResponse $response, array $headers) {
// Drupal treats the HTTP response status code like a header, even though
// it really is not.
if (strtolower($name) === 'status') {
- $response->setStatusCode($value);
+ list ($code, $message) = explode(' ', $value, 2);
+ $response->setStatusCode($code, $message);
}
$response->headers->set($name, $value, $replace);
}
@@ -325,6 +307,11 @@ protected function processHtmlHead(array $html_head) {
* depending on options in the render array. Processing it can add to both the
* html_head and http_header sections.
*
+ * Each head link is specified by a two-element array:
+ * - An array specifying the attributes of the link.
+ * - A boolean specifying whether the link should also be a Link: HTTP
+ * header.
+ *
* @param array $html_head_link
* The 'html_head_link' value of a render array.
*
@@ -359,4 +346,30 @@ protected function processHtmlHeadLink(array $html_head_link) {
return $attached;
}
+ /**
+ *
+ * @param array $attached_feed
+ * The ['#attached']['feed'] portion of a render array.
+ *
+ * @return array
+ * An ['#attached']['html_head_link'] array, suitable for merging with
+ * another 'html_head_link' array.
+ */
+ protected function processFeed($attached_feed) {
+ // @todo: Determine if feed is supposed to be a single structure or an
+ // array of structures, and then refactor this array manipulation into
+ // a separate method if needed.
+ $html_head_link = [];
+ foreach($attached_feed as $item) {
+ $feed_link = [
+ 'rel' => 'alternate',
+ 'type' => 'application/rss+xml',
+ 'href' => $item[0],
+ 'title' => empty($attached_feed[1]) ? '' : $item[1],
+ ];
+ $html_head_link[] = [$feed_link, FALSE];
+ }
+ return ['html_head_link' => $html_head_link];
+ }
+
}
diff --git a/core/lib/Drupal/Core/Render/theme.api.php b/core/lib/Drupal/Core/Render/theme.api.php
index 42fe60e..373efa2 100644
--- a/core/lib/Drupal/Core/Render/theme.api.php
+++ b/core/lib/Drupal/Core/Render/theme.api.php
@@ -369,7 +369,7 @@
* @code
* $build['#attached']['library'][] = 'core/jquery';
* $build['#attached']['drupalSettings']['foo'] = 'bar';
- * $build['#attached']['feed'][] = ['aggregator/rss', $this->t('Feed title')];
+ * $build['#attached']['feed'][] = [$url, $this->t('Feed title')];
* @endcode
*
* See \Drupal\Core\Render\AttachmentsResponseProcessorInterface for additional
diff --git a/core/modules/aggregator/src/Controller/AggregatorController.php b/core/modules/aggregator/src/Controller/AggregatorController.php
index 13ff3d9..398da98 100644
--- a/core/modules/aggregator/src/Controller/AggregatorController.php
+++ b/core/modules/aggregator/src/Controller/AggregatorController.php
@@ -174,6 +174,7 @@ public function pageLast() {
$items = $this->entityManager()->getStorage('aggregator_item')->loadAll(20);
$build = $this->buildPageList($items);
$build['#attached']['feed'][] = array('aggregator/rss', $this->config('system.site')->get('name') . ' ' . $this->t('aggregator'));
+ error_log(print_r($build, TRUE));
return $build;
}
diff --git a/core/modules/system/src/Tests/HttpKernel/HeadersResponseCodeRenderTest.php b/core/modules/system/src/Tests/HttpKernel/HeadersResponseCodeRenderTest.php
deleted file mode 100644
index 7a81a0d..0000000
--- a/core/modules/system/src/Tests/HttpKernel/HeadersResponseCodeRenderTest.php
+++ /dev/null
@@ -1,37 +0,0 @@
-drupalGet('/httpkernel-test/teapot');
- $this->assertResponse(418);
- $this->assertHeader('X-Test-Teapot', 'Teapot Mode Active');
- $this->assertHeader('X-Test-Teapot-Replace', 'Teapot replaced');
- $this->assertHeader('X-Test-Teapot-No-Replace', 'This value is not replaced,This one is added');
- }
-
-}
diff --git a/core/modules/system/src/Tests/Render/HtmlResponseAttachmentsTest.php b/core/modules/system/src/Tests/Render/HtmlResponseAttachmentsTest.php
new file mode 100644
index 0000000..1268ab3
--- /dev/null
+++ b/core/modules/system/src/Tests/Render/HtmlResponseAttachmentsTest.php
@@ -0,0 +1,46 @@
+drupalGet('/render_attached_test/teapot');
+ $this->assertResponse(418);
+
+ $this->drupalGet('/render_attached_test/head');
+ $this->assertHeader('X-Test-Teapot', 'Teapot Mode Active');
+ $this->assertHeader('X-Test-Teapot-Replace', 'Teapot replaced');
+ $this->assertHeader('X-Test-Teapot-No-Replace', 'This value is not replaced,This one is added');
+
+ $this->drupalGet('/render_attached_test/feed');
+ $this->assertEqual(1, count($this->xpath('//head/link[@rel="alternate"]')), 'Link has rel="alternate"');
+ $this->assertEqual(1, count($this->xpath('//head/link[@type="application/rss+xml"]')), 'Link has proper content type.');
+ $this->assertEqual(1, count($this->xpath('//head/link[@href="test://url"]')), 'Link has URL.');
+ $this->assertEqual(1, count($this->xpath('//head/link[@title=""]')), 'Link has title.');
+ }
+
+}
diff --git a/core/modules/system/tests/modules/httpkernel_test/src/Controller/TestController.php b/core/modules/system/tests/modules/httpkernel_test/src/Controller/TestController.php
index 5730080..1699594 100644
--- a/core/modules/system/tests/modules/httpkernel_test/src/Controller/TestController.php
+++ b/core/modules/system/tests/modules/httpkernel_test/src/Controller/TestController.php
@@ -21,21 +21,4 @@ public function get() {
return new Response();
}
- /**
- * Test special header and status code rendering.
- *
- * @return array
- * A render array using features of the 'http_header' directive.
- */
- public function teapot() {
- $render = [];
- $render['#attached']['http_header'][] = ['X-Test-Teapot-Replace', 'This value gets replaced'];
- $render['#attached']['http_header'][] = ['X-Test-Teapot-Replace', 'Teapot replaced', TRUE];
- $render['#attached']['http_header'][] = ['X-Test-Teapot-No-Replace', 'This value is not replaced'];
- $render['#attached']['http_header'][] = ['X-Test-Teapot-No-Replace', 'This one is added', FALSE];
- $render['#attached']['http_header'][] = ['X-Test-Teapot', 'Teapot Mode Active'];
- $render['#attached']['http_header'][] = ['Status', "418 I'm a teapot."];
- return $render;
- }
-
}
diff --git a/core/modules/system/tests/modules/render_attached_test/render_attached_test.info.yml b/core/modules/system/tests/modules/render_attached_test/render_attached_test.info.yml
new file mode 100644
index 0000000..db4ff45
--- /dev/null
+++ b/core/modules/system/tests/modules/render_attached_test/render_attached_test.info.yml
@@ -0,0 +1,6 @@
+name: 'Rendering #attached test'
+type: module
+description: 'Support module for HtmlResponseAttachmentsTest.'
+package: Testing
+version: VERSION
+core: 8.x
diff --git a/core/modules/system/tests/modules/render_attached_test/render_attached_test.routing.yml b/core/modules/system/tests/modules/render_attached_test/render_attached_test.routing.yml
new file mode 100644
index 0000000..e7329ef
--- /dev/null
+++ b/core/modules/system/tests/modules/render_attached_test/render_attached_test.routing.yml
@@ -0,0 +1,20 @@
+render_attached.teapot:
+ path: '/render_attached_test/teapot'
+ defaults:
+ _controller: '\Drupal\render_attached_test\Controller\TestController::teapotHeaderStatus'
+ requirements:
+ _access: 'TRUE'
+
+render_attached.head:
+ path: '/render_attached_test/head'
+ defaults:
+ _controller: '\Drupal\render_attached_test\Controller\TestController::head'
+ requirements:
+ _access: 'TRUE'
+
+render_attached.feed_single:
+ path: '/render_attached_test/feed'
+ defaults:
+ _controller: '\Drupal\render_attached_test\Controller\TestController::feed'
+ requirements:
+ _access: 'TRUE'
diff --git a/core/modules/system/tests/modules/httpkernel_test/src/Controller/TestController.php b/core/modules/system/tests/modules/render_attached_test/src/Controller/TestController.php
similarity index 61%
copy from core/modules/system/tests/modules/httpkernel_test/src/Controller/TestController.php
copy to core/modules/system/tests/modules/render_attached_test/src/Controller/TestController.php
index 5730080..66b1419 100644
--- a/core/modules/system/tests/modules/httpkernel_test/src/Controller/TestController.php
+++ b/core/modules/system/tests/modules/render_attached_test/src/Controller/TestController.php
@@ -2,39 +2,49 @@
/**
* @file
- * Contains \Drupal\httpkernel_test\Controller\TestController.
+ * Contains \Drupal\render_attached_test\Controller\TestController.
*/
-namespace Drupal\httpkernel_test\Controller;
+namespace Drupal\render_attached_test\Controller;
use Symfony\Component\HttpFoundation\Response;
/**
- * A test controller.
+ * Controller for various permutations of #attached in the render array.
*/
class TestController {
/**
- * Return an empty response.
+ * Test special header and status code rendering.
+ *
+ * @return array
+ * A render array using features of the 'http_header' directive.
*/
- public function get() {
- return new Response();
+ public function teapotHeaderStatus() {
+ $render = [];
+ $render['#attached']['http_header'][] = ['Status', "418 I'm a teapot."];
+ return $render;
}
/**
- * Test special header and status code rendering.
+ * Test attached HTML head rendering.
*
* @return array
- * A render array using features of the 'http_header' directive.
+ * A render array using the 'html_head' directive.
*/
- public function teapot() {
+ public function head() {
$render = [];
$render['#attached']['http_header'][] = ['X-Test-Teapot-Replace', 'This value gets replaced'];
$render['#attached']['http_header'][] = ['X-Test-Teapot-Replace', 'Teapot replaced', TRUE];
$render['#attached']['http_header'][] = ['X-Test-Teapot-No-Replace', 'This value is not replaced'];
$render['#attached']['http_header'][] = ['X-Test-Teapot-No-Replace', 'This one is added', FALSE];
$render['#attached']['http_header'][] = ['X-Test-Teapot', 'Teapot Mode Active'];
- $render['#attached']['http_header'][] = ['Status', "418 I'm a teapot."];
+ return $render;
+ }
+
+ public function feed() {
+ $render = [];
+ $render['#attached']['feed'][] = ['test://url', 'Your RSS feed.'];
return $render;
}