Commit 29121e15 authored by Jeff Schuler's avatar Jeff Schuler
Browse files

Issue #2943351 by jeffschuler: Simplify Views display plugin by deriving from RestExport class.

parent a0bcf780
Loading
Loading
Loading
Loading
+4 −283
Original line number Diff line number Diff line
@@ -5,17 +5,11 @@ declare(strict_types = 1);
namespace Drupal\views_geojson\Plugin\views\display;

use Drupal;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Cache\CacheableResponse;
use Drupal\Core\Render\RenderContext;
use Drupal\views\Plugin\views\display\PathPluginBase;
use Drupal\views\Plugin\views\display\ResponseDisplayPluginInterface;
use Drupal\views\Render\ViewsRenderPipelineMarkup;
use Drupal\views\ViewExecutable;
use Symfony\Component\Routing\RouteCollection;
use Drupal\rest\Plugin\views\display\RestExport;

/**
 * The plugin that handles Data response callbacks for REST resources.
 * The plugin that handles Data response callbacks for GeoJSON REST resources.
 *
 * @ingroup views_display_plugins
 *
@@ -28,269 +22,7 @@ use Symfony\Component\Routing\RouteCollection;
 *     returns_response=TRUE
 * )
 */
class GeoJsonExport extends PathPluginBase implements ResponseDisplayPluginInterface {

  /**
   * Overrides the content type of the data response, if needed.
   *
   * @var string
   */
  protected $contentType = 'json';

  /**
   * The mime type for the response.
   *
   * @var string
   */
  protected $mimeType;

  /**
   * Uses AJAX variable.
   *
   * Overrides \Drupal\views\Plugin\views\display\DisplayPluginBase::$usesAJAX.
   *
   * @var bool
   */
  protected $usesAJAX = FALSE;

  /**
   * Uses Areas variable.
   *
   * Overrides \Drupal\views\Plugin\views\display\DisplayPluginBase::$usesAreas.
   *
   * @var bool
   */
  protected $usesAreas = FALSE;

  /**
   * Uses More variable.
   *
   * Overrides \Drupal\views\Plugin\views\display\DisplayPluginBase::$usesMore.
   *
   * @var bool
   */
  protected $usesMore = FALSE;

  /**
   * Uses Options variable.
   *
   * Overrides \Drupal\views\Plugin\views\display\DisplayPluginBase::$usesAreas.
   *
   * @var bool
   */
  protected $usesOptions = FALSE;

  /**
   * Uses Pager variable.
   *
   * Overrides \Drupal\views\Plugin\views\display\DisplayPluginBase::$usesPager.
   *
   * @var bool
   */
  protected $usesPager = FALSE;

  /**
   * {@inheritdoc}
   */
  public static function buildResponse($view_id, $display_id, array $args = []) {
    $build = static::buildBasicRenderable($view_id, $display_id, $args);

    // Set up an empty response, so for example RSS can set the proper
    // Content-Type header.
    $response = new CacheableResponse('', 200);
    $build['#response'] = $response;

    /** @var \Drupal\Core\Render\RendererInterface $renderer */
    $renderer = Drupal::service('renderer');

    $output = (string) $renderer->renderRoot($build);

    if (empty($output)) {
      // Throw new NotFoundHttpException();
    }

    $response->setContent($output);
    $cache_metadata = CacheableMetadata::createFromRenderArray($build);
    $response->addCacheableDependency($cache_metadata);

    return $response;
  }

  /**
   * {@inheritdoc}
   */
  public function collectRoutes(RouteCollection $collection) {
    parent::collectRoutes($collection);
    $view_id = $this->view->storage->id();
    $display_id = $this->display['id'];

    if ($route = $collection->get("view.{$view_id}.{$display_id}")) {
      $style_plugin = $this->getPlugin('style');
      // REST exports should only respond to get methods.
      $requirements = [
        '_method' => 'GET',
        '_format' => 'json',
      ];

      // Add the new requirements to the route.
      $route->addRequirements($requirements);
    }
  }

