Skip to content
Commits on Source (53)
# JSON API Extras
This module provides extra functionality on top of JSON API. You should not need
this module to get an spec compliant JSON API.
This module provides extra functionality on top of JSON API. You should not
need this module to get an spec compliant JSON API, this module is to
customize the output of JSON API.
This module adds the following features:
......@@ -12,4 +13,5 @@ This module adds the following features:
- Lets you remove fields from the JSON API output.
TODO:
* Auto calculate the dependency of the provider of the entity type and bundles in the configuration entity.
* Auto calculate the dependency of the provider of the entity type and
bundles in the configuration entity.
{
"name": "drupal/jsonapi_extras",
"description": "JSON API Extras provides a means to override and provide limited configurations to the default zero-configuration implementation provided by the JSON API module.",
"type": "drupal-module",
"license": "GPL-2.0+",
"authors": [
{
"name": "Mateu Aguiló Bosch",
"email": "mateu.aguilo.bosch@gmail.com"
}
],
"require": {
"drupal/jsonapi": "^1.12"
}
}
path_prefix: jsonapi
include_count: false
......@@ -43,9 +43,31 @@ jsonapi_extras.resource_field:
mapping:
id:
type: string
description: 'ID'
description: 'The enhancer plugin ID'
settings:
type: mapping
description: 'Settings'
description: 'Unstructured settings.'
type: jsonapi_extras.enhancer_plugin.[%parent.id]
jsonapi_extras.enhancer_plugin.date_time:
type: mapping
mapping:
dateTimeFormat:
type: string
jsonapi_extras.enhancer_plugin.nested:
type: mapping
mapping:
path:
type: string
jsonapi_extras.settings:
type: config_object
label: 'JSON API Extras settings'
mapping:
path_prefix:
type: string
label: 'Path prefix'
description: 'The path prefix for JSON API'
include_count:
type: boolean
label: 'Include count in collection responses'
description: 'If activated, all collection responses will return a total record count for the provided query.'
span.label {
background-color: #e0e0d8;
border-radius: 5px;
color: #333;
font-weight: normal;
padding: 2px 5px;
}
span.label--overwritten {
background-color: #f39c12;
}
span.label--status {
background-color: #27ae60;
color: white;
}
span.label--status--disabled {
background-color: #c0392b;
}
/**
* @file
* JSON API Extras resources behaviors.
*/
(function ($, Drupal) {
'use strict';
/**
* Filters the resources tables by a text input search string.
*/
Drupal.behaviors.resourcesTableFilterByText = {
attach: function (context, settings) {
var $input = $('input.jsonapi-resources-filter-text', context).once('jsonapi-resources-filter-text');
var $table = $($input.attr('data-table'));
var $rows;
function filterViewList(e) {
var query = $(e.target).val().toLowerCase();
function showViewRow(index, row) {
var $row = $(row);
$row.closest('tr').toggle($row.is(":contains('" + query.toLowerCase() + "')"));
}
// Filter if the length of the query is at least 2 characters.
if (query.length >= 2) {
$rows.each(showViewRow);
}
else {
$rows.show();
}
}
if ($table.length) {
$rows = $table.find('tbody tr');
$input.on('keyup', filterViewList);
}
}
};
$.expr[":"].contains = $.expr.createPseudo(function(arg) {
return function( elem ) {
return $(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
};
});
}(jQuery, Drupal));
......@@ -4,4 +4,4 @@ description: Builds on top of JSON API to deliver extra functionality.
core: 8.x
package: Web services
dependencies:
- jsonapi
- jsonapi:jsonapi
admin:
version: VERSION
css:
theme:
css/jsonapi_extras.admin.css: {}
js:
js/jsonapi_extras.admin.js: {}
dependencies:
- core/jquery
- core/drupal
- core/jquery.once
entity.jsonapi_resource_config.add_form:
route_name: 'entity.jsonapi_resource_config.add_form'
title: 'Add JSON API Resource Config'
appears_on:
- entity.jsonapi_resource_config.collection
jsonapi_extras.resources:
title: 'Resources'
route_name: entity.jsonapi_resource_config.collection
base_route: entity.jsonapi_resource_config.collection
jsonapi_extras.settings:
title: 'Settings'
route_name: jsonapi_extras.settings
base_route: entity.jsonapi_resource_config.collection
<?php
/**
* @file
* Module implementation file.
*/
use Drupal\Core\Routing\RouteMatchInterface;
/**
* Implements hook_help().
*/
function jsonapi_extras_help($route_name, RouteMatchInterface $route_match) {
switch ($route_name) {
case 'entity.jsonapi_resource_config.collection':
$output = '';
$output .= '<p>' . t('The following table shows the list of JSON API resources available.') . '</p>';
$output .= '<p>' . t("Use the overwrite operation to overwrite a resource's configuration. You can revert back to the default configuration using the revert operation.") . '</p>';
return $output;
}
}
jsonapi_extras.settings:
path: '/admin/config/services/jsonapi/settings'
defaults:
_form: '\Drupal\jsonapi_extras\Form\JsonapiExtrasSettingsForm'
_title: 'Settings'
requirements:
_permission: 'administer site configuration'
services:
route_subscriber.alter_jsonapi:
class: Drupal\jsonapi_extras\EventSubscriber\JsonApiExtrasRouteAlterSubscriber
tags:
- { name: event_subscriber }
serializer.normalizer.field_item.jsonapi_extras:
class: Drupal\jsonapi_extras\Normalizer\FieldItemNormalizer
arguments:
......@@ -11,7 +6,18 @@ services:
- '@entity_type.manager'
- '@plugin.manager.resource_field_enhancer'
tags:
- { name: normalizer, priority: 25 }
- { name: jsonapi_normalizer_do_not_use_removal_imminent, priority: 25 }
serializer.normalizer.entity.jsonapi_extras:
class: Drupal\jsonapi_extras\Normalizer\ContentEntityNormalizer
arguments: ['@jsonapi.link_manager', '@jsonapi.resource_type.repository', '@entity_type.manager']
tags:
- { name: jsonapi_normalizer_do_not_use_removal_imminent, priority: 22 }
serializer.normalizer.config_entity.jsonapi_extras:
class: Drupal\jsonapi_extras\Normalizer\ConfigEntityNormalizer
arguments: ['@jsonapi.link_manager', '@jsonapi.resource_type.repository', '@entity_type.manager']
tags:
- { name: jsonapi_normalizer_do_not_use_removal_imminent, priority: 22 }
plugin.manager.resource_field_enhancer:
class: Drupal\jsonapi_extras\Plugin\ResourceFieldEnhancerManager
......
<?xml version="1.0" encoding="UTF-8"?>
<ruleset name="jsonapi">
<description>Default PHP CodeSniffer configuration for JSON API Extras.</description>
<file>.</file>
<arg name="extensions" value="inc,install,module,php,profile,test,theme,yml"/>
<!--Blacklist of coding standard rules that are not yet fixed. -->
<rule ref="Drupal">
<exclude name="Drupal.Commenting.FunctionComment.IncorrectTypeHint"/>
<exclude name="Drupal.Commenting.FunctionComment.MissingReturnComment"/>
<exclude name="Drupal.NamingConventions.ValidVariableName.LowerCamelName"/>
</rule>
</ruleset>
<?php
namespace Drupal\jsonapi_extras\Annotation;
use Drupal\Component\Annotation\Plugin;
......@@ -24,18 +23,18 @@ class ResourceFieldEnhancer extends Plugin {
/**
* The human-readable name of the formatter type.
*
* @ingroup plugin_translatable
*
* @var \Drupal\Core\Annotation\Translation
*
* @ingroup plugin_translatable
*/
public $label;
/**
* A short description of the formatter type.
*
* @ingroup plugin_translatable
*
* @var \Drupal\Core\Annotation\Translation
*
* @ingroup plugin_translatable
*/
public $description;
......
<?php
/**
* @file
* Contains \Drupal\jsonapi_extras\Entity\JsonapiResourceConfig.
*/
namespace Drupal\jsonapi_extras\Entity;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\Entity\EntityStorageInterface;
......@@ -30,17 +24,18 @@ use Drupal\Core\Entity\EntityStorageInterface;
* },
* config_prefix = "jsonapi_resource_config",
* admin_permission = "administer site configuration",
* static_cache = TRUE,
* entity_keys = {
* "id" = "id",
* "label" = "label",
* "uuid" = "uuid"
* },
* links = {
* "canonical" = "/admin/config/services/jsonapi-overwrites/{jsonapi_resource_config}",
* "add-form" = "/admin/config/services/jsonapi-overwrites/add",
* "edit-form" = "/admin/config/services/jsonapi-overwrites/{jsonapi_resource_config}/edit",
* "delete-form" = "/admin/config/services/jsonapi-overwrites/{jsonapi_resource_config}/delete",
* "collection" = "/admin/config/services/jsonapi-overwrites"
* "canonical" = "/admin/config/services/jsonapi/{jsonapi_resource_config}",
* "add-form" = "/admin/config/services/jsonapi/add/{entity_type_id}/{bundle}",
* "edit-form" = "/admin/config/services/jsonapi/{jsonapi_resource_config}/edit",
* "delete-form" = "/admin/config/services/jsonapi/{jsonapi_resource_config}/delete",
* "collection" = "/admin/config/services/jsonapi"
* }
* )
*/
......@@ -83,4 +78,29 @@ class JsonapiResourceConfig extends ConfigEntityBase {
\Drupal::service('router.builder')->setRebuildNeeded();
}
/**
* {@inheritdoc}
*/
public function calculateDependencies() {
parent::calculateDependencies();
$id = explode('--', $this->id);
$typeManager = $this->entityTypeManager();
$dependency = $typeManager->getDefinition($id[0])->getBundleConfigDependency($id[1]);
$this->addDependency($dependency['type'], $dependency['name']);
}
/**
* {@inheritdoc}
*/
protected function urlRouteParameters($rel) {
$uri_route_parameters = parent::urlRouteParameters($rel);
// The add-form route depends on entity_type_id and bundle.
if (in_array($rel, ['add-form'])) {
$parameters = explode('--', $this->id);
$uri_route_parameters['entity_type_id'] = $parameters[0];
$uri_route_parameters['bundle'] = $parameters[1];
}
return $uri_route_parameters;
}
}
<?php
namespace Drupal\jsonapi_extras\EventSubscriber;
use Drupal\Core\Routing\RouteBuildEvent;
use Drupal\Core\Routing\RoutingEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class JsonApiExtrasRouteAlterSubscriber implements EventSubscriberInterface {
/**
* Alters select routes to update the route path.
*
* @param \Drupal\Core\Routing\RouteBuildEvent $event
* The event to process.
*/
public function onRoutingRouteAlterSetPaths(RouteBuildEvent $event) {
$route_collection = $event->getRouteCollection();
$disabled_routes = [];
foreach ($route_collection as $route_id => $route) {
/** @var \Symfony\Component\Routing\Route $route */
if (!$route->getOption('_is_jsonapi')) {
continue;
}
if (
!$route->getRequirement('_entity_type') ||
!$route->getRequirement('_bundle')
) {
continue;
}
$config_id = sprintf(
'%s--%s',
$route->getRequirement('_entity_type'),
$route->getRequirement('_bundle')
);
/** @var \Drupal\jsonapi_extras\Entity\JsonapiResourceConfig $resource_config */
$resource_config = \Drupal::service('entity.repository')
->loadEntityByConfigTarget('jsonapi_resource_config', $config_id);
if (!$resource_config) {
continue;
}
if ($resource_config->get('disabled')) {
$disabled_routes[] = $route_id;
continue;
}
$path_replacement = preg_quote(sprintf(
'%s/%s',
$route->getRequirement('_entity_type'),
$route->getRequirement('_bundle')
));
$config_full_path = preg_replace(
"@$path_replacement@",
$resource_config->get('path'),
$route->getPath()
);
if ($config_full_path == $route->getPath()) {
continue;
}
$route->setPath($config_full_path);
}
// Remove all disabled routes.
foreach ($disabled_routes as $route_id) {
$route_collection->remove($route_id);
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events[RoutingEvents::ALTER][] = ['onRoutingRouteAlterSetPaths'];
return $events;
}
}
<?php
namespace Drupal\jsonapi_extras\Form;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\ProxyClass\Routing\RouteBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Configure JSON API settings for this site.
*/
class JsonapiExtrasSettingsForm extends ConfigFormBase {
protected $routerBuilder;
/**
* Constructs a \Drupal\system\ConfigFormBase object.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The factory for configuration objects.
* @param \Drupal\Core\ProxyClass\Routing\RouteBuilder $router_builder
* The router builder to rebuild menus after saving config entity.
*/
public function __construct(ConfigFactoryInterface $config_factory, RouteBuilder $router_builder) {
parent::__construct($config_factory);
$this->routerBuilder = $router_builder;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('config.factory'),
$container->get('router.builder')
);
}
/**
* {@inheritdoc}
*/
protected function getEditableConfigNames() {
return ['jsonapi_extras.settings'];
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'jsonapi_settings_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$config = $this->config('jsonapi_extras.settings');
$form['path_prefix'] = [
'#title' => $this->t('Path prefix'),
'#type' => 'textfield',
'#required' => TRUE,
'#field_prefix' => '/',
'#description' => $this->t('The path prefix for JSON API.'),
'#default_value' => $config->get('path_prefix'),
];
$form['include_count'] = [
'#title' => $this->t('Include count in collection queries'),
'#type' => 'checkbox',
'#description' => $this->t('If activated, all collection responses will return a total record count for the provided query.'),
'#default_value' => $config->get('include_count'),
];
return parent::buildForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
if ($path_prefix = $form_state->getValue('path_prefix')) {
$this->config('jsonapi_extras.settings')
->set('path_prefix', trim($path_prefix, '/'))
->save();
}
$this->config('jsonapi_extras.settings')
->set('include_count', $form_state->getValue('include_count'))
->save();
// Rebuild the router.
$this->routerBuilder->setRebuildNeeded();
parent::submitForm($form, $form_state);
}
}
<?php
/**
* @file
* Contains \Drupal\jsonapi_extras\Form\JsonapiResourceConfigDeleteForm.
*/
namespace Drupal\jsonapi_extras\Form;
use Drupal\Core\Entity\EntityConfirmFormBase;
......@@ -20,7 +15,7 @@ class JsonapiResourceConfigDeleteForm extends EntityConfirmFormBase {
* {@inheritdoc}
*/
public function getQuestion() {
return $this->t('Are you sure you want to delete %name?', array('%name' => $this->entity->label()));
return $this->t('Are you sure you want to revert %id to default?', ['%id' => $this->entity->id()]);
}
/**
......@@ -34,7 +29,7 @@ class JsonapiResourceConfigDeleteForm extends EntityConfirmFormBase {
* {@inheritdoc}
*/
public function getConfirmText() {
return $this->t('Delete');
return $this->t('Revert');
}
/**
......@@ -42,15 +37,9 @@ class JsonapiResourceConfigDeleteForm extends EntityConfirmFormBase {
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->entity->delete();
drupal_set_message(
$this->t('content @type: deleted @label.',
[
'@type' => $this->entity->bundle(),
'@label' => $this->entity->label(),
]
)
);
drupal_set_message($this->t('Resource %id has been reverted to default.', [
'%id' => $this->entity->id(),
]));
$form_state->setRedirectUrl($this->getCancelUrl());
}
......
<?php
/**
* @file
* Contains \Drupal\jsonapi_extras\Form\JsonapiResourceConfigForm.
*/
namespace Drupal\jsonapi_extras\Form;
use Drupal\Core\Config\ImmutableConfig;
use Drupal\Core\Config\TypedConfigManagerInterface;
use Drupal\Core\Entity\ContentEntityTypeInterface;
use Drupal\Core\Entity\EntityFieldManager;
use Drupal\Core\Entity\EntityForm;
......@@ -19,34 +16,69 @@ use Drupal\jsonapi\ResourceType\ResourceTypeRepository;
use Drupal\jsonapi_extras\Entity\JsonapiResourceConfig;
use Drupal\jsonapi_extras\Plugin\ResourceFieldEnhancerManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* Base form for jsonapi_resource_config.
*/
class JsonapiResourceConfigForm extends EntityForm {
/**
* The bundle information service.
*
* @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
*/
protected $bundleInfo;
/**
* The JSON API resource type repository.
*
* @var \Drupal\jsonapi\ResourceType\ResourceTypeRepository
*/
protected $resourceTypeRepository;
/**
* The field manager.
*
* @var \Drupal\Core\Entity\EntityFieldManager
*/
protected $fieldManager;
/**
* The entity type repository.
*
* @var \Drupal\Core\Entity\EntityTypeRepositoryInterface
*/
protected $entityTypeRepository;
/**
* The field enhancer manager.
*
* @var \Drupal\jsonapi_extras\Plugin\ResourceFieldEnhancerManager
*/
protected $enhancerManager;
/**
* The JSON API extras config.
*
* @var \Drupal\Core\Config\ImmutableConfig
*/
protected $config;
/**
* The current route match.
*
* @var \Symfony\Component\HttpFoundation\Request
*/
protected $request;
/**
* The typed config manager.
*
* @var \Drupal\Core\Config\TypedConfigManagerInterface
*/
protected $typedConfigManager;
/**
* JsonapiResourceConfigForm constructor.
*
......@@ -60,13 +92,22 @@ class JsonapiResourceConfigForm extends EntityForm {
* The entity type repository.
* @param \Drupal\jsonapi_extras\Plugin\ResourceFieldEnhancerManager $enhancer_manager
* The plugin manager for the resource field enhancer.
* @param \Drupal\Core\Config\ImmutableConfig $config
* The config instance.
* @param \Symfony\Component\HttpFoundation\Request $request
* The HTTP request.
* @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config_manager
* The typed config manager.
*/
public function __construct(EntityTypeBundleInfoInterface $bundle_info, ResourceTypeRepository $resource_type_repository, EntityFieldManager $field_manager, EntityTypeRepositoryInterface $entity_type_repository, ResourceFieldEnhancerManager $enhancer_manager) {
public function __construct(EntityTypeBundleInfoInterface $bundle_info, ResourceTypeRepository $resource_type_repository, EntityFieldManager $field_manager, EntityTypeRepositoryInterface $entity_type_repository, ResourceFieldEnhancerManager $enhancer_manager, ImmutableConfig $config, Request $request, TypedConfigManagerInterface $typed_config_manager) {
$this->bundleInfo = $bundle_info;
$this->resourceTypeRepository = $resource_type_repository;
$this->fieldManager = $field_manager;
$this->entityTypeRepository = $entity_type_repository;
$this->enhancerManager = $enhancer_manager;
$this->config = $config;
$this->request = $request;
$this->typedConfigManager = $typed_config_manager;
}
/**
......@@ -78,7 +119,10 @@ class JsonapiResourceConfigForm extends EntityForm {
$container->get('jsonapi.resource_type.repository'),
$container->get('entity_field.manager'),
$container->get('entity_type.repository'),
$container->get('plugin.manager.resource_field_enhancer')
$container->get('plugin.manager.resource_field_enhancer'),
$container->get('config.factory')->get('jsonapi_extras.settings'),
$container->get('request_stack')->getCurrentRequest(),
$container->get('config.typed')
);
}
......@@ -90,20 +134,23 @@ class JsonapiResourceConfigForm extends EntityForm {
// Disable caching on this form.
$form_state->setCached(FALSE);
$entity_type_id = $this->request->get('entity_type_id');
$bundle = $this->request->get('bundle');
/** @var \Drupal\jsonapi_extras\Entity\JsonapiResourceConfig $entity */
$entity = $this->getEntity();
$resource_id = $entity->get('id');
// If we are editing an entity we don't want the Entity Type and Bundle
// picker, that info is locked.
if ($resource_id) {
if (!$entity_type_id || !$bundle) {
if (!$resource_id) {
// We can't build the form without an entity type and bundle.
throw new \InvalidArgumentException('Unable to load entity type or bundle for the overrides form.');
}
list($entity_type_id, $bundle) = explode('--', $resource_id);
$form['#title'] = $this->t('Edit %label resource config', ['%label' => $resource_id]);
}
else {
list($entity_type_id, $bundle) = $this->buildEntityTypeBundlePicker($form, $form_state);
if (!$entity_type_id) {
return $form;
}
}
if ($entity_type_id && $resource_type = $this->resourceTypeRepository->get($entity_type_id, $bundle)) {
// Get the JSON API resource type.
$resource_config_id = sprintf('%s--%s', $entity_type_id, $bundle);
......@@ -123,6 +170,25 @@ class JsonapiResourceConfigForm extends EntityForm {
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
if (!method_exists($this->typedConfigManager, 'createFromNameAndData')) {
// Versions of Drupal before 8.4 have poor support for constraints. In
// those scenarios we don't validate the form submission.
return;
}
$typed_config = $this->typedConfigManager
->createFromNameAndData($this->entity->id(), $this->entity->toArray());
$constraints = $typed_config->validate();
/** @var \Symfony\Component\Validator\ConstraintViolation $violation */
foreach ($constraints as $violation) {
$form_path = str_replace('.', '][', $violation->getPropertyPath());
$form_state->setErrorByName($form_path, $violation->getMessage());
}
}
/**
* {@inheritdoc}
*/
......@@ -145,19 +211,6 @@ class JsonapiResourceConfigForm extends EntityForm {
$form_state->setRedirectUrl($resource_config->urlInfo('collection'));
}
/**
* Implements callback for Ajax event on entity type or bundle selection.
*
* @param array $form
* From render array.
*
* @return array
* Color selection section of the form.
*/
public function bundleCallback(array &$form) {
return $form['bundle_wrapper'];
}
/**
* Builds the part of the form that contains the overrides.
*
......@@ -179,7 +232,8 @@ class JsonapiResourceConfigForm extends EntityForm {
}, $this->fieldManager->getFieldDefinitions($entity_type_id, $bundle));
}
else {
$field_names = array_keys($entity_type->getKeys());
$field_names = array_keys($entity_type->getPropertiesToExport());
array_unshift($field_names, $entity_type->getKey('id'));
}
$overrides_form['overrides']['entity'] = [
......@@ -195,11 +249,16 @@ class JsonapiResourceConfigForm extends EntityForm {
'#description' => $this->t('Check this if you want to disable this resource. Disabling a resource can have unexpected results when following relationships belonging to that resource.'),
'#default_value' => $entity->get('disabled'),
];
$resource_type_name = $entity->get('resourceType');
if (!$resource_type_name) {
$resource_type_name = sprintf('%s--%s', $entity_type_id, $bundle);
}
$overrides_form['overrides']['entity']['resourceType'] = [
'#type' => 'textfield',
'#title' => $this->t('Resource Type'),
'#description' => $this->t('Overrides the type of the resource. Example: Change "node--article" to "articles".'),
'#default_value' => $entity->get('resourceType'),
'#default_value' => $resource_type_name,
'#states' => [
'visible' => [
':input[name="disabled"]' => ['checked' => FALSE],
......@@ -212,11 +271,14 @@ class JsonapiResourceConfigForm extends EntityForm {
$path = sprintf('%s/%s', $entity_type_id, $bundle);
}
$prefix = $this->config->get('path_prefix');
$overrides_form['overrides']['entity']['path'] = [
'#type' => 'textfield',
'#title' => $this->t('Resource Path'),
'#field_prefix' => '/jsonapi/',
'#description' => $this->t('Overrides the path of the resource. Example: Use "articles" to change "/jsonapi/node/article" to "/jsonapi/articles".'),
'#field_prefix' => sprintf('/%s/', $prefix),
'#description' => $this->t('Overrides the path of the resource. Example: Use "articles" to change "/@prefix/node/article" to "/@prefix/articles".', [
'@prefix' => $prefix,
]),
'#default_value' => $path,
'#required' => TRUE,
'#states' => [
......@@ -229,17 +291,20 @@ class JsonapiResourceConfigForm extends EntityForm {
$overrides_form['overrides']['fields'] = [
'#type' => 'details',
'#title' => $this->t('Fields'),
'#description' => $this->t('Override configuration for the resource fields.'),
'#open' => TRUE,
];
$markup = '';
$markup .= '<dl>';
$markup .= '<dt>' . $this->t('Disabled') . '</dt>';
$markup .= '<dd>' . $this->t('Check this if you want to disable this field completely. Disabling required fields will cause problems when writing to the resource.') . '</dd>';
$markup .= '<dt>' . $this->t('Alias') . '</dt>';
$markup .= '<dd>' . $this->t('Overrides the field name with a custom name. Example: Change "field_tags" to "tags".') . '</dd>';
$markup .= '<dt>' . $this->t('Enhancer') . '</dt>';
$markup .= '<dd>' . $this->t('Select an enhancer to manipulate the public output coming in and out.') . '</dd>';
$markup .= '</dl>';
$overrides_form['overrides']['fields']['info'] = [
'#theme' => 'item_list',
'#items' => [
$this->t('<strong>Disabled</strong>: Check this if you want to disable this field completely. Disabling required fields will cause problems when writing to the resource.'),
$this->t('<strong>Alias</strong>: Overrides the field name with a custom name. Example: Change "field_tags" to "tags".'),
$this->t('<strong>Enhancer</strong>: Select an enhancer to manipulate the public output coming in and out.'),
],
'#markup' => $markup,
];
$overrides_form['overrides']['fields']['resourceFields'] = [
......@@ -265,6 +330,22 @@ class JsonapiResourceConfigForm extends EntityForm {
return $overrides_form;
}
/**
* {@inheritdoc}
*/
public function buildEntity(array $form, FormStateInterface $form_state) {
/** @var \Drupal\jsonapi_extras\Entity\JsonapiResourceConfig $entity */
$entity = parent::buildEntity($form, $form_state);
// Trim slashes from path.
$path = trim($form_state->getValue('path'), '/');
if (strlen($path) > 0) {
$entity->set('path', $path);
}
return $entity;
}
/**
* Builds the part of the form that overrides the field.
*
......@@ -285,7 +366,7 @@ class JsonapiResourceConfigForm extends EntityForm {
$overrides_form['disabled'] = [
'#type' => 'checkbox',
'#title' => $this->t('Disabled'),
'#title_display' => 'hidden',
'#title_display' => 'invisible',
'#default_value' => $resource_field['disabled'],
];
$overrides_form['fieldName'] = [
......@@ -297,9 +378,7 @@ class JsonapiResourceConfigForm extends EntityForm {
'#type' => 'textfield',
'#title' => $this->t('Override Public Name'),
'#title_display' => 'hidden',
'#default_value' => empty($resource_field['publicName'])
? $field_name
: $resource_field['publicName'],
'#default_value' => empty($resource_field['publicName']) ? $field_name : $resource_field['publicName'],
'#states' => [
'visible' => [
':input[name="resourceFields[' . $field_name . '][disabled]"]' => [
......@@ -353,71 +432,6 @@ class JsonapiResourceConfigForm extends EntityForm {
return $overrides_form;
}
/**
* Build the entity picker widget and return the entity type and bundle IDs.
*
* @param array $form
* The form passed by reference to update it.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The state of the form
*
* @return array
* The entity types ID and the bundle ID.
*/
protected function buildEntityTypeBundlePicker(array &$form, FormStateInterface $form_state) {
$form['_entity_type_id'] = [
'#title' => $this->t('Entity Type'),
'#type' => 'select',
'#options' => $this->entityTypeRepository->getEntityTypeLabels(TRUE),
'#empty_option' => $this->t('- Select -'),
'#required' => TRUE,
'#ajax' => [
'callback' => '::bundleCallback',
'wrapper' => 'bundle-wrapper',
],
];
$form['bundle_wrapper'] = [
'#type' => 'container',
'#attributes' => ['id' => 'bundle-wrapper'],
];
if (!$entity_type_id = $form_state->getValue('_entity_type_id')) {
return [$entity_type_id, NULL];
}
$has_bundles = (bool) $this->entityTypeManager
->getDefinition($entity_type_id)->getBundleEntityType();
if ($has_bundles) {
$bundles = [];
$bundle_info = $this->bundleInfo->getBundleInfo($entity_type_id);
foreach ($bundle_info as $bundle_id => $info) {
$bundles[$bundle_id] = $info['translatable']
? $this->t($info['label'])
: $info['label'];
}
$form['bundle_wrapper']['_bundle_id'] = [
'#type' => 'select',
'#empty_option' => $this->t('- Select -'),
'#title' => $this->t('Bundle'),
'#options' => $bundles,
'#required' => TRUE,
'#ajax' => [
'callback' => '::bundleCallback',
'wrapper' => 'bundle-wrapper',
],
];
}
else {
$form['bundle_wrapper']['_bundle_id'] = [
'#type' => 'hidden',
'#value' => $entity_type_id,
];
}
$bundle = $has_bundles
? $form_state->getValue('_bundle_id')
: $entity_type_id;
return [$entity_type_id, $bundle];
}
/**
* AJAX callback to get the form settings for the enhancer for a field.
*
......