From 7ea1cd958e2134ab3297906b7aa660ad2eccaf1f Mon Sep 17 00:00:00 2001 From: Alex Pott Date: Thu, 11 Sep 2014 17:08:26 +0100 Subject: [PATCH] Issue #2221433 by damiankloip, dawehner: Clean up views rendering. Move stuff from template_preprocess_views_view(), into a #pre_render callback. --- .../Plugin/views/field/ContextualLinks.php | 2 +- .../Plugin/views/style/EntityReference.php | 3 - .../field/src/Plugin/views/field/Field.php | 13 ++- .../src/Plugin/views/field/BulkForm.php | 2 +- .../src/Tests/Views/HandlerFieldRoleTest.php | 1 - .../Tests/Views/HandlerFieldUserNameTest.php | 2 - core/modules/views/src/Form/ViewsForm.php | 11 +-- .../views/src/Form/ViewsFormMainForm.php | 12 +-- .../Plugin/views/cache/CachePluginBase.php | 23 +++-- .../src/Plugin/views/display/Attachment.php | 4 +- .../views/display/DisplayPluginBase.php | 87 ++++++++++++++++- .../views/src/Plugin/views/display/Feed.php | 10 +- .../views/src/Plugin/views/query/Sql.php | 6 ++ .../views/src/Plugin/views/style/Opml.php | 10 +- .../views/src/Plugin/views/style/Rss.php | 18 ++-- .../Plugin/views/style/StylePluginBase.php | 4 +- core/modules/views/src/ResultRow.php | 7 ++ .../views/src/Tests/Plugin/StyleTest.php | 6 +- core/modules/views/src/ViewExecutable.php | 3 +- .../src/Unit/Plugin/field/CounterTest.php | 4 +- core/modules/views/views.module | 19 +++- core/modules/views/views.theme.inc | 95 +++---------------- core/modules/views_ui/src/ViewUI.php | 1 - core/modules/views_ui/views_ui.module | 7 ++ 24 files changed, 198 insertions(+), 152 deletions(-) diff --git a/core/modules/contextual/src/Plugin/views/field/ContextualLinks.php b/core/modules/contextual/src/Plugin/views/field/ContextualLinks.php index dd8c990a4b..e7e52381d9 100644 --- a/core/modules/contextual/src/Plugin/views/field/ContextualLinks.php +++ b/core/modules/contextual/src/Plugin/views/field/ContextualLinks.php @@ -86,7 +86,7 @@ public function preRender(&$values) { public function render(ResultRow $values) { $links = array(); foreach ($this->options['fields'] as $field) { - $rendered_field = $this->view->style_plugin->getField($this->view->row_index, $field); + $rendered_field = $this->view->style_plugin->getField($values->index, $field); if (empty($rendered_field)) { continue; } diff --git a/core/modules/entity_reference/src/Plugin/views/style/EntityReference.php b/core/modules/entity_reference/src/Plugin/views/style/EntityReference.php index d0b3bbe498..258e66eb24 100644 --- a/core/modules/entity_reference/src/Plugin/views/style/EntityReference.php +++ b/core/modules/entity_reference/src/Plugin/views/style/EntityReference.php @@ -88,17 +88,14 @@ public function render() { // @todo We don't display grouping info for now. Could be useful for select // widget, though. $results = array(); - $this->view->row_index = 0; foreach ($sets as $records) { foreach ($records as $values) { // Sanitize HTML, remove line breaks and extra whitespace. $output = $this->view->rowPlugin->render($values); $output = drupal_render($output); $results[$values->{$id_field_alias}] = Xss::filterAdmin(preg_replace('/\s\s+/', ' ', str_replace("\n", '', $output))); - $this->view->row_index++; } } - unset($this->view->row_index); return $results; } diff --git a/core/modules/field/src/Plugin/views/field/Field.php b/core/modules/field/src/Plugin/views/field/Field.php index 0bd93b23d7..8a8dc187a7 100644 --- a/core/modules/field/src/Plugin/views/field/Field.php +++ b/core/modules/field/src/Plugin/views/field/Field.php @@ -22,6 +22,7 @@ use Drupal\Core\Session\AccountInterface; use Drupal\views\Plugin\views\display\DisplayPluginBase; use Drupal\views\Plugin\views\field\FieldPluginBase; +use Drupal\views\ResultRow; use Drupal\views\ViewExecutable; use Drupal\views\Views; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -720,9 +721,15 @@ protected function renderItems($items) { } /** - * Return an array of items for the field. + * Gets an array of items for the field. + * + * @param \Drupal\views\ResultRow $values + * The result row object containing the values. + * + * @return array + * An array of items for the field. */ - public function getItems($values) { + public function getItems(ResultRow $values) { $original_entity = $this->getEntity($values); if (!$original_entity) { return array(); @@ -739,7 +746,7 @@ public function getItems($values) { // Pass the View object in the display so that fields can act on it. 'views_view' => $this->view, 'views_field' => $this, - 'views_row_id' => $this->view->row_index, + 'views_row_id' => $values->index, ); $render_array = $entity->get($this->definition['field_name'])->view($display); diff --git a/core/modules/system/src/Plugin/views/field/BulkForm.php b/core/modules/system/src/Plugin/views/field/BulkForm.php index bae235d57e..232a11c2fc 100644 --- a/core/modules/system/src/Plugin/views/field/BulkForm.php +++ b/core/modules/system/src/Plugin/views/field/BulkForm.php @@ -134,7 +134,7 @@ public function validateOptionsForm(&$form, FormStateInterface $form_state) { * {@inheritdoc} */ public function render(ResultRow $values) { - return ''; + return ''; } /** diff --git a/core/modules/user/src/Tests/Views/HandlerFieldRoleTest.php b/core/modules/user/src/Tests/Views/HandlerFieldRoleTest.php index 5af5a3edc3..6612ec42fa 100644 --- a/core/modules/user/src/Tests/Views/HandlerFieldRoleTest.php +++ b/core/modules/user/src/Tests/Views/HandlerFieldRoleTest.php @@ -43,7 +43,6 @@ public function testRole() { $view = Views::getView('test_views_handler_field_role'); $this->executeView($view); - $view->row_index = 0; // The role field is populated during preRender. $view->field['rid']->preRender($view->result); $render = $view->field['rid']->advancedRender($view->result[0]); diff --git a/core/modules/user/src/Tests/Views/HandlerFieldUserNameTest.php b/core/modules/user/src/Tests/Views/HandlerFieldUserNameTest.php index 573a579e38..752d87d289 100644 --- a/core/modules/user/src/Tests/Views/HandlerFieldUserNameTest.php +++ b/core/modules/user/src/Tests/Views/HandlerFieldUserNameTest.php @@ -30,8 +30,6 @@ public function testUserName() { $view = Views::getView('test_views_handler_field_user_name'); $this->executeView($view); - $view->row_index = 0; - $view->field['name']->options['link_to_user'] = TRUE; $username = $view->result[0]->users_field_data_name = $this->randomMachineName(); $view->result[0]->users_field_data_uid = 1; diff --git a/core/modules/views/src/Form/ViewsForm.php b/core/modules/views/src/Form/ViewsForm.php index 2273f9009b..78e52072fa 100644 --- a/core/modules/views/src/Form/ViewsForm.php +++ b/core/modules/views/src/Form/ViewsForm.php @@ -8,7 +8,6 @@ namespace Drupal\views\Form; use Drupal\Component\Utility\UrlHelper; -use Drupal\Core\Controller\ControllerResolverInterface; use Drupal\Core\DependencyInjection\ClassResolverInterface; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\DependencyInjection\DependencySerializationTrait; @@ -68,7 +67,7 @@ class ViewsForm implements FormInterface, ContainerInjectionInterface { /** * Constructs a ViewsForm object. * - * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $controller_resolver + * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver * The class resolver to get the subform form objects. * @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator * The url generator to generate the form action. @@ -79,8 +78,8 @@ class ViewsForm implements FormInterface, ContainerInjectionInterface { * @param string $view_display_id * The ID of the active view's display. */ - public function __construct(ClassResolverInterface $controller_resolver, UrlGeneratorInterface $url_generator, RequestStack $requestStack, $view_id, $view_display_id) { - $this->classResolver = $controller_resolver; + public function __construct(ClassResolverInterface $class_resolver, UrlGeneratorInterface $url_generator, RequestStack $requestStack, $view_id, $view_display_id) { + $this->classResolver = $class_resolver; $this->urlGenerator = $url_generator; $this->requestStack = $requestStack; $this->viewId = $view_id; @@ -92,7 +91,7 @@ public function __construct(ClassResolverInterface $controller_resolver, UrlGene */ public static function create(ContainerInterface $container, $view_id = NULL, $view_display_id = NULL) { return new static( - $container->get('controller_resolver'), + $container->get('class_resolver'), $container->get('url_generator'), $container->get('request_stack'), $view_id, @@ -116,7 +115,7 @@ public function getFormID() { /** * {@inheritdoc} */ - public function buildForm(array $form, FormStateInterface $form_state, ViewExecutable $view = NULL, $output = NULL) { + public function buildForm(array $form, FormStateInterface $form_state, ViewExecutable $view = NULL, $output = []) { $form_state['step'] = isset($form_state['step']) ? $form_state['step'] : 'views_form_views_form'; $form_state['step_controller']['views_form_views_form'] = 'Drupal\views\Form\ViewsFormMainForm'; diff --git a/core/modules/views/src/Form/ViewsFormMainForm.php b/core/modules/views/src/Form/ViewsFormMainForm.php index 6c48b7e38b..f65310c401 100644 --- a/core/modules/views/src/Form/ViewsFormMainForm.php +++ b/core/modules/views/src/Form/ViewsFormMainForm.php @@ -22,7 +22,7 @@ public function getFormID() { /** * {@inheritdoc} */ - public function buildForm(array $form, FormStateInterface $form_state, ViewExecutable $view = NULL, $output = NULL) { + public function buildForm(array $form, FormStateInterface $form_state, ViewExecutable $view = NULL, $output = []) { $form['#prefix'] = '
'; $form['#suffix'] = '
'; $form['#theme'] = 'form'; @@ -30,12 +30,10 @@ public function buildForm(array $form, FormStateInterface $form_state, ViewExecu // Add the output markup to the form array so that it's included when the form // array is passed to the theme function. - $form['output'] = array( - '#markup' => $output, - // This way any additional form elements will go before the view - // (below the exposed widgets). - '#weight' => 50, - ); + $form['output'] = $output; + // This way any additional form elements will go before the view + // (below the exposed widgets). + $form['output']['#weight'] = 50; $form['actions'] = array( '#type' => 'actions', diff --git a/core/modules/views/src/Plugin/views/cache/CachePluginBase.php b/core/modules/views/src/Plugin/views/cache/CachePluginBase.php index c8f9429aa8..d74728315c 100644 --- a/core/modules/views/src/Plugin/views/cache/CachePluginBase.php +++ b/core/modules/views/src/Plugin/views/cache/CachePluginBase.php @@ -142,8 +142,8 @@ public function cacheSet($type) { \Drupal::cache($this->resultsBin)->set($this->generateResultsKey(), $data, $this->cacheSetExpire($type), $this->getCacheTags()); break; case 'output': - $this->storage['output'] = $this->view->display_handler->output; - $this->gatherHeaders(); + $this->gatherHeaders($this->view->display_handler->output); + $this->storage['output'] = drupal_render($this->view->display_handler->output, TRUE); \Drupal::cache($this->outputBin)->set($this->generateOutputKey(), $this->storage, $this->cacheSetExpire($type), $this->getCacheTags()); break; } @@ -177,8 +177,13 @@ public function cacheGet($type) { if ($cache = \Drupal::cache($this->outputBin)->get($this->generateOutputKey())) { if (!$cutoff || $cache->created > $cutoff) { $this->storage = $cache->data; - $this->view->display_handler->output = $cache->data['output']; + $this->restoreHeaders(); + $this->view->display_handler->output = array( + '#attached' => &$this->view->element['#attached'], + '#markup' => $cache->data['output'], + ); + return TRUE; } } @@ -233,9 +238,12 @@ public function cacheStart() { } /** - * Gather the JS/CSS from the render array, the html head from the band data. + * Gather the JS/CSS from the render array and the html head from band data. + * + * @param array $render_array + * The view render array to collect data from. */ - protected function gatherHeaders() { + protected function gatherHeaders(array $render_array = []) { // Simple replacement for head if (isset($this->storage['head'])) { $this->storage['head'] = str_replace($this->storage['head'], '', drupal_add_html_head()); @@ -244,9 +252,8 @@ protected function gatherHeaders() { $this->storage['head'] = ''; } - $attached = $this->storage['output']['#attached']; - $this->storage['css'] = $attached['css']; - $this->storage['js'] = $attached['js']; + $this->storage['css'] = $render_array['#attached']['css']; + $this->storage['js'] = $render_array['#attached']['js']; } /** diff --git a/core/modules/views/src/Plugin/views/display/Attachment.php b/core/modules/views/src/Plugin/views/display/Attachment.php index 30870a0d8b..141a1fca98 100644 --- a/core/modules/views/src/Plugin/views/display/Attachment.php +++ b/core/modules/views/src/Plugin/views/display/Attachment.php @@ -232,9 +232,9 @@ public function submitOptionsForm(&$form, FormStateInterface $form_state) { } /** - * Attach to another view. + * {@inheritdoc} */ - public function attachTo(ViewExecutable $view, $display_id) { + public function attachTo(ViewExecutable $view, $display_id, array &$build) { $displays = $this->getOption('displays'); if (empty($displays[$display_id])) { diff --git a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php index d7d5ea4fa6..c150f20f10 100644 --- a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php +++ b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php @@ -13,6 +13,7 @@ use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Theme\Registry; +use Drupal\views\Form\ViewsForm; use Drupal\views\Plugin\views\area\AreaPluginBase; use Drupal\views\ViewExecutable; use Drupal\views\Plugin\views\PluginBase; @@ -419,8 +420,15 @@ public function usesAreas() { /** * Allow displays to attach to other views. + * + * @param \Drupal\views\ViewExecutable $view + * The views executable. + * @param string $display_id + * The display to attach to. + * @param array $build + * The parent view render array. */ - public function attachTo(ViewExecutable $view, $display_id) { } + public function attachTo(ViewExecutable $view, $display_id, array &$build) { } /** * Static member function to list which sections are defaultable @@ -2125,11 +2133,86 @@ public function getMenuLinks() { * Render this display. */ public function render() { + $rows = (!empty($this->view->result) || $this->view->style_plugin->evenEmpty()) ? $this->view->style_plugin->render($this->view->result) : array(); + $element = array( '#theme' => $this->themeFunctions(), '#view' => $this->view, + // Assigned by reference so anything added in $element['#attached'] will + // be available on the view. + '#attached' => &$this->view->element['#attached'], + '#pre_render' => [[$this, 'elementPreRender']], + '#rows' => $rows, ); - $element['#attached'] = &$this->view->element['#attached']; + + return $element; + } + + /** + * #pre_render callback for view display rendering. + * + * @see self::render() + * + * @param array $element + * The element to #pre_render + * + * @return array + * The processed element. + */ + public function elementPreRender(array $element) { + $view = $element['#view']; + $empty = empty($view->result); + + // Force a render array so CSS/JS can be attached. + if (!is_array($element['#rows'])) { + $element['#rows'] = array('#markup' => $element['#rows']); + } + + $element['#header'] = $view->display_handler->renderArea('header', $empty); + $element['#footer'] = $view->display_handler->renderArea('footer', $empty); + $element['#empty'] = $empty ? $view->display_handler->renderArea('empty', $empty) : array(); + $element['#exposed'] = !empty($view->exposed_widgets) ? $view->exposed_widgets : array(); + $element['#more'] = $view->display_handler->renderMoreLink(); + $element['#feed_icon'] = !empty($view->feed_icon) ? $view->feed_icon : array(); + + if ($view->display_handler->renderPager()) { + $exposed_input = isset($view->exposed_raw_input) ? $view->exposed_raw_input : NULL; + $element['#pager'] = $view->renderPager($exposed_input); + } + + if (!empty($view->attachment_before)) { + $element['#attachment_before'] = $view->attachment_before; + } + if (!empty($view->attachment_after)) { + $element['#attachment_after'] = $view->attachment_after; + } + + // If form fields were found in the view, reformat the view output as a form. + if ($view->hasFormElements()) { + // Only render row output if there are rows. Otherwise, render the empty + // region. + if (!empty($element['#rows'])) { + $output = $element['#rows']; + } + else { + $output = $element['#empty']; + } + + $form_object = ViewsForm::create(\Drupal::getContainer(), $view->storage->id(), $view->current_display); + $form = \Drupal::formBuilder()->getForm($form_object, $view, $output); + // The form is requesting that all non-essential views elements be hidden, + // usually because the rendered step is not a view result. + if ($form['show_view_elements']['#value'] == FALSE) { + $element['#header'] = array(); + $element['#exposed'] = array(); + $element['#pager'] = array(); + $element['#footer'] = array(); + $element['#more'] = array(); + $element['#feed_icon'] = array(); + } + + $element['#rows'] = $form; + } return $element; } diff --git a/core/modules/views/src/Plugin/views/display/Feed.php b/core/modules/views/src/Plugin/views/display/Feed.php index bd84581591..f544cdf65f 100644 --- a/core/modules/views/src/Plugin/views/display/Feed.php +++ b/core/modules/views/src/Plugin/views/display/Feed.php @@ -80,7 +80,7 @@ public function execute() { $response = $this->view->getResponse(); - $response->setContent($output); + $response->setContent(drupal_render($output)); return $response; } @@ -94,7 +94,7 @@ public function preview() { if (!empty($this->view->live_preview)) { $output = array( '#prefix' => '
',
-        '#markup' => String::checkPlain($output),
+        '#markup' => String::checkPlain(drupal_render($output)),
         '#suffix' => '
