diff --git a/core/lib/Drupal/Core/CoreBundle.php b/core/lib/Drupal/Core/CoreBundle.php index 63037b6..37a2623 100644 --- a/core/lib/Drupal/Core/CoreBundle.php +++ b/core/lib/Drupal/Core/CoreBundle.php @@ -257,6 +257,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..28015f9 --- /dev/null +++ b/core/lib/Drupal/Core/EventSubscriber/HtmlViewSubscriber.php @@ -0,0 +1,96 @@ + 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'); + // We only do this hack for a main request because a subrequest would be a + // block, and thus doesn't need page-level variables. + 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))); + } + + /** + * 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..d82f82c --- /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 PartialResponse. + * + * @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 html title. + */ + 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..641b0d9 --- /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 html.tpl.php.', + 'group' => 'Common', // @todo put me somewhere better + ); + } + + /** + * Tests that a PartialResponse object is rendered correctly by issuing an + * HTTP 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('#.*#s', 'There was no double-page effect from a misrendered subrequest.'); + } + + /** + * Tests that a PartialResponse object is rendered correctly by mocking the + * environment. + */ + public function testPartialResponseByUnit() { + $kernel = $this->container->get('http_kernel'); + $request = Request::create('/foo'); + $response = new PartialResponse('hello world'); + $subscriber = new HtmlViewSubscriber($this->container->get('module_handler')); + $event = new GetResponseForControllerResultEvent($kernel, $request, HttpKernel::MASTER_REQUEST, $response); + $subscriber->onPartialHtmlResponse($event); + $this->assertTrue(preg_match('/hello world/', $event->getResponse()->getContent()), 'Expected raw output found within HTML response.'); + } +} diff --git a/core/modules/system/tests/modules/html_test/html_test.info b/core/modules/system/tests/modules/html_test/html_test.info new file mode 100644 index 0000000..6408ea4 --- /dev/null +++ b/core/modules/system/tests/modules/html_test/html_test.info @@ -0,0 +1,6 @@ +name = "HTML test" +description = "Support module for testing finalized HTML rendering." +package = Testing +version = VERSION +core = 8.x +hidden = TRUE diff --git a/core/modules/system/tests/modules/html_test/html_test.module b/core/modules/system/tests/modules/html_test/html_test.module new file mode 100644 index 0000000..ea3dfb5 --- /dev/null +++ b/core/modules/system/tests/modules/html_test/html_test.module @@ -0,0 +1,4 @@ +hello world

'); + } +}