You are here

public function ViewUI::renderPreview in Zircon Profile 8

Same name and namespace in other branches
  1. 8.0 core/modules/views_ui/src/ViewUI.php \Drupal\views_ui\ViewUI::renderPreview()

File

core/modules/views_ui/src/ViewUI.php, line 546
Contains \Drupal\views_ui\ViewUI.

Class

ViewUI
Stores UI related temporary settings.

Namespace

Drupal\views_ui

Code

public function renderPreview($display_id, $args = array()) {

  // Save the current path so it can be restored before returning from this function.
  $request_stack = \Drupal::requestStack();
  $current_request = $request_stack
    ->getCurrentRequest();
  $executable = $this
    ->getExecutable();

  // Determine where the query and performance statistics should be output.
  $config = \Drupal::config('views.settings');
  $show_query = $config
    ->get('ui.show.sql_query.enabled');
  $show_info = $config
    ->get('ui.show.preview_information');
  $show_location = $config
    ->get('ui.show.sql_query.where');
  $show_stats = $config
    ->get('ui.show.performance_statistics');
  if ($show_stats) {
    $show_stats = $config
      ->get('ui.show.sql_query.where');
  }
  $combined = $show_query && $show_stats;
  $rows = array(
    'query' => array(),
    'statistics' => array(),
  );
  $errors = $executable
    ->validate();
  $executable
    ->destroy();
  if (empty($errors)) {
    $this->ajax = TRUE;
    $executable->live_preview = TRUE;

    // AJAX happens via HTTP POST but everything expects exposed data to
    // be in GET. Copy stuff but remove ajax-framework specific keys.
    // If we're clicking on links in a preview, though, we could actually
    // have some input in the query parameters, so we merge request() and
    // query() to ensure we get it all.
    $exposed_input = array_merge(\Drupal::request()->request
      ->all(), \Drupal::request()->query
      ->all());
    foreach (array(
      'view_name',
      'view_display_id',
      'view_args',
      'view_path',
      'view_dom_id',
      'pager_element',
      'view_base_path',
      AjaxResponseSubscriber::AJAX_REQUEST_PARAMETER,
      'ajax_page_state',
      'form_id',
      'form_build_id',
      'form_token',
    ) as $key) {
      if (isset($exposed_input[$key])) {
        unset($exposed_input[$key]);
      }
    }
    $executable
      ->setExposedInput($exposed_input);
    if (!$executable
      ->setDisplay($display_id)) {
      return [
        '#markup' => t('Invalid display id @display', array(
          '@display' => $display_id,
        )),
      ];
    }
    $executable
      ->setArguments($args);

    // Store the current view URL for later use:
    if ($executable
      ->hasUrl() && $executable->display_handler
      ->getOption('path')) {
      $path = $executable
        ->getUrl();
    }

    // Make view links come back to preview.
    // Also override the current path so we get the pager, and make sure the
    // Request object gets all of the proper values from $_SERVER.
    $request = Request::createFromGlobals();
    $request->attributes
      ->set(RouteObjectInterface::ROUTE_NAME, 'entity.view.preview_form');
    $request->attributes
      ->set(RouteObjectInterface::ROUTE_OBJECT, \Drupal::service('router.route_provider')
      ->getRouteByName('entity.view.preview_form'));
    $request->attributes
      ->set('view', $this->storage);
    $request->attributes
      ->set('display_id', $display_id);
    $raw_parameters = new ParameterBag();
    $raw_parameters
      ->set('view', $this
      ->id());
    $raw_parameters
      ->set('display_id', $display_id);
    $request->attributes
      ->set('_raw_variables', $raw_parameters);
    foreach ($args as $key => $arg) {
      $request->attributes
        ->set('arg_' . $key, $arg);
    }
    $request_stack
      ->push($request);

    // Suppress contextual links of entities within the result set during a
    // Preview.
    // @todo We'll want to add contextual links specific to editing the View, so
    //   the suppression may need to be moved deeper into the Preview pipeline.
    views_ui_contextual_links_suppress_push();
    $show_additional_queries = $config
      ->get('ui.show.additional_queries');
    Timer::start('entity.view.preview_form');
    if ($show_additional_queries) {
      $this
        ->startQueryCapture();
    }

    // Execute/get the view preview.
    $preview = $executable
      ->preview($display_id, $args);
    if ($show_additional_queries) {
      $this
        ->endQueryCapture();
    }
    $this->render_time = Timer::stop('entity.view.preview_form');
    views_ui_contextual_links_suppress_pop();

    // Prepare the query information and statistics to show either above or
    // below the view preview.
    if ($show_info || $show_query || $show_stats) {

      // Get information from the preview for display.
      if (!empty($executable->build_info['query'])) {
        if ($show_query) {
          $query_string = $executable->build_info['query'];

          // Only the sql default class has a method getArguments.
          $quoted = array();
          if ($executable->query instanceof Sql) {
            $quoted = $query_string
              ->getArguments();
            $connection = Database::getConnection();
            foreach ($quoted as $key => $val) {
              if (is_array($val)) {
                $quoted[$key] = implode(', ', array_map(array(
                  $connection,
                  'quote',
                ), $val));
              }
              else {
                $quoted[$key] = $connection
                  ->quote($val);
              }
            }
          }
          $rows['query'][] = array(
            array(
              'data' => array(
                '#type' => 'inline_template',
                '#template' => "<strong>{% trans 'Query' %}</strong>",
              ),
            ),
            array(
              'data' => array(
                '#type' => 'inline_template',
                '#template' => '<pre>{{ query }}</pre>',
                '#context' => array(
                  'query' => strtr($query_string, $quoted),
                ),
              ),
            ),
          );
          if (!empty($this->additionalQueries)) {
            $queries[] = array(
              '#prefix' => '<strong>',
              '#markup' => t('These queries were run during view rendering:'),
              '#suffix' => '</strong>',
            );
            foreach ($this->additionalQueries as $query) {
              $query_string = strtr($query['query'], $query['args']);
              $queries[] = array(
                '#prefix' => "\n",
                '#markup' => t('[@time ms] @query', array(
                  '@time' => round($query['time'] * 100000, 1) / 100000.0,
                  '@query' => $query_string,
                )),
              );
            }
            $rows['query'][] = array(
              array(
                'data' => array(
                  '#type' => 'inline_template',
                  '#template' => "<strong>{% trans 'Other queries' %}</strong>",
                ),
              ),
              array(
                'data' => array(
                  '#prefix' => '<pre>',
                  'queries' => $queries,
                  '#suffix' => '</pre>',
                ),
              ),
            );
          }
        }
        if ($show_info) {
          $rows['query'][] = array(
            array(
              'data' => array(
                '#type' => 'inline_template',
                '#template' => "<strong>{% trans 'Title' %}</strong>",
              ),
            ),
            Xss::filterAdmin($executable
              ->getTitle()),
          );
          if (isset($path)) {

            // @todo Views should expect and store a leading /. See:
            //   https://www.drupal.org/node/2423913
            $path = \Drupal::l($path
              ->toString(), $path);
          }
          else {
            $path = t('This display has no path.');
          }
          $rows['query'][] = array(
            array(
              'data' => array(
                '#prefix' => '<strong>',
                '#markup' => t('Path'),
                '#suffix' => '</strong>',
              ),
            ),
            array(
              'data' => array(
                '#markup' => $path,
              ),
            ),
          );
        }
        if ($show_stats) {
          $rows['statistics'][] = array(
            array(
              'data' => array(
                '#type' => 'inline_template',
                '#template' => "<strong>{% trans 'Query build time' %}</strong>",
              ),
            ),
            t('@time ms', array(
              '@time' => intval($executable->build_time * 100000) / 100,
            )),
          );
          $rows['statistics'][] = array(
            array(
              'data' => array(
                '#type' => 'inline_template',
                '#template' => "<strong>{% trans 'Query execute time' %}</strong>",
              ),
            ),
            t('@time ms', array(
              '@time' => intval($executable->execute_time * 100000) / 100,
            )),
          );
          $rows['statistics'][] = array(
            array(
              'data' => array(
                '#type' => 'inline_template',
                '#template' => "<strong>{% trans 'View render time' %}</strong>",
              ),
            ),
            t('@time ms', array(
              '@time' => intval($executable->render_time * 100000) / 100,
            )),
          );
        }
        \Drupal::moduleHandler()
          ->alter('views_preview_info', $rows, $executable);
      }
      else {

        // No query was run. Display that information in place of either the
        // query or the performance statistics, whichever comes first.
        if ($combined || $show_location === 'above') {
          $rows['query'][] = array(
            array(
              'data' => array(
                '#prefix' => '<strong>',
                '#markup' => t('Query'),
                '#suffix' => '</strong>',
              ),
            ),
            array(
              'data' => array(
                '#markup' => t('No query was run'),
              ),
            ),
          );
        }
        else {
          $rows['statistics'][] = array(
            array(
              'data' => array(
                '#prefix' => '<strong>',
                '#markup' => t('Query'),
                '#suffix' => '</strong>',
              ),
            ),
            array(
              'data' => array(
                '#markup' => t('No query was run'),
              ),
            ),
          );
        }
      }
    }
  }
  else {
    foreach ($errors as $display_errors) {
      foreach ($display_errors as $error) {
        drupal_set_message($error, 'error');
      }
    }
    $preview = t('Unable to preview due to validation errors.');
  }

  // Assemble the preview, the query info, and the query statistics in the
  // requested order.
  $table = array(
    '#type' => 'table',
    '#prefix' => '<div class="views-query-info">',
    '#suffix' => '</div>',
  );
  if ($show_location === 'above' || $show_location === 'below') {
    if ($combined) {
      $table['#rows'] = array_merge($rows['query'], $rows['statistics']);
    }
    else {
      $table['#rows'] = $rows['query'];
    }
  }
  elseif ($show_stats === 'above' || $show_stats === 'below') {
    $table['#rows'] = $rows['statistics'];
  }
  if ($show_location === 'above' || $show_stats === 'above') {
    $output = [
      'table' => $table,
      'preview' => $preview,
    ];
  }
  elseif ($show_location === 'below' || $show_stats === 'below') {
    $output = [
      'preview' => $preview,
      'table' => $table,
    ];
  }

  // Ensure that we just remove an additional request we pushed earlier.
  // This could happen if $errors was not empty.
  if ($request_stack
    ->getCurrentRequest() != $current_request) {
    $request_stack
      ->pop();
  }
  return $output;
}