Commit 7652f683 authored by catch's avatar catch

Issue #1849822 by dawehner: Convert (HTML) view rendering to a render array.

parent 71acfa69
......@@ -27,7 +27,8 @@
* title = @Translation("REST export"),
* help = @Translation("Create a REST export resource."),
* uses_route = TRUE,
* admin = @Translation("REST export")
* admin = @Translation("REST export"),
* returns_response = TRUE
* )
*/
class RestExport extends PathPluginBase {
......
......@@ -170,6 +170,9 @@ public function testShortcutLinkChangeRoute() {
$this->assertResponse(200);
// Disable the view.
entity_load('view', 'content')->disable()->save();
/** @var \Drupal\Core\Routing\RouteBuilderInterface $router_builder */
$router_builder = \Drupal::service('router.builder');
$router_builder->rebuildIfNeeded();
$this->drupalGet('admin/content');
$this->assertResponse(200);
}
......
......@@ -77,7 +77,7 @@ function testWhosOnlineBlock() {
$block = $this->drupalPlaceBlock('views_block:who_s_online-who_s_online_block');
// Generate users.
$user1 = $this->drupalCreateUser(array());
$user1 = $this->drupalCreateUser(array('access user profiles'));
$user2 = $this->drupalCreateUser(array());
$user3 = $this->drupalCreateUser(array());
......@@ -92,6 +92,7 @@ function testWhosOnlineBlock() {
$this->updateAccess($this->adminUser->id(), $inactive_time);
// Test block output.
\Drupal::currentUser()->setAccount($user1);
$content = entity_view($block, 'block');
$this->drupalSetContent(render($content));
$this->assertRaw(t('2 users'), 'Correct number of online users (2 users).');
......
......@@ -131,4 +131,11 @@ class ViewsDisplay extends ViewsPluginAnnotationBase {
*/
public $no_ui;
/**
* Whether the display returns a response object.
*
* @var bool
*/
public $returns_response;
}
......@@ -30,6 +30,7 @@ public function getInfo() {
'#name' => NULL,
'#display_id' => 'default',
'#arguments' => array(),
'#embed' => FALSE,
);
}
......@@ -39,9 +40,29 @@ public function getInfo() {
public static function preRenderViewElement($element) {
$element['#attributes']['class'][] = 'views-element-container';
$view = Views::getView($element['#name']);
if (!isset($element['#view'])) {
$view = Views::getView($element['#name']);
}
else {
$view = $element['#view'];
}
if ($view && $view->access($element['#display_id'])) {
$element['view'] = $view->preview($element['#display_id'], $element['#arguments']);
if (!empty($element['embed'])) {
$element += $view->preview($element['#display_id'], $element['#arguments']);
}
else {
// Add contextual links to the view. We need to attach them to the dummy
// $view_array variable, since contextual_preprocess() requires that they
// be attached to an array (not an object) in order to process them. For
// our purposes, it doesn't matter what we attach them to, since once they
// are processed by contextual_preprocess() they will appear in the
// $title_suffix variable (which we will then render in
// views-view.html.twig).
$view->setDisplay($element['#display_id']);
$element += $view->executeDisplay($element['#display_id'], $element['#arguments']);
views_add_contextual_links($element, 'view', $view, $view->current_display);
}
}
return $element;
......
......@@ -345,6 +345,13 @@ public function postSave(EntityStorageInterface $storage, $update = TRUE) {
// @todo Remove if views implements a view_builder controller.
views_invalidate_cache();
// Rebuild the router case the view got enabled.
if (!isset($this->original) || ($this->status() != $this->original->status())) {
/** @var \Drupal\Core\Routing\RouteBuilderInterface $router_builder */
$router_builder = \Drupal::service('router.builder');
$router_builder->setRebuildNeeded();
}
}
/**
......
......@@ -29,7 +29,7 @@ class ViewsBlock extends ViewsBlockBase {
public function build() {
$this->view->display_handler->preBlockBuild($this);
if ($output = $this->view->executeDisplay($this->displayID)) {
if ($output = $this->view->buildRenderable($this->displayID)) {
// Override the label to the dynamic title configured in the view.
if (empty($this->configuration['views_label']) && $this->view->getTitle()) {
$output['#title'] = Xss::filterAdmin($this->view->getTitle());
......
......@@ -254,7 +254,7 @@ public function attachTo(ViewExecutable $view, $display_id, array &$build) {
$view->display_handler->setOption('pager', $this->view->displayHandlers->get($display_id)->getOption('pager'));
}
$attachment = $view->executeDisplay($this->display['id'], $args);
$attachment = $view->buildRenderable($this->display['id'], $args);
switch ($this->getOption('attachment_position')) {
case 'before':
......
......@@ -2278,8 +2278,6 @@ public function preExecute() {
foreach ($this->extender as $extender) {
$extender->preExecute();
}
$this->view->setShowAdminLinks($this->getOption('show_admin_links'));
}
/**
......@@ -2334,6 +2332,27 @@ public function calculateCacheMetadata () {
*/
public function execute() { }
/**
* Builds a renderable array of the view.
*
* Note: This does not yet contain the executed view, but just the loaded view
* executable.
*
* @return array
* The render array of a view.
*/
public function buildRenderable(array $args = []) {
return [
'#type' => 'view',
'#name' => $this->view->storage->id(),
'#display_id' => $this->display['id'],
'#arguments' => $args,
'#embed' => FALSE,
'#pre_render' => [['\Drupal\views\Element\View', 'preRenderViewElement'], [$this, 'elementPreRender']],
'#view' => $this->view,
];
}
/**
* Fully render the display for the purposes of a live preview or
* some other AJAXy reason.
......
......@@ -24,7 +24,8 @@
* title = @Translation("Feed"),
* help = @Translation("Display the view as a feed, such as an RSS feed."),
* uses_route = TRUE,
* admin = @Translation("Feed")
* admin = @Translation("Feed"),
* returns_response = TRUE
* )
*/
class Feed extends PathPluginBase {
......
......@@ -104,7 +104,13 @@ public function handle($view_id, $display_id, Request $request, RouteMatchInterf
}
}
return $view->executeDisplay($display_id, $args);
$plugin_definition = $view->display_handler->getPluginDefinition();
if (!empty($plugin_definition['returns_response'])) {
return $view->executeDisplay($display_id, $args);
}
else {
return $view->buildRenderable($display_id, $args);
}
}
}
......@@ -1396,6 +1396,33 @@ public function render($display_id = NULL) {
return $this->display_handler->output;
}
/**
* Builds the render array outline for the given display.
*
* This render array has a #pre_render callback which will call
* ::executeDisplay in order to actually execute the view and then build the
* final render array structure.
*
* @param string $display_id
* The display ID.
* @param array $args
* An array of arguments passed along to the view.
*
* @return array|null
* A renderable array with #type 'view' or NULL if the display ID was
* invalid.
*/
public function buildRenderable($display_id = NULL, $args = array()) {
// @todo Extract that into a generic method.
if (empty($this->current_display) || $this->current_display != $this->chooseDisplay($display_id)) {
if (!$this->setDisplay($display_id)) {
return NULL;
}
}
return $this->display_handler->buildRenderable($args);
}
/**
* Execute the given display, with the given arguments.
* To be called externally by whatever mechanism invokes the view,
......@@ -2083,6 +2110,9 @@ public function setShowAdminLinks($show_admin_links) {
* Returns TRUE if admin links should be rendered, else FALSE.
*/
public function getShowAdminLinks() {
if (!isset($this->showAdminLinks)) {
return $this->getDisplay()->getOption('show_admin_links');
}
return $this->showAdminLinks;
}
......
......@@ -72,7 +72,7 @@ protected function setUp() {
$this->executable = $this->getMockBuilder('Drupal\views\ViewExecutable')
->disableOriginalConstructor()
->setMethods(array('executeDisplay', 'setDisplay', 'setItemsPerPage'))
->setMethods(['buildRenderable', 'setDisplay', 'setItemsPerPage'])
->getMock();
$this->executable->expects($this->any())
->method('setDisplay')
......@@ -116,9 +116,9 @@ public function testBuild() {
$output = $this->randomMachineName(100);
$build = array('#markup' => $output);
$this->executable->expects($this->once())
->method('executeDisplay')
->with($this->equalTo('block_1'))
->will($this->returnValue($build));
->method('buildRenderable')
->with('block_1', [])
->willReturn($build);
$block_id = 'views_block:test_view-block_1';
$config = array();
......@@ -142,9 +142,9 @@ public function testBuild() {
public function testBuildFailed() {
$output = FALSE;
$this->executable->expects($this->once())
->method('executeDisplay')
->with($this->equalTo('block_1'))
->will($this->returnValue($output));
->method('buildRenderable')
->with('block_1', [])
->willReturn($output);
$block_id = 'views_block:test_view-block_1';
$config = array();
......
......@@ -73,10 +73,24 @@ public function testPageController() {
->with('default');
$executable->expects($this->once())
->method('initHandlers');
$views_display = $this->getMockBuilder('Drupal\views\Plugin\views\display\DisplayPluginBase')
->disableOriginalConstructor()
->getMock();
$views_display->expects($this->any())
->method('getDefinition')
->willReturn([]);
$executable->display_handler = $views_display;
$build = [
'#type' => 'view',
'#name' => 'test_page_view',
'#display_id' => 'default'
];
$executable->expects($this->once())
->method('executeDisplay')
->with('default', array())
->will($this->returnValue(array('#markup' => 'example output')));
->method('buildRenderable')
->with('default', [])
->will($this->returnValue($build));
$this->executableFactory->expects($this->any())
->method('get')
......@@ -91,7 +105,7 @@ public function testPageController() {
$output = $this->pageController->handle($route_match->getParameter('view_id'), $route_match->getParameter('display_id'), $request, $route_match);
$this->assertInternalType('array', $output);
$this->assertEquals(array('#markup' => 'example output'), $output);
$this->assertEquals($build, $output);
}
/**
......@@ -114,6 +128,14 @@ public function testHandleWithArgumentsWithoutOverridden() {
$executable->expects($this->once())
->method('initHandlers');
$views_display = $this->getMockBuilder('Drupal\views\Plugin\views\display\DisplayPluginBase')
->disableOriginalConstructor()
->getMock();
$views_display->expects($this->any())
->method('getDefinition')
->willReturn([]);
$executable->display_handler = $views_display;
// Manually setup a argument handler.
$argument = $this->getMockBuilder('Drupal\views\Plugin\views\argument\ArgumentPluginBase')
->disableOriginalConstructor()
......@@ -121,7 +143,7 @@ public function testHandleWithArgumentsWithoutOverridden() {
$executable->argument['test_id'] = $argument;
$executable->expects($this->once())
->method('executeDisplay')
->method('buildRenderable')
->with('page_1', array('test-argument'));
$this->executableFactory->expects($this->any())
......@@ -162,6 +184,14 @@ public function testHandleWithArgumentsOnOveriddenRoute() {
$executable->expects($this->once())
->method('initHandlers');
$views_display = $this->getMockBuilder('Drupal\views\Plugin\views\display\DisplayPluginBase')
->disableOriginalConstructor()
->getMock();
$views_display->expects($this->any())
->method('getDefinition')
->willReturn([]);
$executable->display_handler = $views_display;
// Manually setup a argument handler.
$argument = $this->getMockBuilder('Drupal\views\Plugin\views\argument\ArgumentPluginBase')
->disableOriginalConstructor()
......@@ -169,7 +199,7 @@ public function testHandleWithArgumentsOnOveriddenRoute() {
$executable->argument['test_id'] = $argument;
$executable->expects($this->once())
->method('executeDisplay')
->method('buildRenderable')
->with('page_1', array('test-argument'));
$this->executableFactory->expects($this->any())
......@@ -213,6 +243,14 @@ public function testHandleWithArgumentsOnOveriddenRouteWithUpcasting() {
$executable->expects($this->once())
->method('initHandlers');
$views_display = $this->getMockBuilder('Drupal\views\Plugin\views\display\DisplayPluginBase')
->disableOriginalConstructor()
->getMock();
$views_display->expects($this->any())
->method('getDefinition')
->willReturn([]);
$executable->display_handler = $views_display;
// Manually setup a argument handler.
$argument = $this->getMockBuilder('Drupal\views\Plugin\views\argument\ArgumentPluginBase')
->disableOriginalConstructor()
......@@ -220,7 +258,7 @@ public function testHandleWithArgumentsOnOveriddenRouteWithUpcasting() {
$executable->argument['test_id'] = $argument;
$executable->expects($this->once())
->method('executeDisplay')
->method('buildRenderable')
->with('page_1', array('example_id'));
$this->executableFactory->expects($this->any())
......
......@@ -42,14 +42,6 @@ function template_preprocess_views_view(&$variables) {
$variables['attributes']['class'][] = $variables['css_class'];
}
// Add contextual links to the view. We need to attach them to the dummy
// $view_array variable, since contextual_preprocess() requires that they be
// attached to an array (not an object) in order to process them. For our
// purposes, it doesn't matter what we attach them to, since once they are
// processed by contextual_preprocess() they will appear in the $title_suffix
// variable (which we will then render in views-view.html.twig).
views_add_contextual_links($variables['view_array'], 'view', $view, $view->current_display);
// Attachments are always updated with the outer view, never by themselves,
// so they do not have dom ids.
if (empty($view->is_attachment)) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment