Skip to content
Commits on Source (9)
......@@ -43,12 +43,21 @@ 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
......
......@@ -24,6 +24,7 @@ use Drupal\Core\Entity\EntityStorageInterface;
* },
* config_prefix = "jsonapi_resource_config",
* admin_permission = "administer site configuration",
* static_cache = TRUE,
* entity_keys = {
* "id" = "id",
* "label" = "label",
......@@ -77,4 +78,14 @@ 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('config', $dependency['name']);
}
}
......@@ -3,6 +3,7 @@
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;
......@@ -71,6 +72,11 @@ class JsonapiResourceConfigForm extends EntityForm {
*/
protected $request;
/**
* @var \Drupal\Core\Config\TypedConfigManagerInterface
*/
protected $typedConfigManager;
/**
* JsonapiResourceConfigForm constructor.
*
......@@ -87,8 +93,9 @@ class JsonapiResourceConfigForm extends EntityForm {
* @param \Drupal\Core\Config\ImmutableConfig $config
* The config instance.
* @param \Symfony\Component\HttpFoundation\Request $request
* @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config_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) {
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;
......@@ -96,6 +103,7 @@ class JsonapiResourceConfigForm extends EntityForm {
$this->enhancerManager = $enhancer_manager;
$this->config = $config;
$this->request = $request;
$this->typedConfigManager = $typed_config_manager;
}
/**
......@@ -109,7 +117,8 @@ class JsonapiResourceConfigForm extends EntityForm {
$container->get('entity_type.repository'),
$container->get('plugin.manager.resource_field_enhancer'),
$container->get('config.factory')->get('jsonapi_extras.settings'),
$container->get('request_stack')->getCurrentRequest()
$container->get('request_stack')->getCurrentRequest(),
$container->get('config.typed')
);
}
......@@ -161,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}
*/
......@@ -275,17 +303,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>' . t('Disabled') . '</dt>';
$markup .= '<dd>' . 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>' . t('Alias') . '</dt>';
$markup .= '<dd>' . t('Overrides the field name with a custom name. Example: Change "field_tags" to "tags".') . '</dd>';
$markup .= '<dt>' . t('Enhancer') . '</dt>';
$markup .= '<dd>' . 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'] = [
......@@ -347,7 +378,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'] = [
......
<?php
namespace Drupal\jsonapi_extras\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraint;
/**
* @Constraint(
* id = "jsonapi_extras__duplicate_field",
* label = @Translation("Duplicate field", context = "Validation")
* )
*/
class DuplicateFieldConstraint extends Constraint {
public $message = 'The override must be unique.';
}
<?php
namespace Drupal\jsonapi_extras\Plugin\Validation\Constraint;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
/**
*/
class DuplicateFieldConstraintValidator extends ConstraintValidator {
/**
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* DuplicateFieldConstraintValidator constructor.
*/
public function __construct(EntityTypeManagerInterface $entityTypeManager = NULL) {
$this->entityTypeManager = $entityTypeManager ?: \Drupal::entityTypeManager();
}
/**
* {@inheritdoc}
*/
public function validate($entity_data, Constraint $constraint) {
$resourceFields = $entity_data['resourceFields'];
$overrides = [];
// Get the field values
foreach ($resourceFields as $field => $data) {
// Only get the overridden fields.
if ($data['fieldName'] != $data['publicName']) {
// Store the publicName for comparison.
$overrides[$field] = $data['publicName'];
}
}
// Compare the overrides and find any duplicate values.
$deduped_overrides = array_unique($overrides);
$dupes = array_diff_assoc($overrides, $deduped_overrides);
// Set an error if there are duplicates.
if ($dupes) {
foreach ($dupes as $field => $value) {
$this->context->buildViolation($constraint->message)
->atPath("resourceFields.$field.publicName")
->addViolation();
}
}
// Now compare the overrides with the default names to validate no dupes exist.
foreach ($overrides as $field => $override) {
if (array_key_exists($override, $resourceFields)) {
$this->context->buildViolation($constraint->message)
->atPath("resourceFields.$field.publicName")
->addViolation();
}
}
// Validate URL and resource type
$resource_types = $this->entityTypeManager
->getStorage('jsonapi_resource_config')
->loadByProperties(['disabled' => FALSE]);
foreach ($resource_types as $id => $resource_type) {
if ($entity_data['id'] == $id) {
continue;
}
if ($resource_type->get('resourceType') == $entity_data['resourceType']) {
$this->context->buildViolation('There is already resource (@name) with this resource type.', ['@name' => $resource_type->id()])
->atPath('resourceType')
->addViolation();
}
if ($resource_type->get('path') == $entity_data['path']) {
$this->context->buildViolation('There is already resource (@name) with this path.', ['@name' => $resource_type->id()])
->atPath('resourceType')
->addViolation();
}
}
}
}
......@@ -34,6 +34,11 @@ class ConfigurableResourceTypeRepository extends ResourceTypeRepository {
*/
protected $configFactory;
/**
* @var \Drupal\jsonapi_extras\ResourceType\ConfigurableResourceType[]
*/
protected $resourceTypes;
/**
* {@inheritdoc}
*/
......@@ -81,18 +86,29 @@ class ConfigurableResourceTypeRepository extends ResourceTypeRepository {
* An array of resource types.
*/
public function getResourceTypes($include_disabled = TRUE) {
if (isset($this->resourceTypes)) {
return $this->resourceTypes;
}
$entity_type_ids = array_keys($this->entityTypeManager->getDefinitions());
$resource_types = [];
$resource_config_ids = [];
foreach ($entity_type_ids as $entity_type_id) {
$bundles = array_keys($this->bundleManager->getBundleInfo($entity_type_id));
$current_types = array_map(function ($bundle) use ($entity_type_id, $include_disabled) {
$resource_config_ids = array_merge($resource_config_ids, array_map(function ($bundle) use ($entity_type_id) {
return sprintf('%s--%s', $entity_type_id, $bundle);
}, $bundles));
}
$resource_configs = $this->entityTypeManager->getStorage('jsonapi_resource_config')->loadMultiple($resource_config_ids);
foreach ($entity_type_ids as $entity_type_id) {
$bundles = array_keys($this->bundleManager->getBundleInfo($entity_type_id));
$current_types = array_map(function ($bundle) use ($entity_type_id, $include_disabled, $resource_configs) {
$resource_config_id = sprintf('%s--%s', $entity_type_id, $bundle);
$resource_config = $this->entityRepository->loadEntityByConfigTarget(
'jsonapi_resource_config',
$resource_config_id
);
$resource_config = $resource_config ?: new NullJsonapiResourceConfig([], '');
$resource_config = isset($resource_configs[$resource_config_id]) ? $resource_configs[$resource_config_id] : new NullJsonapiResourceConfig([], '');
if (!$include_disabled && $resource_config->get('disabled')) {
return NULL;
}
......@@ -108,7 +124,8 @@ class ConfigurableResourceTypeRepository extends ResourceTypeRepository {
$resource_types = array_merge($resource_types, $current_types);
}
return array_filter($resource_types);
$this->resourceTypes = array_filter($resource_types);
return $this->resourceTypes;
}
}