JsonapiHooks.php

Namespace

Drupal\jsonapi\Hook

File

core/modules/jsonapi/src/Hook/JsonapiHooks.php

View source
<?php
namespace Drupal\jsonapi\Hook;
use Drupal\Core\Access\AccessResult ;
use Drupal\Core\Session\AccountInterface ;
use Drupal\Core\Entity\EntityTypeInterface ;
use Drupal\Core\Entity\EntityInterface ;
use Drupal\Core\StringTranslation\StringTranslationTrait ;
use Drupal\jsonapi\JsonApiFilter ;
use Drupal\jsonapi\Routing\Routes ;
use Drupal\Core\Routing\RouteMatchInterface ;
use Drupal\Core\Hook\Attribute\Hook ;

/**
 * Hook implementations for jsonapi.
 */
class JsonapiHooks  {
 use StringTranslationTrait;
 
 /**
 * Implements hook_help().
 */
 public function help($route_name, RouteMatchInterface $route_match) : ?string {
 switch ($route_name) {
 case 'help.page.jsonapi':
 $output = '<h2>' . $this->t('About') . '</h2>';
 $output .= '<p>' . $this->t('The JSON:API module is a fully compliant implementation of the <a href=":spec">JSON:API Specification</a>. By following shared conventions, you can increase productivity, take advantage of generalized tooling, and focus on what matters: your application. Clients built around JSON:API are able to take advantage of features like efficient response caching, which can sometimes eliminate network requests entirely. For more information, see the <a href=":docs">online documentation for the JSON:API module</a>.', [
 ':spec' => 'https://jsonapi.org',
 ':docs' => 'https://www.drupal.org/docs/8/modules/json-api',
 ]) . '</p>';
 $output .= '<dl>';
 $output .= '<dt>' . $this->t('General') . '</dt>';
 $output .= '<dd>' . $this->t('JSON:API is a particular implementation of REST that provides conventions for resource relationships, collections, filters, pagination, and sorting. These conventions help developers build clients faster and encourages reuse of code.') . '</dd>';
 $output .= '<dd>' . $this->t('The <a href=":jsonapi-docs">JSON:API</a> and <a href=":rest-docs">RESTful Web Services</a> modules serve similar purposes. <a href=":comparison">Read the comparison of the RESTFul Web Services and JSON:API modules</a> to determine the best choice for your site.', [
 ':jsonapi-docs' => 'https://www.drupal.org/docs/8/modules/json-api',
 ':rest-docs' => 'https://www.drupal.org/docs/8/core/modules/rest',
 ':comparison' => 'https://www.drupal.org/docs/8/modules/jsonapi/jsonapi-vs-cores-rest-module',
 ]) . '</dd>';
 $output .= '<dd>' . $this->t('Some multilingual features currently do not work well with JSON:API. See the <a href=":jsonapi-docs">JSON:API multilingual support documentation</a> for more information on the current status of multilingual support.', [
 ':jsonapi-docs' => 'https://www.drupal.org/docs/8/modules/jsonapi/translations',
 ]) . '</dd>';
 $output .= '<dd>' . $this->t('Revision support is currently read-only and only for the "Content" and "Media" entity types in JSON:API. See the <a href=":jsonapi-docs">JSON:API revision support documentation</a> for more information on the current status of revision support.', [
 ':jsonapi-docs' => 'https://www.drupal.org/docs/8/modules/jsonapi/revisions',
 ]) . '</dd>';
 $output .= '</dl>';
 return $output;
 }
 return NULL;
 }
 
 /**
 * Implements hook_modules_installed().
 */
 public function modulesInstalled($modules) : void {
 $potential_conflicts = [
 'content_translation',
 'config_translation',
 'language',
 ];
 if (!empty(array_intersect ($modules, $potential_conflicts))) {
 \Drupal ::messenger ()->addWarning ($this->t('Some multilingual features currently do not work well with JSON:API. See the <a href=":jsonapi-docs">JSON:API multilingual support documentation</a> for more information on the current status of multilingual support.', [
 ':jsonapi-docs' => 'https://www.drupal.org/docs/8/modules/jsonapi/translations',
 ]));
 }
 }
 
 /**
 * Implements hook_entity_bundle_create().
 */
 public function entityBundleCreate() : void {
 Routes ::rebuild ();
 }
 
 /**
 * Implements hook_entity_bundle_delete().
 */
 public function entityBundleDelete() : void {
 Routes ::rebuild ();
 }
 
