You are here

class RestExport in Drupal 10

Same name and namespace in other branches
  1. 8 core/modules/rest/src/Plugin/views/display/RestExport.php \Drupal\rest\Plugin\views\display\RestExport
  2. 9 core/modules/rest/src/Plugin/views/display/RestExport.php \Drupal\rest\Plugin\views\display\RestExport

The plugin that handles Data response callbacks for REST resources.

Plugin annotation


@ViewsDisplay(
  id = "rest_export",
  title = @Translation("REST export"),
  help = @Translation("Create a REST export resource."),
  uses_route = TRUE,
  admin = @Translation("REST export"),
  returns_response = TRUE
)

Hierarchy

Expanded class hierarchy of RestExport

Related topics

3 files declare their use of RestExport
CollectRoutesTest.php in core/modules/rest/tests/src/Unit/CollectRoutesTest.php
RestExportTest.php in core/modules/rest/tests/src/Kernel/Views/RestExportTest.php
SerializerTest.php in core/modules/rest/tests/src/Unit/Plugin/views/style/SerializerTest.php

File

core/modules/rest/src/Plugin/views/display/RestExport.php, line 34

Namespace

Drupal\rest\Plugin\views\display
View source
class RestExport extends PathPluginBase implements ResponseDisplayPluginInterface {

  /**
   * {@inheritdoc}
   */
  protected $usesAJAX = FALSE;

  /**
   * {@inheritdoc}
   */
  protected $usesPager = FALSE;

  /**
   * {@inheritdoc}
   */
  protected $usesMore = FALSE;

  /**
   * {@inheritdoc}
   */
  protected $usesAreas = FALSE;

  /**
   * {@inheritdoc}
   */
  protected $usesOptions = FALSE;

  /**
   * 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 = 'application/json';

  /**
   * The renderer.
   *
   * @var \Drupal\Core\Render\RendererInterface
   */
  protected $renderer;

  /**
   * The collector of authentication providers.
   *
   * @var \Drupal\Core\Authentication\AuthenticationCollectorInterface
   */
  protected $authenticationCollector;

  /**
   * The authentication providers, like 'cookie' and 'basic_auth'.
   *
   * @var string[]
   */
  protected $authenticationProviderIds;

  /**
   * The serialization format providers, keyed by format.
   *
   * @var string[]
   */
  protected $formatProviders;

  /**
   * Constructs a RestExport object.
   *
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $plugin_id
   *   The plugin_id for the plugin instance.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
   *   The route provider.
   * @param \Drupal\Core\State\StateInterface $state
   *   The state key value store.
   * @param \Drupal\Core\Render\RendererInterface $renderer
   *   The renderer.
   * @param string[] $authentication_providers
   *   The authentication providers, keyed by ID.
   * @param string[] $serializer_format_providers
   *   The serialization format providers, keyed by format.
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteProviderInterface $route_provider, StateInterface $state, RendererInterface $renderer, array $authentication_providers, array $serializer_format_providers) {
    parent::__construct($configuration, $plugin_id, $plugin_definition, $route_provider, $state);
    $this->renderer = $renderer;

    // $authentication_providers as defined in
    // \Drupal\Core\DependencyInjection\Compiler\AuthenticationProviderPass
    // and as such it is an array, with authentication providers (cookie,
    // basic_auth) as keys and modules providing those as values (user,
    // basic_auth).
    $this->authenticationProviderIds = array_keys($authentication_providers);
    $this->formatProviders = $serializer_format_providers;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static($configuration, $plugin_id, $plugin_definition, $container
      ->get('router.route_provider'), $container
      ->get('state'), $container
      ->get('renderer'), $container
      ->getParameter('authentication_providers'), $container
      ->getParameter('serializer.format_providers'));
  }

  /**
   * {@inheritdoc}
   */
  public function initDisplay(ViewExecutable $view, array &$display, array &$options = NULL) {
    parent::initDisplay($view, $display, $options);

    // If the default 'json' format is not selected as a format option in the
    // view display, fallback to the first format available for the default.
    if (!empty($options['style']['options']['formats']) && !isset($options['style']['options']['formats'][$this
      ->getContentType()])) {
      $default_format = reset($options['style']['options']['formats']);
      $this
        ->setContentType($default_format);
    }

    // Only use the requested content type if it's not 'html'. This allows
    // still falling back to the default for things like views preview.
    $request_content_type = $this->view
      ->getRequest()
      ->getRequestFormat();
    if ($request_content_type !== 'html') {
      $this
        ->setContentType($request_content_type);
    }
    $this
      ->setMimeType($this->view
      ->getRequest()
      ->getMimeType($this
      ->getContentType()));
  }

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

