From 36fb3e9e79233dc8c2863ea4434bb48cbd82a856 Mon Sep 17 00:00:00 2001
From: Will Jones
Date: Wed, 27 Feb 2013 07:42:14 +0000
Subject: [PATCH 1/7] Define an AssetInterface based on that of Assetic.
---
core/lib/Drupal/Core/Asset/AssetInterface.php | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
create mode 100644 core/lib/Drupal/Core/Asset/AssetInterface.php
diff --git a/core/lib/Drupal/Core/Asset/AssetInterface.php b/core/lib/Drupal/Core/Asset/AssetInterface.php
new file mode 100644
index 0000000..1bd3b26
--- /dev/null
+++ b/core/lib/Drupal/Core/Asset/AssetInterface.php
@@ -0,0 +1,21 @@
+
Date: Wed, 27 Feb 2013 07:42:54 +0000
Subject: [PATCH 2/7] Build a specialised AssetGraph for producing TSLs.
---
core/lib/Drupal/Core/Asset/AssetGraph.php | 98 +++++++++++++++++++++++++++++++
1 file changed, 98 insertions(+)
create mode 100644 core/lib/Drupal/Core/Asset/AssetGraph.php
diff --git a/core/lib/Drupal/Core/Asset/AssetGraph.php b/core/lib/Drupal/Core/Asset/AssetGraph.php
new file mode 100644
index 0000000..69d3764
--- /dev/null
+++ b/core/lib/Drupal/Core/Asset/AssetGraph.php
@@ -0,0 +1,98 @@
+vertices = array();
+ $this->edges = array();
+
+ $this->incoming = array();
+
+ $this->sequencing = array();
+ }
+
+ public function add(AssetInterface $asset) {
+ $assetId = $asset->getId();
+ if (!isset($this->vertices[$assetId])) {
+ $this->vertices[$assetId] = $asset;
+ $this->incoming[$assetId] = 0;
+
+ if (isset($this->sequencing[$assetId])) {
+ foreach ($this->sequencing[$assetId] as $successorId) {
+ $this->edges[$assetId][$successorId] = $successorId;
+ $this->incoming[$successorId]++;
+ }
+ }
+
+ if ($asset->hasPredecessors()) {
+ foreach ($asset->getPredecessors() as $predecessor) {
+ $predecessorId = $predecessor->getId();
+
+ $this->sequencing[$predecessorId][$assetId] = $assetId;
+ }
+ }
+
+ if ($asset->hasDependencies()) {
+ foreach ($asset->getDependencies() as $dependency) {
+ $dependencyId = $dependency->getId();
+
+ $this->add($dependency);
+ $this->edges[$dependencyId][$assetId] = $assetId;
+ $this->incoming[$assetId]++;
+ }
+ }
+ }
+
+ return $this;
+ }
+
+ public function getSources() {
+ $sources = array();
+ foreach ($this->vertices as $id => $asset) {
+ if ($this->incoming[$id] == 0) {
+ $sources[] = $id;
+ }
+ }
+
+ return $sources;
+ }
+
+ public function getTSL() {
+ $ids = array();
+ $edges = $this->edges;
+ $incoming = $this->incoming;
+
+ $queue = $this->getSources();
+ while ($id = array_shift($queue)) {
+ $ids[] = $id;
+
+ if (isset($edges[$id])) {
+ foreach ($edges[$id] as $to) {
+ unset($edges[$id][$to]);
+ $incoming[$to]--;
+
+ if ($incoming[$to] == 0) {
+ $queue[] = $to;
+ }
+ }
+ }
+ }
+
+ return $ids;
+ }
+}
--
1.8.1.4
From d1e7faad66c43c9a8b76ae50c752371c0612b501 Mon Sep 17 00:00:00 2001
From: Will Jones
Date: Wed, 27 Feb 2013 07:43:12 +0000
Subject: [PATCH 3/7] Add BaseAsset, FileAsset and AssetBag classes.
---
core/lib/Drupal/Core/Asset/AssetBag.php | 23 ++++++++++++
core/lib/Drupal/Core/Asset/BaseAsset.php | 60 ++++++++++++++++++++++++++++++++
core/lib/Drupal/Core/Asset/FileAsset.php | 44 +++++++++++++++++++++++
3 files changed, 127 insertions(+)
create mode 100644 core/lib/Drupal/Core/Asset/AssetBag.php
create mode 100644 core/lib/Drupal/Core/Asset/BaseAsset.php
create mode 100644 core/lib/Drupal/Core/Asset/FileAsset.php
diff --git a/core/lib/Drupal/Core/Asset/AssetBag.php b/core/lib/Drupal/Core/Asset/AssetBag.php
new file mode 100644
index 0000000..efd1c2d
--- /dev/null
+++ b/core/lib/Drupal/Core/Asset/AssetBag.php
@@ -0,0 +1,23 @@
+assetGraph = new AssetGraph();
+ }
+
+ public function add(AssetInterface $asset) {
+ $this->assetGraph->add($asset);
+
+ return $this;
+ }
+
+ public function dump() {
+ return $this->assetGraph->getTSL();
+ }
+}
diff --git a/core/lib/Drupal/Core/Asset/BaseAsset.php b/core/lib/Drupal/Core/Asset/BaseAsset.php
new file mode 100644
index 0000000..9ba152e
--- /dev/null
+++ b/core/lib/Drupal/Core/Asset/BaseAsset.php
@@ -0,0 +1,60 @@
+id = $id;
+
+ $this->predecessors = array();
+ $this->hasPredecessors = FALSE;
+
+ $this->dependencies = array();
+ $this->hasDependencies = FALSE;
+ }
+
+ public function getId() {
+ return $this->id;
+ }
+
+ public function after($asset) {
+ $this->predecessors[] = $asset;
+ $this->hasPredecessors = TRUE;
+ }
+
+ public function hasPredecessors() {
+ return $this->hasPredecessors;
+ }
+
+ public function getPredecessors() {
+ return $this->predecessors;
+ }
+
+ public function dependsOn($dependency) {
+ $this->dependencies[] = $dependency;
+ $this->hasDependencies = TRUE;
+ }
+
+ public function hasDependencies() {
+ return $this->hasDependencies;
+ }
+
+ public function getDependencies() {
+ return $this->dependencies;
+ }
+}
diff --git a/core/lib/Drupal/Core/Asset/FileAsset.php b/core/lib/Drupal/Core/Asset/FileAsset.php
new file mode 100644
index 0000000..a4583fc
--- /dev/null
+++ b/core/lib/Drupal/Core/Asset/FileAsset.php
@@ -0,0 +1,44 @@
+source = $source;
+ }
+
+ public function load(FilterInterface $additionFilter = NULL) {
+
+ }
+
+ public function getLastModified() {
+
+ }
+}
--
1.8.1.4
From a4d10c790c38f7ded9c313dd9dcb4ab6825d7e85 Mon Sep 17 00:00:00 2001
From: Will Jones
Date: Wed, 27 Feb 2013 07:43:25 +0000
Subject: [PATCH 4/7] Add some simple tests for Assets.
---
.../lib/Drupal/system/Tests/Asset/AssetsTest.php | 93 ++++++++++++++++++++++
1 file changed, 93 insertions(+)
create mode 100644 core/modules/system/lib/Drupal/system/Tests/Asset/AssetsTest.php
diff --git a/core/modules/system/lib/Drupal/system/Tests/Asset/AssetsTest.php b/core/modules/system/lib/Drupal/system/Tests/Asset/AssetsTest.php
new file mode 100644
index 0000000..591f0de
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Asset/AssetsTest.php
@@ -0,0 +1,93 @@
+ 'Asset tests',
+ 'description' => 'Asset tests.',
+ 'group' => 'Asset',
+ );
+ }
+
+ public function assertArraysEqual($type, $expected, $actual) {
+ $this->assertEqual($expected, $actual,
+ t(
+ 'Expected @type:
@expected
' .
+ 'Actual @type:
@actual
',
+
+ array(
+ '@type' => $type,
+ '@expected' => print_r($expected, TRUE),
+ '@actual' => print_r($actual, TRUE),
+ )
+ ));
+ }
+
+ public function testSimpleDependencies() {
+ $jquery = new FileAsset('core/misc/jquery.js');
+ $ajax = new FileAsset('core/misc/ajax.js');
+ $dropdowns = new FileAsset('core/misc/dropdowns.js');
+
+ $ajax->dependsOn($jquery);
+ $dropdowns->dependsOn($jquery);
+
+ $bag = new AssetBag();
+ $bag->add($dropdowns)->add($jquery)->add($ajax);
+
+ $actual = $bag->dump();
+ $expected = array(
+ 'core/misc/jquery.js',
+ 'core/misc/dropdowns.js',
+ 'core/misc/ajax.js',
+ );
+
+ $this->assertArraysEqual('TSL', $expected, $actual);
+ }
+
+ public function testSequencing() {
+ $before = new FileAsset('core/misc/jquery.before.js');
+ $jquery = new FileAsset('core/misc/jquery.js');
+ $ajax = new FileAsset('core/misc/ajax.js');
+ $dropdowns = new FileAsset('core/misc/dropdowns.js');
+
+ $jquery->after($before);
+ $ajax->dependsOn($jquery);
+ $dropdowns->dependsOn($jquery);
+
+ $bag = new AssetBag;
+ $bag->add($dropdowns)->add($jquery)->add($ajax);
+
+ $actual = $bag->dump();
+ $expected = array(
+ 'core/misc/jquery.js',
+ 'core/misc/dropdowns.js',
+ 'core/misc/ajax.js',
+ );
+
+ $this->assertArraysEqual('TSL', $expected, $actual);
+
+ $bag->add($before);
+
+ $actual = $bag->dump();
+ $expected = array(
+ 'core/misc/jquery.before.js',
+ 'core/misc/jquery.js',
+ 'core/misc/dropdowns.js',
+ 'core/misc/ajax.js',
+ );
+
+ $this->assertArraysEqual('TSL', $expected, $actual);
+ }
+}
--
1.8.1.4
From 66d8202dededf2c657e89e957f22168f489fc482 Mon Sep 17 00:00:00 2001
From: Will Jones
Date: Wed, 27 Feb 2013 07:55:22 +0000
Subject: [PATCH 5/7] Autoload all Assetic classes properly.
---
core/vendor/composer/autoload_namespaces.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/core/vendor/composer/autoload_namespaces.php b/core/vendor/composer/autoload_namespaces.php
index 54b2551..3bff62e 100644
--- a/core/vendor/composer/autoload_namespaces.php
+++ b/core/vendor/composer/autoload_namespaces.php
@@ -26,5 +26,5 @@
'Guzzle\\Common' => $vendorDir . '/guzzle/common/',
'EasyRdf_' => $vendorDir . '/easyrdf/easyrdf/lib/',
'Doctrine\\Common' => $vendorDir . '/doctrine/common/lib/',
- 'Assetic' => $vendorDir . '/kriswallsmith/assetic/src/',
+ 'Assetic\\' => $vendorDir . '/kriswallsmith/assetic/src/',
);
--
1.8.1.4
From bde1fa2cd51d938f60c84ee80b7006349d4202ca Mon Sep 17 00:00:00 2001
From: Will Jones
Date: Wed, 27 Feb 2013 07:57:16 +0000
Subject: [PATCH 6/7] Apply the PartialResponse patch from #1871596.
---
core/lib/Drupal/Core/CoreBundle.php | 3 +
.../Core/EventSubscriber/HtmlViewSubscriber.php | 98 ++++++++++++++++++++++
.../Drupal/Core/EventSubscriber/ViewSubscriber.php | 2 +-
core/lib/Drupal/Core/PartialResponse.php | 92 ++++++++++++++++++++
.../system/Tests/Common/PartialResponseTest.php | 56 +++++++++++++
.../system/tests/modules/html_test/html_test.info | 6 ++
.../tests/modules/html_test/html_test.module | 4 +
.../tests/modules/html_test/html_test.routing.yml | 6 ++
.../lib/Drupal/html_test/TestController.php | 18 ++++
9 files changed, 284 insertions(+), 1 deletion(-)
create mode 100644 core/lib/Drupal/Core/EventSubscriber/HtmlViewSubscriber.php
create mode 100644 core/lib/Drupal/Core/PartialResponse.php
create mode 100644 core/modules/system/lib/Drupal/system/Tests/Common/PartialResponseTest.php
create mode 100644 core/modules/system/tests/modules/html_test/html_test.info
create mode 100644 core/modules/system/tests/modules/html_test/html_test.module
create mode 100644 core/modules/system/tests/modules/html_test/html_test.routing.yml
create mode 100644 core/modules/system/tests/modules/html_test/lib/Drupal/html_test/TestController.php
diff --git a/core/lib/Drupal/Core/CoreBundle.php b/core/lib/Drupal/Core/CoreBundle.php
index cc4e4aa..2f7b3a6 100644
--- a/core/lib/Drupal/Core/CoreBundle.php
+++ b/core/lib/Drupal/Core/CoreBundle.php
@@ -232,6 +232,9 @@ public function build(ContainerBuilder $container) {
->addArgument(new Reference('router'))
->addTag('event_subscriber');
$container->register('content_negotiation', 'Drupal\Core\ContentNegotiation');
+ $container->register('html_view_subscriber', 'Drupal\Core\EventSubscriber\HtmlViewSubscriber')
+ ->addArgument(new Reference('module_handler'))
+ ->addTag('event_subscriber');
$container->register('view_subscriber', 'Drupal\Core\EventSubscriber\ViewSubscriber')
->addArgument(new Reference('content_negotiation'))
->addTag('event_subscriber');
diff --git a/core/lib/Drupal/Core/EventSubscriber/HtmlViewSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/HtmlViewSubscriber.php
new file mode 100644
index 0000000..c777a33
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/HtmlViewSubscriber.php
@@ -0,0 +1,98 @@
+ tag
+ * and its contents.
+ *
+ */
+class HtmlViewSubscriber implements EventSubscriberInterface {
+
+ /**
+ * @var \Drupal\Core\Extension\ModuleHandlerInterface
+ */
+ protected $moduleHandler;
+
+ public function __construct(ModuleHandlerInterface $moduleHandler) {
+ $this->moduleHandler = $moduleHandler;
+ }
+
+ /**
+ * Processes a specialized Drupal Response into fully-formed HTML.
+ *
+ * This subscriber only acts if presented with a specialized Response object
+ * that contains a partial HTML page, plus the metadata necessary to construct
+ * the remainder of the page.
+ *
+ * @todo we're ignoring content negotiation and assuming HTML. could be JSON, at least...
+ *
+ * @param \Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent $event
+ * The Event to process.
+ *
+ * @return \Symfony\Component\HttpFoundation\Response;
+ */
+ public function onPartialHtmlResponse(GetResponseForControllerResultEvent $event) {
+ // Only act if we have a specialized Drupal response to work with.
+ if (!($response = $event->getControllerResult()) instanceof PartialResponse) {
+ return;
+ }
+
+ // @todo this is really lazy, but the easiest way to get $page_{top,bottom}. this can be removed once the responsibility is shifted into displays/controllers
+ $page = element_info('page');
+
+ if ($event->getRequestType() === HttpKernelInterface::MASTER_REQUEST) {
+ foreach ($this->moduleHandler->getImplementations('page_build') as $module) {
+ $function = $module . '_page_build';
+ $function($page);
+ }
+ $this->moduleHandler->alter('page', $page);
+ }
+
+ // @todo these are horrible passthrough hacks; remove them incrementally as we implement the respective pieces elsewhere.
+ $vars = array(
+ 'page' => array(
+ '#children' => $response->getContent(),
+ 'page_top' => empty($page['page_top']) ? array(): $page['page_top'],
+ 'page_bottom' => empty($page['page_bottom']) ? array(): $page['page_bottom'],
+ ),
+ 'response' => $response,
+ );
+
+ $event->setResponse(new Response(theme('html', $vars)));
+ // Stop propagation, as the ViewSubscriber will handle this poorly.
+ $event->stopPropagation();
+ }
+
+ /**
+ * Registers the methods in this class that should be listeners.
+ *
+ * @return array
+ * An array of event listener definitions.
+ */
+ static function getSubscribedEvents() {
+ $events[KernelEvents::VIEW][] = array('onPartialHtmlResponse', 40);
+
+ return $events;
+ }
+}
diff --git a/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php
index ac48bc8..ccdb595 100644
--- a/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php
@@ -152,7 +152,7 @@ public function onHtml(GetResponseForControllerResultEvent $event) {
* An array of event listener definitions.
*/
static function getSubscribedEvents() {
- $events[KernelEvents::VIEW][] = array('onView');
+ $events[KernelEvents::VIEW][] = array('onView', 30);
return $events;
}
diff --git a/core/lib/Drupal/Core/PartialResponse.php b/core/lib/Drupal/Core/PartialResponse.php
new file mode 100644
index 0000000..8976e64
--- /dev/null
+++ b/core/lib/Drupal/Core/PartialResponse.php
@@ -0,0 +1,92 @@
+content = $content;
+ }
+
+ /**
+ * Sets the response content.
+ *
+ * This should be the bulk of the page content, and will ultimately be placed
+ * within the tag in final HTML output.
+ *
+ * Valid types are strings, numbers, and objects that implement a __toString()
+ * method.
+ *
+ * @param mixed $content
+ *
+ * @return PartialResponse
+ */
+ public function setContent($content) {
+ $this->content = $content;
+ return $this;
+ }
+
+ /**
+ * Gets the main content of this Response.
+ *
+ * @return string
+ */
+ public function getContent() {
+ return $this->content;
+ }
+
+ /**
+ * Sets the title of this PartialResponse.
+ *
+ * Handling of this title varies depending on what is consuming this
+ * PartialResponse object. If it's a block, it may only be used as the block's
+ * title; if it's at the page level, it will be used in a number of places,
+ * including the title in HTML head.
+ */
+ public function setTitle($title, $output = CHECK_PLAIN) {
+ $this->title = ($output == PASS_THROUGH) ? $title : check_plain($title);
+ }
+
+ /**
+ * Indicates whether or not this PartialResponse has a title.
+ *
+ * @return bool
+ */
+ public function hasTitle() {
+ return !empty($this->title);
+ }
+
+ /**
+ * Gets the title for this PartialResponse, if any.
+ *
+ * @return string
+ */
+ public function getTitle() {
+ return $this->title;
+ }
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/PartialResponseTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/PartialResponseTest.php
new file mode 100644
index 0000000..caa73c2
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Common/PartialResponseTest.php
@@ -0,0 +1,56 @@
+ 'Partial HTML Response',
+ 'description' => 'Tests that PartialResponse data is properly rendered into the html template.',
+ 'group' => 'Common', // @todo put me somewhere better
+ );
+ }
+
+ /**
+ * Tests that a PartialResponse object is rendered correctly by issuing a web
+ * request against it.
+ */
+ public function testPartialResponseByWeb() {
+ $this->drupalGet('html_test');
+
+ // Check that our basic body text is there
+ $this->assertRaw('hello world');
+ // Check that we didn't get a page within a page, inception-style.
+ $this->assertNoPattern('#.*