Chris@18: ' . t('About') . '';
Chris@18: $output .= '
' . t('The JSON:API module is a fully compliant implementation of the JSON:API Specification. 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 online documentation for the JSON:API module.', [
Chris@18: ':spec' => 'https://jsonapi.org',
Chris@18: ':docs' => 'https://www.drupal.org/docs/8/modules/json-api',
Chris@18: ]) . '
';
Chris@18: $output .= '';
Chris@18: $output .= '- ' . t('General') . '
';
Chris@18: $output .= '- ' . 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.') . '
';
Chris@18: $output .= '- ' . t('The JSON:API and RESTful Web Services modules serve similar purposes. Read the comparison of the RESTFul Web Services and JSON:API modules to determine the best choice for your site.', [
Chris@18: ':jsonapi-docs' => 'https://www.drupal.org/docs/8/modules/json-api',
Chris@18: ':rest-docs' => 'https://www.drupal.org/docs/8/core/modules/rest',
Chris@18: ':comparison' => 'https://www.drupal.org/docs/8/modules/jsonapi/jsonapi-vs-cores-rest-module',
Chris@18: ]) . '
';
Chris@18: $output .= '- ' . t('Some multilingual features currently do not work well with JSON:API. See the JSON:API multilingual support documentation for more information on the current status of multilingual support.', [
Chris@18: ':jsonapi-docs' => 'https://www.drupal.org/docs/8/modules/jsonapi/translations',
Chris@18: ]) . '
';
Chris@18: $output .= '- ' . t('Revision support is currently read-only and only for the "Content" and "Media" entity types in JSON:API. See the JSON:API revision support documentation for more information on the current status of revision support.', [
Chris@18: ':jsonapi-docs' => 'https://www.drupal.org/docs/8/modules/jsonapi/revisions',
Chris@18: ]) . '
';
Chris@18: $output .= '
';
Chris@18:
Chris@18: return $output;
Chris@18: }
Chris@18: return NULL;
Chris@18: }
Chris@18:
Chris@18: /**
Chris@18: * Implements hook_modules_installed().
Chris@18: */
Chris@18: function jsonapi_modules_installed($modules) {
Chris@18: $potential_conflicts = [
Chris@18: 'content_translation',
Chris@18: 'config_translation',
Chris@18: 'language',
Chris@18: ];
Chris@18: if (!empty(array_intersect($modules, $potential_conflicts))) {
Chris@18: \Drupal::messenger()->addWarning(t('Some multilingual features currently do not work well with JSON:API. See the JSON:API multilingual support documentation for more information on the current status of multilingual support.', [
Chris@18: ':jsonapi-docs' => 'https://www.drupal.org/docs/8/modules/jsonapi/translations',
Chris@18: ]));
Chris@18: }
Chris@18: }
Chris@18:
Chris@18: /**
Chris@18: * Implements hook_entity_bundle_create().
Chris@18: */
Chris@18: function jsonapi_entity_bundle_create() {
Chris@18: JsonApiRoutes::rebuild();
Chris@18: }
Chris@18:
Chris@18: /**
Chris@18: * Implements hook_entity_bundle_delete().
Chris@18: */
Chris@18: function jsonapi_entity_bundle_delete() {
Chris@18: JsonApiRoutes::rebuild();
Chris@18: }
Chris@18:
Chris@18: /**
Chris@18: * Implements hook_entity_create().
Chris@18: */
Chris@18: function jsonapi_entity_create(EntityInterface $entity) {
Chris@18: if (in_array($entity->getEntityTypeId(), ['field_storage_config', 'field_config'])) {
Chris@18: // @todo: only do this when relationship fields are updated, not just any field.
Chris@18: JsonApiRoutes::rebuild();
Chris@18: }
Chris@18: }
Chris@18:
Chris@18: /**
Chris@18: * Implements hook_entity_delete().
Chris@18: */
Chris@18: function jsonapi_entity_delete(EntityInterface $entity) {
Chris@18: if (in_array($entity->getEntityTypeId(), ['field_storage_config', 'field_config'])) {
Chris@18: // @todo: only do this when relationship fields are updated, not just any field.
Chris@18: JsonApiRoutes::rebuild();
Chris@18: }
Chris@18: }
Chris@18:
Chris@18: /**
Chris@18: * Implements hook_jsonapi_entity_filter_access().
Chris@18: */
Chris@18: function jsonapi_jsonapi_entity_filter_access(EntityTypeInterface $entity_type, AccountInterface $account) {
Chris@18: // All core entity types and most or all contrib entity types allow users
Chris@18: // with the entity type's administrative permission to view all of the
Chris@18: // entities, so enable similarly permissive filtering to those users as well.
Chris@18: // A contrib module may override this decision by returning
Chris@18: // AccessResult::forbidden() from its implementation of this hook.
Chris@18: if ($admin_permission = $entity_type->getAdminPermission()) {
Chris@18: return ([
Chris@18: JSONAPI_FILTER_AMONG_ALL => AccessResult::allowedIfHasPermission($account, $admin_permission),
Chris@18: ]);
Chris@18: }
Chris@18: }
Chris@18:
Chris@18: /**
Chris@18: * Implements hook_jsonapi_ENTITY_TYPE_filter_access() for 'aggregator_feed'.
Chris@18: */
Chris@18: function jsonapi_jsonapi_aggregator_feed_filter_access(EntityTypeInterface $entity_type, AccountInterface $account) {
Chris@18: // @see \Drupal\aggregator\FeedAccessControlHandler::checkAccess()
Chris@18: return ([
Chris@18: JSONAPI_FILTER_AMONG_ALL => AccessResult::allowedIfHasPermission($account, 'access news feeds'),
Chris@18: ]);
Chris@18: }
Chris@18:
Chris@18: /**
Chris@18: * Implements hook_jsonapi_ENTITY_TYPE_filter_access() for 'block_content'.
Chris@18: */
Chris@18: function jsonapi_jsonapi_block_content_filter_access(EntityTypeInterface $entity_type, AccountInterface $account) {
Chris@18: // @see \Drupal\block_content\BlockContentAccessControlHandler::checkAccess()
Chris@18: // \Drupal\jsonapi\Access\TemporaryQueryGuard adds the condition for
Chris@18: // (isReusable()), so this does not have to.
Chris@18: return ([
Chris@18: JSONAPI_FILTER_AMONG_PUBLISHED => AccessResult::allowed(),
Chris@18: ]);
Chris@18: }
Chris@18:
Chris@18: /**
Chris@18: * Implements hook_jsonapi_ENTITY_TYPE_filter_access() for 'comment'.
Chris@18: */
Chris@18: function jsonapi_jsonapi_comment_filter_access(EntityTypeInterface $entity_type, AccountInterface $account) {
Chris@18: // @see \Drupal\comment\CommentAccessControlHandler::checkAccess()
Chris@18: // \Drupal\jsonapi\Access\TemporaryQueryGuard adds the condition for
Chris@18: // (access to the commented entity), so this does not have to.
Chris@18: return ([
Chris@18: JSONAPI_FILTER_AMONG_ALL => AccessResult::allowedIfHasPermission($account, 'administer comments'),
Chris@18: JSONAPI_FILTER_AMONG_PUBLISHED => AccessResult::allowedIfHasPermission($account, 'access comments'),
Chris@18: ]);
Chris@18: }
Chris@18:
Chris@18: /**
Chris@18: * Implements hook_jsonapi_ENTITY_TYPE_filter_access() for 'entity_test'.
Chris@18: */
Chris@18: function jsonapi_jsonapi_entity_test_filter_access(EntityTypeInterface $entity_type, AccountInterface $account) {
Chris@18: // @see \Drupal\entity_test\EntityTestAccessControlHandler::checkAccess()
Chris@18: return ([
Chris@18: JSONAPI_FILTER_AMONG_ALL => AccessResult::allowedIfHasPermission($account, 'view test entity'),
Chris@18: ]);
Chris@18: }
Chris@18:
Chris@18: /**
Chris@18: * Implements hook_jsonapi_ENTITY_TYPE_filter_access() for 'file'.
Chris@18: */
Chris@18: function jsonapi_jsonapi_file_filter_access(EntityTypeInterface $entity_type, AccountInterface $account) {
Chris@18: // @see \Drupal\file\FileAccessControlHandler::checkAccess()
Chris@18: // \Drupal\jsonapi\Access\TemporaryQueryGuard adds the condition for
Chris@18: // (public OR owner), so this does not have to.
Chris@18: return ([
Chris@18: JSONAPI_FILTER_AMONG_ALL => AccessResult::allowedIfHasPermission($account, 'access content'),
Chris@18: ]);
Chris@18: }
Chris@18:
Chris@18: /**
Chris@18: * Implements hook_jsonapi_ENTITY_TYPE_filter_access() for 'media'.
Chris@18: */
Chris@18: function jsonapi_jsonapi_media_filter_access(EntityTypeInterface $entity_type, AccountInterface $account) {
Chris@18: // @see \Drupal\media\MediaAccessControlHandler::checkAccess()
Chris@18: return ([
Chris@18: JSONAPI_FILTER_AMONG_PUBLISHED => AccessResult::allowedIfHasPermission($account, 'view media'),
Chris@18: ]);
Chris@18: }
Chris@18:
Chris@18: /**
Chris@18: * Implements hook_jsonapi_ENTITY_TYPE_filter_access() for 'node'.
Chris@18: */
Chris@18: function jsonapi_jsonapi_node_filter_access(EntityTypeInterface $entity_type, AccountInterface $account) {
Chris@18: // @see \Drupal\node\NodeAccessControlHandler::access()
Chris@18: if ($account->hasPermission('bypass node access')) {
Chris@18: return ([
Chris@18: JSONAPI_FILTER_AMONG_ALL => AccessResult::allowed()->cachePerPermissions(),
Chris@18: ]);
Chris@18: }
Chris@18: if (!$account->hasPermission('access content')) {
Chris@18: $forbidden = AccessResult::forbidden("The 'access content' permission is required.")->cachePerPermissions();
Chris@18: return ([
Chris@18: JSONAPI_FILTER_AMONG_ALL => $forbidden,
Chris@18: JSONAPI_FILTER_AMONG_OWN => $forbidden,
Chris@18: JSONAPI_FILTER_AMONG_PUBLISHED => $forbidden,
Chris@18: // For legacy reasons, the Node entity type has a "status" key, so forbid
Chris@18: // this subset as well, even though it has no semantic meaning.
Chris@18: JSONAPI_FILTER_AMONG_ENABLED => $forbidden,
Chris@18: ]);
Chris@18: }
Chris@18:
Chris@18: return ([
Chris@18: // @see \Drupal\node\NodeAccessControlHandler::checkAccess()
Chris@18: JSONAPI_FILTER_AMONG_OWN => AccessResult::allowedIfHasPermission($account, 'view own unpublished content'),
Chris@18:
Chris@18: // @see \Drupal\node\NodeGrantDatabaseStorage::access()
Chris@18: // Note that:
Chris@18: // - This is just for the default grant. Other node access conditions are
Chris@18: // added via the 'node_access' query tag.
Chris@18: // - Permissions were checked earlier in this function, so we must vary the
Chris@18: // cache by them.
Chris@18: JSONAPI_FILTER_AMONG_PUBLISHED => AccessResult::allowed()->cachePerPermissions(),
Chris@18: ]);
Chris@18: }
Chris@18:
Chris@18: /**
Chris@18: * Implements hook_jsonapi_ENTITY_TYPE_filter_access() for 'shortcut'.
Chris@18: */
Chris@18: function jsonapi_jsonapi_shortcut_filter_access(EntityTypeInterface $entity_type, AccountInterface $account) {
Chris@18: // @see \Drupal\shortcut\ShortcutAccessControlHandler::checkAccess()
Chris@18: // \Drupal\jsonapi\Access\TemporaryQueryGuard adds the condition for
Chris@18: // (shortcut_set = shortcut_current_displayed_set()), so this does not have
Chris@18: // to.
Chris@18: return ([
Chris@18: JSONAPI_FILTER_AMONG_ALL => AccessResult::allowedIfHasPermission($account, 'administer shortcuts')
Chris@18: ->orIf(AccessResult::allowedIfHasPermissions($account, ['access shortcuts', 'customize shortcut links'])),
Chris@18: ]);
Chris@18: }
Chris@18:
Chris@18: /**
Chris@18: * Implements hook_jsonapi_ENTITY_TYPE_filter_access() for 'taxonomy_term'.
Chris@18: */
Chris@18: function jsonapi_jsonapi_taxonomy_term_filter_access(EntityTypeInterface $entity_type, AccountInterface $account) {
Chris@18: // @see \Drupal\taxonomy\TermAccessControlHandler::checkAccess()
Chris@18: return ([
Chris@18: JSONAPI_FILTER_AMONG_ALL => AccessResult::allowedIfHasPermission($account, 'administer taxonomy'),
Chris@18: JSONAPI_FILTER_AMONG_PUBLISHED => AccessResult::allowedIfHasPermission($account, 'access content'),
Chris@18: ]);
Chris@18: }
Chris@18:
Chris@18: /**
Chris@18: * Implements hook_jsonapi_ENTITY_TYPE_filter_access() for 'user'.
Chris@18: */
Chris@18: function jsonapi_jsonapi_user_filter_access(EntityTypeInterface $entity_type, AccountInterface $account) {
Chris@18: // @see \Drupal\user\UserAccessControlHandler::checkAccess()
Chris@18: // \Drupal\jsonapi\Access\TemporaryQueryGuard adds the condition for
Chris@18: // (!isAnonymous()), so this does not have to.
Chris@18: return ([
Chris@18: JSONAPI_FILTER_AMONG_OWN => AccessResult::allowed(),
Chris@18: JSONAPI_FILTER_AMONG_ENABLED => AccessResult::allowedIfHasPermission($account, 'access user profiles'),
Chris@18: ]);
Chris@18: }
Chris@18:
Chris@18: /**
Chris@18: * Implements hook_jsonapi_ENTITY_TYPE_filter_access() for 'workspace'.
Chris@18: */
Chris@18: function jsonapi_jsonapi_workspace_filter_access(EntityTypeInterface $entity_type, $published, $owner, AccountInterface $account) {
Chris@18: // @see \Drupal\workspaces\WorkspaceAccessControlHandler::checkAccess()
Chris@18: // \Drupal\jsonapi\Access\TemporaryQueryGuard adds the condition for
Chris@18: // (isDefaultWorkspace()), so this does not have to.
Chris@18: return ([
Chris@18: JSONAPI_FILTER_AMONG_ALL => AccessResult::allowedIfHasPermission($account, 'view any workspace'),
Chris@18: JSONAPI_FILTER_AMONG_OWN => AccessResult::allowedIfHasPermission($account, 'view own workspace'),
Chris@18: ]);
Chris@18: }