  /**
   * {@inheritdoc}
   */
  public function usesExposed() {
    return TRUE;
  }

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

  /**
   * 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;
  }

  /**
   * 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;
  }

  /**
   * 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;
  }

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

  /**
   * Gets the auth options available.
   *
   * @return string[]
   *   An array to use as value for "#options" in the form element.
   */
  public function getAuthOptions() {
    return array_combine($this->authenticationProviderIds, $this->authenticationProviderIds);
  }

  /**
   * {@inheritdoc}
   */
  protected function defineOptions() {
    $options = parent::defineOptions();

    // Options for REST authentication.
    $options['auth'] = [
      'default' => [],
    ];

    // Set the default style plugin to 'json'.
    $options['style']['contains']['type']['default'] = 'serializer';
    $options['row']['contains']['type']['default'] = 'data_entity';
    $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']);
    unset($options['exposed_block']);
    unset($options['css_class']);
    return $options;
  }

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

    // Authentication.
    $auth = $this
      ->getOption('auth') ? implode(', ', $this
      ->getOption('auth')) : $this
      ->t('No authentication is set');
    unset($categories['page'], $categories['exposed']);

    // Hide some settings, as they aren't useful for pure data output.
    unset($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');
    $options['auth'] = [
      'category' => 'path',
      'title' => $this
        ->t('Authentication'),
      'value' => views_ui_truncate($auth, 24),
    ];

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

  /**
   * {@inheritdoc}
   */
  public function buildOptionsForm(&$form, FormStateInterface $form_state) {
    parent::buildOptionsForm($form, $form_state);
    if ($form_state
      ->get('section') === 'auth') {
      $form['#title'] .= $this
        ->t('The supported authentication methods for this view');
      $form['auth'] = [
        '#type' => 'checkboxes',
        '#title' => $this
          ->t('Authentication methods'),
        '#description' => $this
          ->t('These are the supported authentication providers for this view. When this view is requested, the client will be forced to authenticate with one of the selected providers. Make sure you set the appropriate requirements at the <em>Access</em> section since the Authentication System will fallback to the anonymous user if it fails to authenticate. For example: require Access: Role | Authenticated User.'),
        '#options' => $this
          ->getAuthOptions(),
        '#default_value' => $this
          ->getOption('auth'),
      ];
    }
  }

  /**
   * {@inheritdoc}
   */
  public function submitOptionsForm(&$form, FormStateInterface $form_state) {
    parent::submitOptionsForm($form, $form_state);
    if ($form_state
      ->get('section') == 'auth') {
      $this
        ->setOption('auth', array_keys(array_filter($form_state
        ->getValue('auth'))));
    }
  }

  /**
   * {@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.
      $route
        ->setMethods([
        'GET',
      ]);
      $formats = $style_plugin
        ->getFormats();

      // If there are no configured formats, add all formats that serialization
      // is known to support.
      if (!$formats) {
        $formats = $this
          ->getFormatOptions();
      }

      // Format as a string using pipes as a delimiter.
      $route
        ->setRequirement('_format', implode('|', $formats));

      // Add authentication to the route if it was set. If no authentication was
      // set, the default authentication will be used, which is cookie based by
      // default.
      $auth = $this
        ->getOption('auth');
      if (!empty($auth)) {
        $route
          ->setOption('_auth', $auth);
      }
    }
  }

  /**
   * Determines whether the view overrides the given route.
   *
   * @param string $view_path
   *   The path of the view.
   * @param \Symfony\Component\Routing\Route $view_route
   *   The route of the view.
   * @param \Symfony\Component\Routing\Route $route
   *   The route itself.
   *
   * @return bool
   *   TRUE, when the view should override the given route.
   */
  protected function overrideApplies($view_path, Route $view_route, Route $route) {
    $route_has_format = $route
      ->hasRequirement('_format');
    $route_formats = $route_has_format ? explode('|', $route
      ->getRequirement('_format')) : [];
    $view_route_formats = $view_route
      ->hasRequirement('_format') ? explode('|', $view_route
      ->getRequirement('_format')) : [];
    return $this
      ->overrideAppliesPathAndMethod($view_path, $view_route, $route) && (!$route_has_format || array_intersect($route_formats, $view_route_formats) != []);
  }

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

    // Setup an empty response so headers can be added as needed during views
    // rendering and processing.
    $response = new CacheableResponse('', 200);
    $build['#response'] = $response;

    /** @var \Drupal\Core\Render\RendererInterface $renderer */
    $renderer = \Drupal::service('renderer');
    $output = (string) $renderer
      ->renderRoot($build);
    $response
      ->setContent($output);
    $cache_metadata = CacheableMetadata::createFromRenderArray($build);
    $response
      ->addCacheableDependency($cache_metadata);
    $response->headers
      ->set('Content-type', $build['#content_type']);
    return $response;
  }