', ); } @@ -253,9 +253,9 @@ public function submitOptionsForm(&$form, FormStateInterface $form_state) { } /** - * Overrides \Drupal\views\Plugin\views\display\DisplayPluginBase::attachTo(). + * {@inheritdoc} */ - public function attachTo(ViewExecutable $clone, $display_id) { + public function attachTo(ViewExecutable $clone, $display_id, array &$build) { $displays = $this->getOption('displays'); if (empty($displays[$display_id])) { return; @@ -266,7 +266,7 @@ public function attachTo(ViewExecutable $clone, $display_id) { $clone->setDisplay($this->display['id']); $clone->buildTitle(); if ($plugin = $clone->display_handler->getPlugin('style')) { - $plugin->attachTo($display_id, $this->getPath(), $clone->getTitle()); + $plugin->attachTo($build, $display_id, $this->getPath(), $clone->getTitle()); } // Clean up. diff --git a/core/modules/views/src/Plugin/views/query/Sql.php b/core/modules/views/src/Plugin/views/query/Sql.php index 25b1a230e7..c52ccacc7a 100644 --- a/core/modules/views/src/Plugin/views/query/Sql.php +++ b/core/modules/views/src/Plugin/views/query/Sql.php @@ -14,6 +14,7 @@ use Drupal\Core\Database\DatabaseExceptionWrapper; use Drupal\views\Plugin\views\join\JoinPluginBase; use Drupal\views\Plugin\views\HandlerBase; +use Drupal\views\ResultRow; use Drupal\views\ViewExecutable; use Drupal\views\Views; @@ -1428,7 +1429,12 @@ function execute(ViewExecutable $view) { $result = $query->execute(); $result->setFetchMode(\PDO::FETCH_CLASS, 'Drupal\views\ResultRow'); + + // Setup the result row objects. $view->result = iterator_to_array($result); + array_walk($view->result, function(ResultRow $row, $index) { + $row->index = $index; + }); $view->pager->postExecute($view->result); $view->pager->updatePageInfo(); diff --git a/core/modules/views/src/Plugin/views/style/Opml.php b/core/modules/views/src/Plugin/views/style/Opml.php index 11b359c3b8..a1a0094c92 100644 --- a/core/modules/views/src/Plugin/views/style/Opml.php +++ b/core/modules/views/src/Plugin/views/style/Opml.php @@ -32,7 +32,7 @@ class Opml extends StylePluginBase { /** * {@inheritdoc} */ - public function attachTo($display_id, $path, $title) { + public function attachTo(array &$build, $display_id, $path, $title) { $display = $this->view->displayHandlers->get($display_id); $url_options = array(); $input = $this->view->getExposedInput(); @@ -45,19 +45,15 @@ public function attachTo($display_id, $path, $title) { if ($display->hasPath()) { if (empty($this->preview)) { $build['#attached']['drupal_add_feed'][] = array($url, $title); - drupal_render($build); } } else { - if (empty($this->view->feed_icon)) { - $this->view->feed_icon = ''; - } $feed_icon = array( '#theme' => 'feed_icon', '#url' => $url, '#title' => $title, ); - $this->view->feed_icon .= drupal_render($feed_icon); + $this->view->feed_icon = $feed_icon; } } @@ -83,7 +79,7 @@ public function render() { '#rows' => $rows, ); unset($this->view->row_index); - return drupal_render($build); + return $build; } } diff --git a/core/modules/views/src/Plugin/views/style/Rss.php b/core/modules/views/src/Plugin/views/style/Rss.php index e710fac10f..60482b148a 100644 --- a/core/modules/views/src/Plugin/views/style/Rss.php +++ b/core/modules/views/src/Plugin/views/style/Rss.php @@ -32,7 +32,7 @@ class Rss extends StylePluginBase { */ protected $usesRowPlugin = TRUE; - public function attachTo($display_id, $path, $title) { + public function attachTo(array &$build, $display_id, $path, $title) { $display = $this->view->displayHandlers->get($display_id); $url_options = array(); $input = $this->view->getExposedInput(); @@ -44,27 +44,25 @@ public function attachTo($display_id, $path, $title) { $url = url($this->view->getUrl(NULL, $path), $url_options); if ($display->hasPath()) { if (empty($this->preview)) { + // Add a call for drupal_add_feed to the view attached data. $build['#attached']['drupal_add_feed'][] = array($url, $title); - drupal_process_attached($build); } } else { - if (empty($this->view->feed_icon)) { - $this->view->feed_icon = ''; - } $feed_icon = array( '#theme' => 'feed_icon', '#url' => $url, '#title' => $title, ); - $feed_icon['#attached']['drupal_add_html_head_link'][][] = array( + $this->view->feed_icon = $feed_icon; + + // Add a call for drupal_add_html_head_link to the view attached data. + $build['#attached']['drupal_add_html_head_link'][][] = array( 'rel' => 'alternate', 'type' => 'application/rss+xml', 'title' => $title, 'href' => $url, ); - $this->view->feed_icon .= drupal_render($feed_icon); - drupal_process_attached($feed_icon); } } @@ -116,7 +114,7 @@ public function getDescription() { public function render() { if (empty($this->view->rowPlugin)) { debug('Drupal\views\Plugin\views\style\Rss: Missing row plugin'); - return; + return array(); } $rows = ''; @@ -145,7 +143,7 @@ public function render() { '#rows' => SafeMarkup::set($rows), ); unset($this->view->row_index); - return drupal_render($build); + return $build; } } diff --git a/core/modules/views/src/Plugin/views/style/StylePluginBase.php b/core/modules/views/src/Plugin/views/style/StylePluginBase.php index b1f8e4e36f..4b79f8414e 100644 --- a/core/modules/views/src/Plugin/views/style/StylePluginBase.php +++ b/core/modules/views/src/Plugin/views/style/StylePluginBase.php @@ -492,9 +492,7 @@ public function renderGroupingSets($sets, $level = 0) { if ($this->usesRowPlugin()) { foreach ($set['rows'] as $index => $row) { $this->view->row_index = $index; - $render = $this->view->rowPlugin->render($row); - // Row render arrays cannot be contained by style render arrays. - $set['rows'][$index] = drupal_render($render); + $set['rows'][$index] = $this->view->rowPlugin->render($row); } } diff --git a/core/modules/views/src/ResultRow.php b/core/modules/views/src/ResultRow.php index 018a0de58c..a74ed91bc3 100644 --- a/core/modules/views/src/ResultRow.php +++ b/core/modules/views/src/ResultRow.php @@ -26,6 +26,13 @@ class ResultRow { */ public $_relationship_entities = array(); + /** + * An incremental number which represents the row in the entire result. + * + * @var integer + */ + public $index; + /** * Constructs a ResultRow object. * diff --git a/core/modules/views/src/Tests/Plugin/StyleTest.php b/core/modules/views/src/Tests/Plugin/StyleTest.php index 72d7403e2b..957f8f87ad 100644 --- a/core/modules/views/src/Tests/Plugin/StyleTest.php +++ b/core/modules/views/src/Tests/Plugin/StyleTest.php @@ -147,14 +147,14 @@ function _testGrouping($stripped = FALSE) { $expected['Job: Singer']['group'] = 'Job: Singer'; $expected['Job: Singer']['rows']['Age: 25'] = array(); $expected['Job: Singer']['rows']['Age: 25']['group'] = 'Age: 25'; - $expected['Job: Singer']['rows']['Age: 25']['rows'][0] = new ResultRow(); + $expected['Job: Singer']['rows']['Age: 25']['rows'][0] = new ResultRow(['index' => 0]); $expected['Job: Singer']['rows']['Age: 25']['rows'][0]->views_test_data_name = 'John'; $expected['Job: Singer']['rows']['Age: 25']['rows'][0]->views_test_data_job = 'Singer'; $expected['Job: Singer']['rows']['Age: 25']['rows'][0]->views_test_data_age = '25'; $expected['Job: Singer']['rows']['Age: 25']['rows'][0]->views_test_data_id = '1'; $expected['Job: Singer']['rows']['Age: 27'] = array(); $expected['Job: Singer']['rows']['Age: 27']['group'] = 'Age: 27'; - $expected['Job: Singer']['rows']['Age: 27']['rows'][1] = new ResultRow(); + $expected['Job: Singer']['rows']['Age: 27']['rows'][1] = new ResultRow(['index' => 1]); $expected['Job: Singer']['rows']['Age: 27']['rows'][1]->views_test_data_name = 'George'; $expected['Job: Singer']['rows']['Age: 27']['rows'][1]->views_test_data_job = 'Singer'; $expected['Job: Singer']['rows']['Age: 27']['rows'][1]->views_test_data_age = '27'; @@ -163,7 +163,7 @@ function _testGrouping($stripped = FALSE) { $expected['Job: Drummer']['group'] = 'Job: Drummer'; $expected['Job: Drummer']['rows']['Age: 28'] = array(); $expected['Job: Drummer']['rows']['Age: 28']['group'] = 'Age: 28'; - $expected['Job: Drummer']['rows']['Age: 28']['rows'][2] = new ResultRow(); + $expected['Job: Drummer']['rows']['Age: 28']['rows'][2] = new ResultRow(['index' => 2]); $expected['Job: Drummer']['rows']['Age: 28']['rows'][2]->views_test_data_name = 'Ringo'; $expected['Job: Drummer']['rows']['Age: 28']['rows'][2]->views_test_data_job = 'Drummer'; $expected['Job: Drummer']['rows']['Age: 28']['rows'][2]->views_test_data_age = '28'; diff --git a/core/modules/views/src/ViewExecutable.php b/core/modules/views/src/ViewExecutable.php index cdbd39d4a8..34f7500f4d 100644 --- a/core/modules/views/src/ViewExecutable.php +++ b/core/modules/views/src/ViewExecutable.php @@ -1314,6 +1314,7 @@ public function render($display_id = NULL) { $cache = $this->display_handler->getPlugin('cache'); } + /** @var \Drupal\views\Plugin\views\cache\CachePluginBase $cache */ if ($cache && $cache->cacheGet('output')) { } else { @@ -1508,7 +1509,7 @@ public function attachDisplays() { // Create a clone for the attachments to manipulate. 'static' refers to the current class name. $cloned_view = new static($this->storage, $this->user); $cloned_view->setRequest($this->getRequest()); - $this->displayHandlers->get($id)->attachTo($cloned_view, $this->current_display); + $this->displayHandlers->get($id)->attachTo($cloned_view, $this->current_display, $this->element); } $this->is_attachment = FALSE; } diff --git a/core/modules/views/tests/src/Unit/Plugin/field/CounterTest.php b/core/modules/views/tests/src/Unit/Plugin/field/CounterTest.php index 1f9d0105e9..72516cf5de 100644 --- a/core/modules/views/tests/src/Unit/Plugin/field/CounterTest.php +++ b/core/modules/views/tests/src/Unit/Plugin/field/CounterTest.php @@ -86,8 +86,8 @@ protected function setUp() { $this->view->display_handler = $this->display; $this->view->pager = $this->pager; - foreach (ViewTestData::dataSet() as $set) { - $this->testData[] = new ResultRow($set); + foreach (ViewTestData::dataSet() as $index => $set) { + $this->testData[] = new ResultRow($set + ['index' => $index]); } $this->definition = array('title' => 'counter field', 'plugin_type' => 'field'); diff --git a/core/modules/views/views.module b/core/modules/views/views.module index b4da2d954e..8c989b1882 100644 --- a/core/modules/views/views.module +++ b/core/modules/views/views.module @@ -128,7 +128,21 @@ function views_theme($existing, $type, $theme, $path) { // For displays, we pass in a dummy array as the first parameter, since // $view is an object but the core contextual_preprocess() function only // attaches contextual links when the primary theme argument is an array. - 'display' => array('view_array' => array(), 'view' => NULL), + 'display' => array( + 'view_array' => array(), + 'view' => NULL, + 'rows' => array(), + 'header' => array(), + 'footer' => array(), + 'empty' => array(), + 'exposed' => array(), + 'more' => array(), + 'feed_icon' => array(), + 'pager' => array(), + 'title' => '', + 'attachment_before' => array(), + 'attachment_after' => array(), + ), 'style' => array('view' => NULL, 'options' => NULL, 'rows' => NULL, 'title' => NULL), 'row' => array('view' => NULL, 'options' => NULL, 'row' => NULL, 'field_alias' => NULL), 'exposed_form' => array('view' => NULL, 'options' => NULL), @@ -771,11 +785,12 @@ function views_pre_render_views_form_views_form($element) { } // Apply substitutions to the rendered output. - $element['output']['#markup'] = str_replace($search, $replace, $element['output']['#markup']); + $element['output'] = array('#markup' => str_replace($search, $replace, drupal_render($element['output']))); // Sort, render and add remaining form fields. $children = Element::children($element, TRUE); $element['#children'] = drupal_render_children($element, $children); + return $element; } diff --git a/core/modules/views/views.theme.inc b/core/modules/views/views.theme.inc index 7af7216ed9..f32da9a7c9 100644 --- a/core/modules/views/views.theme.inc +++ b/core/modules/views/views.theme.inc @@ -5,12 +5,11 @@ * Preprocessors and helper functions to make theming easier. */ +use Drupal\Component\Utility\Html; use Drupal\Component\Utility\SafeMarkup; use Drupal\Component\Utility\String; use Drupal\Component\Utility\Xss; use Drupal\Core\Template\Attribute; -use Drupal\views\Form\ViewsForm; -use Drupal\views\ViewExecutable; /** * Prepares variables for view templates. @@ -23,23 +22,16 @@ */ function template_preprocess_views_view(&$variables) { $view = $variables['view']; + $id = $view->storage->id(); - $variables['rows'] = (!empty($view->result) || $view->style_plugin->evenEmpty()) ? $view->style_plugin->render($view->result) : array(); - // Force a render array so CSS/JS can be added. - if (!is_array($variables['rows'])) { - $variables['rows'] = array('#markup' => $variables['rows']); - } - - $variables['css_name'] = drupal_clean_css_identifier($view->storage->id()); - $variables['id'] = $view->storage->id(); + $variables['css_name'] = Html::cleanCssIdentifier($id); + $variables['id'] = $id; $variables['display_id'] = $view->current_display; // Basic classes. - $variables['css_class'] = ''; - $variables['attributes']['class'] = array(); $variables['attributes']['class'][] = 'view'; - $variables['attributes']['class'][] = 'view-' . drupal_clean_css_identifier($variables['id']); + $variables['attributes']['class'][] = 'view-' . Html::cleanCssIdentifier($variables['id']); $variables['attributes']['class'][] = 'view-id-' . $variables['id']; $variables['attributes']['class'][] = 'view-display-id-' . $variables['display_id']; @@ -49,39 +41,6 @@ function template_preprocess_views_view(&$variables) { $variables['attributes']['class'][] = $variables['css_class']; } - $empty = empty($view->result); - $variables['header'] = $view->display_handler->renderArea('header', $empty); - $variables['footer'] = $view->display_handler->renderArea('footer', $empty); - $variables['empty'] = $empty ? $view->display_handler->renderArea('empty', $empty) : FALSE; - - $variables['exposed'] = !empty($view->exposed_widgets) ? $view->exposed_widgets : ''; - $variables['more'] = $view->display_handler->renderMoreLink(); - $variables['feed_icon'] = !empty($view->feed_icon) ? $view->feed_icon : ''; - - $variables['pager'] = ''; - - // @todo: Figure out whether this belongs into views_ui_preprocess_views_view. - // Render title for the admin preview. - $variables['title'] = !empty($view->views_ui_context) ? Xss::filterAdmin($view->getTitle()) : ''; - - if ($view->display_handler->renderPager()) { - $exposed_input = isset($view->exposed_raw_input) ? $view->exposed_raw_input : NULL; - $variables['pager'] = $view->renderPager($exposed_input); - } - - if (!empty($view->attachment_before)) { - $variables['attachment_before'] = $view->attachment_before; - } - else { - $variables['attachment_before'] = array(); - } - if (!empty($view->attachment_after)) { - $variables['attachment_after'] = $view->attachment_after; - } - else { - $variables['attachment_after'] = array(); - } - // 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 @@ -105,36 +64,6 @@ function template_preprocess_views_view(&$variables) { $variables['dom_id'] = $view->dom_id; $variables['attributes']['class'][] = 'view-dom-id-' . $variables['dom_id']; } - - // If form fields were found in the view, reformat the view output as a form. - if ($view->hasFormElements()) { - // Copy the rows so as not to modify them by reference when rendering. - $rows = $variables['rows']; - // Only render row output if there are rows. Otherwise, render the empty - // region. - if (!empty($rows)) { - $output = drupal_render($rows); - } - else { - $empty = $variables['empty']; - $output = drupal_render($empty); - } - - $container = \Drupal::getContainer(); - $form_object = new ViewsForm($container->get('class_resolver'), $container->get('url_generator'), $container->get('request_stack'), $view->storage->id(), $view->current_display); - $form = \Drupal::formBuilder()->getForm($form_object, $view, $output); - // The form is requesting that all non-essential views elements be hidden, - // usually because the rendered step is not a view result. - if ($form['show_view_elements']['#value'] == FALSE) { - $variables['header'] = ''; - $variables['exposed'] = ''; - $variables['pager'] = ''; - $variables['footer'] = ''; - $variables['more'] = ''; - $variables['feed_icon'] = ''; - } - $variables['rows'] = $form; - } } /** @@ -162,9 +91,11 @@ function template_preprocess_views_view_fields(&$variables) { // Loop through the fields for this view. $previous_inline = FALSE; $variables['fields'] = array(); // ensure it's at least an empty array. + /** @var \Drupal\views\ResultRow $row */ + $row = $variables['row']; foreach ($view->field as $id => $field) { // render this even if set to exclude so it can be used elsewhere. - $field_output = $view->style_plugin->getField($view->row_index, $id); + $field_output = $view->style_plugin->getField($row->index, $id); $empty = $field->isValueEmpty($field_output, $field->options['empty_zero']); if (empty($field->options['exclude']) && (!$empty || (empty($field->options['hide_empty']) && empty($variables['options']['hide_empty'])))) { $object = new stdClass(); @@ -178,7 +109,7 @@ function template_preprocess_views_view_fields(&$variables) { $attributes['class'][] = 'field-content'; } - if ($classes = $object->handler->elementClasses($view->row_index)) { + if ($classes = $object->handler->elementClasses($row->index)) { $attributes['class'][] = $classes; } $attributes = new Attribute($attributes); @@ -195,8 +126,8 @@ function template_preprocess_views_view_fields(&$variables) { } $object->content = $field_output; - if (isset($view->field[$id]->field_alias) && isset($variables['row']->{$view->field[$id]->field_alias})) { - $object->raw = $variables['row']->{$view->field[$id]->field_alias}; + if (isset($view->field[$id]->field_alias) && isset($row->{$view->field[$id]->field_alias})) { + $object->raw = $row->{$view->field[$id]->field_alias}; } else { $object->raw = NULL; // make sure it exists to reduce NOTICE @@ -225,7 +156,7 @@ function template_preprocess_views_view_fields(&$variables) { $attributes['class'][] = 'views-field-' . $object->class; } - if ($classes = $object->handler->elementWrapperClasses($view->row_index)) { + if ($classes = $object->handler->elementWrapperClasses($row->index)) { $attributes['class'][] = $classes; } $attributes = new Attribute($attributes); @@ -254,7 +185,7 @@ function template_preprocess_views_view_fields(&$variables) { $attributes['class'][] = 'views-label-' . $object->class; } - $element_label_class = $object->handler->elementLabelClasses($view->row_index); + $element_label_class = $object->handler->elementLabelClasses($row->index); if ($element_label_class) { $attributes['class'][] = $element_label_class; } diff --git a/core/modules/views_ui/src/ViewUI.php b/core/modules/views_ui/src/ViewUI.php index 51e16b2907..ab77b38ab7 100644 --- a/core/modules/views_ui/src/ViewUI.php +++ b/core/modules/views_ui/src/ViewUI.php @@ -590,7 +590,6 @@ public function renderPreview($display_id, $args = array()) { if (empty($errors)) { $this->ajax = TRUE; $this->executable->live_preview = TRUE; - $this->views_ui_context = TRUE; // AJAX happens via HTTP POST but everything expects exposed data to // be in GET. Copy stuff but remove ajax-framework specific keys. diff --git a/core/modules/views_ui/views_ui.module b/core/modules/views_ui/views_ui.module index 54192ed574..3960294e33 100644 --- a/core/modules/views_ui/views_ui.module +++ b/core/modules/views_ui/views_ui.module @@ -13,6 +13,7 @@ use Drupal\views\Analyzer; use Drupal\Core\Ajax\AjaxResponse; use Drupal\Core\Ajax\ReplaceCommand; +use Drupal\Component\Utility\Xss; /** * Implements hook_help(). @@ -130,6 +131,12 @@ function views_ui_theme() { */ function views_ui_preprocess_views_view(&$variables) { $view = $variables['view']; + + // Render title for the admin preview. + if (!empty($view->live_preview)) { + $variables['title'] = Xss::filterAdmin($view->getTitle()); + } + if (!empty($view->live_preview) && \Drupal::moduleHandler()->moduleExists('contextual')) { $view->setShowAdminLinks(FALSE); foreach (array('title', 'header', 'exposed', 'rows', 'pager', 'more', 'footer', 'empty', 'attachment_after', 'attachment_before') as $section) { -- GitLab