Loading src/Plugin/views/display/GeoJsonExport.php +4 −283 Original line number Diff line number Diff line Loading @@ -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 * Loading @@ -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} */ Loading @@ -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; } } Loading
src/Plugin/views/display/GeoJsonExport.php +4 −283 Original line number Diff line number Diff line Loading @@ -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 * Loading @@ -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} */ Loading @@ -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; } }