  /**
   * {@inheritdoc}
   */
  public function displaysExposed() {
    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function execute() {
    parent::execute();

    return $this->view->render();
  }

  /**
   * Gets the content type.
   *
   * @return string
   *   The content type machine name. E.g. 'json'.
   */
  public function getContentType() {
    return $this->contentType;
  }

  /**
   * Gets the mime type.
   *
   * This will return any overridden mime type, otherwise returns the mime type
   * from the request.
   *
   * @return string
   *   The response mime type. E.g. 'application/json'.
   */
  public function getMimeType() {
    return $this->mimeType;
  }

  /**
   * {@inheritdoc}
   */
  public function getType() {
    return 'data';
  }

  /**
   * {@inheritdoc}
   */
  public function initDisplay(ViewExecutable $view, array &$display, array &$options = NULL) {
    parent::initDisplay($view, $display, $options);
    $this->setContentType('json');
    $this->setMimeType($this->view->getRequest()
      ->getMimeType($this->contentType));
  }

  /**
   * {@inheritdoc}
   */
  public function optionsSummary(&$categories, &$options) {
    parent::optionsSummary($categories, $options);

    // Hide some settings, as they aren't useful for pure data output.
    unset($categories['page'], $categories['exposed'], $options['show_admin_links'], $options['analyze-theme']);

    $categories['path'] = [
      'title' => $this->t('Path settings'),
      'column' => 'second',
      'build' => [
        '#weight' => -10,
      ],
    ];

    $options['path']['category'] = 'path';
    $options['path']['title'] = $this->t('Path');

    // Remove css/exposed form settings, as they are not used for the data
    // display.
    unset($options['exposed_form'], $options['exposed_block'], $options['css_class']);
  }

  /**
   * {@inheritdoc}
   *
   * The DisplayPluginBase preview method assumes we will be returning a render
   * array. The data plugin will already return the serialized string.
   */
  public function preview() {
    return $this->view->render();
  }

  /**
   * {@inheritdoc}
   */
  public function render() {
    $build = [];
    $build['#markup'] = $this
      ->getRenderer()
      ->executeInRenderContext(
        new RenderContext(),
        function () {
          return $this->view->style_plugin->render();
        }
      );

    $this->view->element['#content_type'] = $this->getMimeType();
    $this->view->element['#cache_properties'][] = '#content_type';

    // Encode and wrap the output in a pre tag if this is for a live preview.    // Wrap the output in a pre tag if this is for a live preview.
    if (!empty($this->view->live_preview)) {
      $build['#prefix'] = '<pre>';
      $build['#plain_text'] = $build['#markup'];
      $build['#suffix'] = '</pre>';
      unset($build['#markup']);
    }
    else {
      // This display plugin is for returning non-HTML formats. However, we
      // still invoke the renderer to collect cacheability metadata. Because the
      // renderer is designed for HTML rendering, it filters #markup for XSS
      // unless it is already known to be safe, but that filter only works for
      // HTML. Therefore, we mark the contents as safe to bypass the filter. So
      // long as we are returning this in a non-HTML response,
      // this is safe, because an XSS attack only works when executed by an HTML
      // agent.
      // @todo Decide how to support non-HTML in the render API in
      //   https://www.drupal.org/node/2501313.
      $build['#markup'] = ViewsRenderPipelineMarkup::create($build['#markup']);
    }

    parent::applyDisplayCacheabilityMetadata($build);

    return $build;
  }

  /**
   * Sets the content type.
   *
   * @param string $content_type
   *   The content type machine name. E.g. 'json'.
   */
  public function setContentType($content_type) {
    $this->contentType = $content_type;
  }

  /**
   * Sets the request content type.
   *
   * @param string $mime_type
   *   The response mime type. E.g. 'application/json'.
   */
  public function setMimeType($mime_type) {
    $this->mimeType = $mime_type;
  }

class GeoJsonExport extends RestExport {
  /**
   * {@inheritdoc}
   */
@@ -304,21 +36,10 @@ class GeoJsonExport extends PathPluginBase implements ResponseDisplayPluginInter
  protected function defineOptions() {
    $options = parent::defineOptions();

    // Set the default style plugin to 'json'.
    // Set the default style plugin to 'geojson'.
    $options['style']['contains']['type']['default'] = 'geojson';
    $options['row']['contains']['type']['default'] = 'data_field';
    $options['defaults']['default']['style'] = FALSE;
    $options['defaults']['default']['row'] = FALSE;

    // Remove css/exposed form settings, as they are not used for the data
    // display.
    unset(
      $options['exposed_form'],
      $options['exposed_block'],
      $options['css_class']
    );

    return $options;
  }

}