 /**
 * Implements hook_entity_create().
 */
 public function entityCreate(EntityInterface $entity) : void {
 if (in_array ($entity->getEntityTypeId (), [
 'field_storage_config',
 'field_config',
 ])) {
 // @todo Only do this when relationship fields are updated, not just any field.
 Routes ::rebuild ();
 }
 }
 
 /**
 * Implements hook_entity_delete().
 */
 public function entityDelete(EntityInterface $entity) : void {
 if (in_array ($entity->getEntityTypeId (), [
 'field_storage_config',
 'field_config',
 ])) {
 // @todo Only do this when relationship fields are updated, not just any field.
 Routes ::rebuild ();
 }
 }
 
 /**
 * Implements hook_jsonapi_entity_filter_access().
 */
 public function jsonapiEntityFilterAccess(EntityTypeInterface $entity_type, AccountInterface $account) : array {
 // All core entity types and most or all contrib entity types allow users
 // with the entity type's administrative permission to view all of the
 // entities, so enable similarly permissive filtering to those users as
 // well. A contrib module may override this decision by returning
 // AccessResult::forbidden() from its implementation of this hook.
 if ($admin_permission = $entity_type->getAdminPermission ()) {
 return [
 JsonApiFilter::AMONG_ALL => AccessResult ::allowedIfHasPermission ($account, $admin_permission),
 ];
 }
 return [];
 }
 
 /**
 * Implements hook_jsonapi_ENTITY_TYPE_filter_access() for 'block_content'.
 */
 public function jsonapiBlockContentFilterAccess(EntityTypeInterface $entity_type, AccountInterface $account) : array {
 // @see \Drupal\block_content\BlockContentAccessControlHandler::checkAccess()
 // \Drupal\jsonapi\Access\TemporaryQueryGuard adds the condition for
 // (isReusable()), so this does not have to.
 return [
 JsonApiFilter::AMONG_ALL => AccessResult ::allowedIfHasPermission ($account, 'access block library'),
 JsonApiFilter::AMONG_PUBLISHED => AccessResult ::allowed (),
 ];
 }
 
 /**
 * Implements hook_jsonapi_ENTITY_TYPE_filter_access() for 'comment'.
 */
 public function jsonapiCommentFilterAccess(EntityTypeInterface $entity_type, AccountInterface $account) : array {
 // @see \Drupal\comment\CommentAccessControlHandler::checkAccess()
 // \Drupal\jsonapi\Access\TemporaryQueryGuard adds the condition for
 // (access to the commented entity), so this does not have to.
 return [
 JsonApiFilter::AMONG_ALL => AccessResult ::allowedIfHasPermission ($account, 'administer comments'),
 JsonApiFilter::AMONG_PUBLISHED => AccessResult ::allowedIfHasPermission ($account, 'access comments'),
 ];
 }
 
 /**
 * Implements hook_jsonapi_ENTITY_TYPE_filter_access() for 'entity_test'.
 */
 public function jsonapiEntityTestFilterAccess(EntityTypeInterface $entity_type, AccountInterface $account) : array {
 // @see \Drupal\entity_test\EntityTestAccessControlHandler::checkAccess()
 return [
 JsonApiFilter::AMONG_ALL => AccessResult ::allowedIfHasPermission ($account, 'view test entity'),
 ];
 }
 
 /**
 * Implements hook_jsonapi_ENTITY_TYPE_filter_access() for 'file'.
 */
 public function jsonapiFileFilterAccess(EntityTypeInterface $entity_type, AccountInterface $account) : array {
 // @see \Drupal\file\FileAccessControlHandler::checkAccess()
 // \Drupal\jsonapi\Access\TemporaryQueryGuard adds the condition for
 // (public OR owner), so this does not have to.
 return [
 JsonApiFilter::AMONG_ALL => AccessResult ::allowedIfHasPermission ($account, 'access content'),
 ];
 }
 
 /**
 * Implements hook_jsonapi_ENTITY_TYPE_filter_access() for 'media'.
 */
 public function jsonapiMediaFilterAccess(EntityTypeInterface $entity_type, AccountInterface $account) : array {
 // @see \Drupal\media\MediaAccessControlHandler::checkAccess()
 return [
 JsonApiFilter::AMONG_PUBLISHED => AccessResult ::allowedIfHasPermission ($account, 'view media'),
 ];
 }
 