  /**
   * {@inheritdoc}
   */
  public function execute() {
    parent::execute();
    return $this->view
      ->render();
  }

  /**
   * {@inheritdoc}
   */
  public function render() {
    $build = [];
    $build['#markup'] = $this->renderer
      ->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.
    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;
  }

  /**
   * {@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();
  }

  /**
   * Returns an array of format options.
   *
   * @return string[]
   *   An array of format options. Both key and value are the same.
   */
  protected function getFormatOptions() {
    $formats = array_keys($this->formatProviders);
    return array_combine($formats, $formats);
  }

}

Members

Namesort descending Modifiers Type Description Overrides
DisplayPluginInterface::acceptAttachments public function Determines whether this display can use attachments.
DisplayPluginInterface::access public function Determines if the user has access to this display of the view.
DisplayPluginInterface::ajaxEnabled public function Whether the display is actually using AJAX or not.
DisplayPluginInterface::attachTo public function Allows displays to attach to other views. 1
DisplayPluginInterface::buildBasicRenderable public static function Builds a basic render array which can be properly render cached. 1
DisplayPluginInterface::buildRenderable public function Builds a renderable array of the view.
DisplayPluginInterface::calculateCacheMetadata public function Calculates the display's cache metadata by inspecting each handler/plugin.
DisplayPluginInterface::defaultableSections public function Lists the 'defaultable' sections and what items each section contains. 1
DisplayPluginInterface::destroy public function Destroys the display's components and the display itself.
DisplayPluginInterface::elementPreRender public function #pre_render callback for view display rendering.
DisplayPluginInterface::getArgumentsTokens public function Returns to tokens for arguments.
DisplayPluginInterface::getArgumentText public function Provides help text for the arguments. 1
DisplayPluginInterface::getAttachedDisplays public function Find out all displays which are attached to this display.
DisplayPluginInterface::getCacheMetadata public function Gets the cache metadata.
DisplayPluginInterface::getExtenders public function Gets the display extenders.
DisplayPluginInterface::getFieldLabels public function Retrieves a list of fields for the current display.
DisplayPluginInterface::getHandler public function Get the handler object for a single handler.
DisplayPluginInterface::getHandlers public function Get a full array of handlers for $type. This caches them.
DisplayPluginInterface::getLinkDisplay public function Returns the ID of the display to use when making links.
DisplayPluginInterface::getOption public function Gets an option, from this display or the default display.
DisplayPluginInterface::getPagerText public function Provides help text for pagers. 1
DisplayPluginInterface::getPlugin public function Get the instance of a plugin, for example style or row.
DisplayPluginInterface::getRoutedDisplay public function Points to the display which can be linked by this display.
DisplayPluginInterface::getSpecialBlocks public function Provides the block system with any exposed widget blocks for this display.
DisplayPluginInterface::getUrl public function Returns a URL to $this display or its configured linked display.
DisplayPluginInterface::isDefaultDisplay public function Determines if this display is the 'default' display.
DisplayPluginInterface::isDefaulted public function Determines if an option is set to use the default or current display.
DisplayPluginInterface::isEnabled public function Whether the display is enabled.
DisplayPluginInterface::isIdentifierUnique public function Checks if the provided identifier is unique.
DisplayPluginInterface::isMoreEnabled public function Whether the display is using the 'more' link or not.
DisplayPluginInterface::isPagerEnabled public function Whether the display is using a pager or not.
DisplayPluginInterface::mergeDefaults public function Merges default values for all plugin types.
DisplayPluginInterface::newDisplay public function Reacts on adding a display. 1
DisplayPluginInterface::optionLink public function Returns a link to a section of a form.
DisplayPluginInterface::optionsOverride public function If override/revert was clicked, perform the proper toggle.
DisplayPluginInterface::outputIsEmpty public function Is the output of the view empty.
DisplayPluginInterface::overrideOption public function Set an option and force it to be an override.
DisplayPluginInterface::preExecute public function Sets up any variables on the view prior to execution.
DisplayPluginInterface::query public function Injects anything into the query that the display handler needs.
DisplayPluginInterface::renderArea public function Renders one of the available areas.
DisplayPluginInterface::renderFilters public function Does nothing (obsolete function).
DisplayPluginInterface::renderMoreLink public function Renders the 'more' link.
DisplayPluginInterface::renderPager public function Checks to see if the display plugins support pager rendering.
DisplayPluginInterface::setOption public function Sets an option, on this display or the default display.
DisplayPluginInterface::setOverride public function Flip the override setting for the given section.
DisplayPluginInterface::useGroupBy public function Does the display have groupby enabled?
DisplayPluginInterface::useMoreAlways public function Should the enabled display more link be shown when no more items?
DisplayPluginInterface::useMoreText public function Does the display have custom link text?
DisplayPluginInterface::usesAJAX public function Whether the display allows the use of AJAX or not.
DisplayPluginInterface::usesAreas public function Returns whether the display can use areas.
DisplayPluginInterface::usesAttachments public function Returns whether the display can use attachments.
DisplayPluginInterface::usesExposedFormInBlock public function Checks to see if the display can put the exposed form in a block.
DisplayPluginInterface::usesFields public function Determines if the display's style uses fields.
DisplayPluginInterface::usesLinkDisplay public function Checks to see if the display has some need to link to another display. 1
DisplayPluginInterface::usesMore public function Whether the display allows the use of a 'more' link or not.
DisplayPluginInterface::usesPager public function Whether the display allows the use of a pager or not.
DisplayPluginInterface::viewExposedFormBlocks public function Renders the exposed form as block.
PathPluginBase::$routeProvider protected property The route provider.
PathPluginBase::$state protected property The state key value store.
PathPluginBase::alterRoutes public function Alters a collection of routes and replaces definitions to the view. Overrides DisplayRouterInterface::alterRoutes
PathPluginBase::getAlteredRouteNames public function Returns the list of routes overridden by Views. Overrides DisplayRouterInterface::getAlteredRouteNames
PathPluginBase::getMenuLinks public function Gets menu links, if this display provides some. Overrides DisplayMenuInterface::getMenuLinks
PathPluginBase::getPath public function Returns the base path to use for this display. Overrides DisplayPluginInterface::getPath
PathPluginBase::getRoute protected function Generates a route entry for a given view and display. 1
PathPluginBase::getRouteName public function Returns the route name for the display. Overrides DisplayRouterInterface::getRouteName
PathPluginBase::getUrlInfo public function Generates a URL to this display. Overrides DisplayRouterInterface::getUrlInfo
PathPluginBase::hasPath public function Checks to see if the display has a 'path' field. Overrides DisplayPluginInterface::hasPath
PathPluginBase::isDefaultTabPath protected function Determines if this display's path is a default tab.
PathPluginBase::overrideAppliesPathAndMethod protected function Determines whether an override for the path and method should happen.
PathPluginBase::remove public function Reacts on deleting a display. Overrides DisplayPluginInterface::remove
PathPluginBase::validate public function Make sure the display and all associated handlers are valid. Overrides DisplayPluginInterface::validate 1
PathPluginBase::validateOptionsForm public function Validates the options form. Overrides DisplayPluginInterface::validateOptionsForm 1
PathPluginBase::validatePath protected function Validates the path of the display.
RestExport::$authenticationCollector protected property The collector of authentication providers.
RestExport::$authenticationProviderIds protected property The authentication providers, like 'cookie' and 'basic_auth'.
RestExport::$contentType protected property Overrides the content type of the data response, if needed.
RestExport::$formatProviders protected property The serialization format providers, keyed by format.
RestExport::$mimeType protected property The mime type for the response.
RestExport::$renderer protected property The renderer.
RestExport::$usesAJAX protected property
RestExport::$usesAreas protected property
RestExport::$usesMore protected property
RestExport::$usesOptions protected property
RestExport::$usesPager protected property
RestExport::buildOptionsForm public function Provides the default form for setting options. Overrides PathPluginBase::buildOptionsForm
RestExport::buildResponse public static function Builds up a response with the rendered view as content. Overrides ResponseDisplayPluginInterface::buildResponse
RestExport::collectRoutes public function Adds the route entry of a view to the collection. Overrides PathPluginBase::collectRoutes
RestExport::create public static function Overrides PathPluginBase::create
RestExport::defineOptions protected function Overrides \Drupal\views\Plugin\views\display\DisplayPluginBase:defineOptions(). Overrides PathPluginBase::defineOptions
RestExport::displaysExposed public function Determines if this display should display the exposed filters widgets. Overrides DisplayPluginInterface::displaysExposed
RestExport::execute public function Executes the view and returns data in the format required. Overrides PathPluginBase::execute
RestExport::getAuthOptions public function Gets the auth options available.
RestExport::getContentType public function Gets the content type.
RestExport::getFormatOptions protected function Returns an array of format options.
RestExport::getMimeType public function Gets the mime type.
RestExport::getType public function Returns the display type that this display requires. Overrides DisplayPluginInterface::getType
RestExport::initDisplay public function Initializes the display plugin. Overrides DisplayPluginInterface::initDisplay
RestExport::optionsSummary public function Provides the default summary for options in the views UI. Overrides PathPluginBase::optionsSummary
RestExport::overrideApplies protected function Determines whether the view overrides the given route. Overrides PathPluginBase::overrideApplies
RestExport::preview public function The DisplayPluginBase preview method assumes we will be returning a render array. The data plugin will already return the serialized string. Overrides DisplayPluginInterface::preview
RestExport::render public function Renders this display. Overrides DisplayPluginInterface::render
RestExport::setContentType public function Sets the content type.
RestExport::setMimeType public function Sets the request content type.
RestExport::submitOptionsForm public function Performs any necessary changes to the form values prior to storage. Overrides PathPluginBase::submitOptionsForm
RestExport::usesExposed public function Determines if this display uses exposed filters. Overrides DisplayPluginInterface::usesExposed
RestExport::__construct public function Constructs a RestExport object. Overrides PathPluginBase::__construct