 /**
 * Implements hook_jsonapi_ENTITY_TYPE_filter_access() for 'node'.
 */
 public function jsonapiNodeFilterAccess(EntityTypeInterface $entity_type, AccountInterface $account) : array {
 // @see \Drupal\node\NodeAccessControlHandler::access()
 if ($account->hasPermission ('bypass node access')) {
 return [
 JsonApiFilter::AMONG_ALL => AccessResult ::allowed ()->cachePerPermissions (),
 ];
 }
 if (!$account->hasPermission ('access content')) {
 $forbidden = AccessResult ::forbidden ("The 'access content' permission is required.")->cachePerPermissions ();
 return [
 JsonApiFilter::AMONG_ALL => $forbidden,
 JsonApiFilter::AMONG_OWN => $forbidden,
 JsonApiFilter::AMONG_PUBLISHED => $forbidden,
 // For legacy reasons, the Node entity type has a "status" key, so
 // forbid this subset as well, even though it has no semantic meaning.
JsonApiFilter::AMONG_ENABLED => $forbidden,
 ];
 }
 return [
 // @see \Drupal\node\NodeAccessControlHandler::checkAccess()
JsonApiFilter::AMONG_OWN => AccessResult ::allowedIfHasPermission ($account, 'view own unpublished content'),
 // @see \Drupal\node\NodeGrantDatabaseStorage::access()
 // Note that:
 // - This is just for the default grant. Other node access conditions
 // are added via the 'node_access' query tag.
 // - Permissions were checked earlier in this function, so we must
 // vary the cache by them.
JsonApiFilter::AMONG_PUBLISHED => AccessResult ::allowed ()->cachePerPermissions (),
 ];
 }
 
 /**
 * Implements hook_jsonapi_ENTITY_TYPE_filter_access() for 'shortcut'.
 */
 public function jsonapiShortcutFilterAccess(EntityTypeInterface $entity_type, AccountInterface $account) : array {
 // @see \Drupal\shortcut\ShortcutAccessControlHandler::checkAccess()
 // \Drupal\jsonapi\Access\TemporaryQueryGuard adds the condition for
 // "shortcut_set = $shortcut_set_storage->getDisplayedToUser($current_user)"
 // so this does not have to.
 return [
 JsonApiFilter::AMONG_ALL => AccessResult ::allowedIfHasPermission ($account, 'administer shortcuts')->orIf (AccessResult ::allowedIfHasPermissions ($account, [
 'access shortcuts',
 'customize shortcut links',
 ])),
 ];
 }
 
 /**
 * Implements hook_jsonapi_ENTITY_TYPE_filter_access() for 'taxonomy_term'.
 */
 public function jsonapiTaxonomyTermFilterAccess(EntityTypeInterface $entity_type, AccountInterface $account) : array {
 // @see \Drupal\taxonomy\TermAccessControlHandler::checkAccess()
 return [
 JsonApiFilter::AMONG_ALL => AccessResult ::allowedIfHasPermission ($account, 'administer taxonomy'),
 JsonApiFilter::AMONG_PUBLISHED => AccessResult ::allowedIfHasPermission ($account, 'access content'),
 ];
 }
 
 /**
 * Implements hook_jsonapi_ENTITY_TYPE_filter_access() for 'user'.
 */
 public function jsonapiUserFilterAccess(EntityTypeInterface $entity_type, AccountInterface $account) : array {
 // @see \Drupal\user\UserAccessControlHandler::checkAccess()
 // \Drupal\jsonapi\Access\TemporaryQueryGuard adds the condition for
 // (!isAnonymous()), so this does not have to.
 return [
 JsonApiFilter::AMONG_OWN => AccessResult ::allowed (),
 JsonApiFilter::AMONG_ENABLED => AccessResult ::allowedIfHasPermission ($account, 'access user profiles'),
 ];
 }
 
 /**
 * Implements hook_jsonapi_ENTITY_TYPE_filter_access() for 'workspace'.
 */
 public function jsonapiWorkspaceFilterAccess(EntityTypeInterface $entity_type, AccountInterface $account) : array {
 // @see \Drupal\workspaces\WorkspaceAccessControlHandler::checkAccess()
 return [
 JsonApiFilter::AMONG_ALL => AccessResult ::allowedIfHasPermission ($account, 'view any workspace'),
 JsonApiFilter::AMONG_OWN => AccessResult ::allowedIfHasPermission ($account, 'view own workspace'),
 ];
 }
}

Classes

Title Deprecated Summary
JsonapiHooks Hook implementations for jsonapi.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.