diff --git a/entity_browser.api.php b/entity_browser.api.php index 8231aea..230eefc 100644 --- a/entity_browser.api.php +++ b/entity_browser.api.php @@ -62,5 +62,16 @@ function hook_entity_browser_field_widget_display_info_alter(&$field_displays) { } /** + * Alter the information provided in \Drupal\entity_browser\Annotation\EntityBrowserWidgetValidation. + * + * @param $validation_plugins + * The array of widget validation plugins, keyed on the machine-readable + * name. + */ +function hook_entity_browser_widget_validation_info_alter(&$validation_plugins) { + $field_displays['not_null']['label'] = t('Not null fabulous validator'); +} + +/** * @} End of "addtogroup hooks". */ diff --git a/entity_browser.services.yml b/entity_browser.services.yml index 88fedfd..4c27024 100644 --- a/entity_browser.services.yml +++ b/entity_browser.services.yml @@ -14,6 +14,9 @@ services: plugin.manager.entity_browser.field_widget_display: class: Drupal\entity_browser\FieldWidgetDisplayManager parent: default_plugin_manager + plugin.manager.entity_browser.widget_validation: + class: Drupal\entity_browser\WidgetValidationManager + parent: default_plugin_manager entity_browser.route_subscriber: class: Drupal\entity_browser\RouteSubscriber arguments: ['@entity.manager', '@plugin.manager.entity_browser.display', '@entity.query'] diff --git a/src/Annotation/EntityBrowserWidgetValidation.php b/src/Annotation/EntityBrowserWidgetValidation.php new file mode 100644 index 0000000..26038e2 --- /dev/null +++ b/src/Annotation/EntityBrowserWidgetValidation.php @@ -0,0 +1,43 @@ +configuration += $this->defaultConfiguration(); $this->eventDispatcher = $event_dispatcher; $this->uuidGenerator = $uuid_generator; + $this->keyValue = $key_value; } /** @@ -80,7 +91,8 @@ abstract class DisplayBase extends PluginBase implements DisplayInterface, Conta $plugin_id, $plugin_definition, $container->get('event_dispatcher'), - $container->get('uuid') + $container->get('uuid'), + $container->get('keyvalue.expirable')->get('entity_browser') ); } @@ -139,4 +151,24 @@ abstract class DisplayBase extends PluginBase implements DisplayInterface, Conta $this->uuid = $uuid; } + /** + * {@inheritdoc} + */ + public function setValidators(array $validators) { + // Generate the hash that we use as key for the key/value. + $hash = md5(serialize($validators)); + + if (!$this->keyValue->has($hash)) { + $this->keyValue->set($hash, $validators); + } + return $hash; + } + + /** + * {@inheritdoc} + */ + public function getValidators($hash) { + return $this->keyValue->get($hash, []); + } + } diff --git a/src/DisplayInterface.php b/src/DisplayInterface.php index a73f443..e0238d2 100644 --- a/src/DisplayInterface.php +++ b/src/DisplayInterface.php @@ -36,11 +36,14 @@ interface DisplayInterface extends PluginInspectionInterface, ConfigurablePlugin * * @param \Drupal\Core\Form\FormStateInterface $form_state * The form state object. + * @param string $validators + * (optional) Validators hash identifier as returned by + * EntityReference::prepareValidators(). * * @return array * An array suitable for drupal_render(). */ - public function displayEntityBrowser(FormStateInterface $form_state); + public function displayEntityBrowser(FormStateInterface $form_state, $validators = ''); /** * Indicates completed selection. @@ -69,4 +72,29 @@ interface DisplayInterface extends PluginInspectionInterface, ConfigurablePlugin */ public function setUuid($uuid); + /** + * Set validators. + * + * Saves Entity Browser Widget validators in key/value storage if an identical + * set of constraints is not already stored there. + * + * @param array $validators + * An array where keys are validator ids and values configurations for them. + * + * @return string + * The hash generated from hashing the validators array. + */ + public function setValidators(array $validators); + + /** + * Get validators. + * + * @param $hash + * The hash generated from hashing the validators array. + * + * @return mixed + * An array where keys are validator ids and values configurations for them + * or empty array if no validators are stored. + */ + public function getValidators($hash); } diff --git a/src/Plugin/EntityBrowser/Display/IFrame.php b/src/Plugin/EntityBrowser/Display/IFrame.php index 9eed34f..df86073 100644 --- a/src/Plugin/EntityBrowser/Display/IFrame.php +++ b/src/Plugin/EntityBrowser/Display/IFrame.php @@ -5,6 +5,7 @@ namespace Drupal\entity_browser\Plugin\EntityBrowser\Display; use Drupal\Component\Uuid\UuidInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Url; use Drupal\entity_browser\DisplayBase; @@ -72,9 +73,11 @@ class IFrame extends DisplayBase implements DisplayRouterInterface { * Current request. * @param \Drupal\Core\Path\CurrentPathStack $current_path * The current path. + * @param \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface $key_value + * The key value store. */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, EventDispatcherInterface $event_dispatcher, UuidInterface $uuid, RouteMatchInterface $current_route_match, Request $request, CurrentPathStack $current_path) { - parent::__construct($configuration, $plugin_id, $plugin_definition, $event_dispatcher, $uuid); + public function __construct(array $configuration, $plugin_id, $plugin_definition, EventDispatcherInterface $event_dispatcher, UuidInterface $uuid, RouteMatchInterface $current_route_match, Request $request, CurrentPathStack $current_path, KeyValueStoreExpirableInterface $key_value) { + parent::__construct($configuration, $plugin_id, $plugin_definition, $event_dispatcher, $uuid, $key_value); $this->currentRouteMatch = $current_route_match; $this->request = $request; $this->currentPath = $current_path; @@ -89,10 +92,12 @@ class IFrame extends DisplayBase implements DisplayRouterInterface { $plugin_id, $plugin_definition, $container->get('event_dispatcher'), + $container->get('current_route_match'), $container->get('uuid'), $container->get('current_route_match'), $container->get('request_stack')->getCurrentRequest(), - $container->get('path.current') + $container->get('path.current'), + $container->get('keyvalue.expirable')->get('entity_browser') ); } @@ -111,7 +116,7 @@ class IFrame extends DisplayBase implements DisplayRouterInterface { /** * {@inheritdoc} */ - public function displayEntityBrowser(FormStateInterface $form_state) { + public function displayEntityBrowser(FormStateInterface $form_state, $validators = '') { $uuid = $this->getUuid(); /** @var \Drupal\entity_browser\Events\RegisterJSCallbacks $event */ $js_event_object = new RegisterJSCallbacks($this->configuration['entity_browser_id'], $uuid); @@ -123,6 +128,7 @@ class IFrame extends DisplayBase implements DisplayRouterInterface { 'query' => [ 'uuid' => $uuid, 'original_path' => $original_path, + 'validators' => $validators, ], ], 'attributes' => [ diff --git a/src/Plugin/EntityBrowser/Display/Modal.php b/src/Plugin/EntityBrowser/Display/Modal.php index 433cf27..6941d94 100644 --- a/src/Plugin/EntityBrowser/Display/Modal.php +++ b/src/Plugin/EntityBrowser/Display/Modal.php @@ -7,6 +7,7 @@ use Drupal\Component\Utility\NestedArray; use Drupal\Component\Uuid\UuidInterface; use Drupal\Core\Ajax\OpenDialogCommand; use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Url; use Drupal\entity_browser\DisplayBase; @@ -73,13 +74,15 @@ class Modal extends DisplayBase implements DisplayRouterInterface { * UUID generator interface. * @param \Drupal\Core\Routing\RouteMatchInterface * The currently active route match object. - * @param \Symfony\Component\HttpFoundation\Request $request - * Current request. * @param \Drupal\Core\Path\CurrentPathStack $current_path * The current path. + * @param \Symfony\Component\HttpFoundation\Request $request + * Current request. + * @param \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface $key_value + * The key value store. */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, EventDispatcherInterface $event_dispatcher, UuidInterface $uuid, RouteMatchInterface $current_route_match, CurrentPathStack $current_path, Request $request) { - parent::__construct($configuration, $plugin_id, $plugin_definition, $event_dispatcher, $uuid); + public function __construct(array $configuration, $plugin_id, $plugin_definition, EventDispatcherInterface $event_dispatcher, UuidInterface $uuid, RouteMatchInterface $current_route_match, CurrentPathStack $current_path, Request $request, KeyValueStoreExpirableInterface $key_value) { + parent::__construct($configuration, $plugin_id, $plugin_definition, $event_dispatcher, $uuid, $key_value); $this->currentRouteMatch = $current_route_match; $this->currentPath = $current_path; $this->request = $request; @@ -97,7 +100,8 @@ class Modal extends DisplayBase implements DisplayRouterInterface { $container->get('uuid'), $container->get('current_route_match'), $container->get('path.current'), - $container->get('request_stack')->getCurrentRequest() + $container->get('request_stack')->getCurrentRequest(), + $container->get('keyvalue.expirable')->get('entity_browser') ); } @@ -115,7 +119,7 @@ class Modal extends DisplayBase implements DisplayRouterInterface { /** * {@inheritdoc} */ - public function displayEntityBrowser(FormStateInterface $form_state) { + public function displayEntityBrowser(FormStateInterface $form_state, $validators = '') { $uuid = $this->getUuid(); $js_event_object = new RegisterJSCallbacks($this->configuration['entity_browser_id'], $uuid); $js_event_object->registerCallback('Drupal.entityBrowser.selectionCompleted'); @@ -126,6 +130,7 @@ class Modal extends DisplayBase implements DisplayRouterInterface { 'query' => [ 'uuid' => $uuid, 'original_path' => $original_path, + 'validators' => $validators, ], ], 'attributes' => [ diff --git a/src/Plugin/EntityBrowser/Display/Standalone.php b/src/Plugin/EntityBrowser/Display/Standalone.php index 9b8fbc6..949fc5e 100644 --- a/src/Plugin/EntityBrowser/Display/Standalone.php +++ b/src/Plugin/EntityBrowser/Display/Standalone.php @@ -45,7 +45,7 @@ class Standalone extends DisplayBase implements DisplayRouterInterface { /** * {@inheritdoc} */ - public function displayEntityBrowser(FormStateInterface $form_state) { + public function displayEntityBrowser(FormStateInterface $form_state, $validators = '') { // @TODO Implement it. } diff --git a/src/Plugin/EntityBrowser/Widget/Upload.php b/src/Plugin/EntityBrowser/Widget/Upload.php index eff51b2..f882dba 100644 --- a/src/Plugin/EntityBrowser/Widget/Upload.php +++ b/src/Plugin/EntityBrowser/Widget/Upload.php @@ -103,13 +103,12 @@ class Upload extends WidgetBase { /** * {@inheritdoc} */ - public function validate(array &$form, FormStateInterface $form_state) { - $uploaded_files = $form_state->getValue(['upload'], []); - $trigger = $form_state->getTriggeringElement(); - // Only validate if we are uploading a file. - if (empty($uploaded_files) && $trigger['#value'] == 'Upload') { - $form_state->setError($form['widget']['upload'], t('At least one file should be uploaded.')); + public function prepareEntities(FormStateInterface $form_state) { + $files = []; + foreach ($form_state->getValue(['upload'], []) as $fid) { + $files[] = $this->entityManager->getStorage('file')->load($fid); } + return $files; } /** diff --git a/src/Plugin/EntityBrowser/Widget/View.php b/src/Plugin/EntityBrowser/Widget/View.php index 6e86429..98a4849 100644 --- a/src/Plugin/EntityBrowser/Widget/View.php +++ b/src/Plugin/EntityBrowser/Widget/View.php @@ -4,6 +4,7 @@ namespace Drupal\entity_browser\Plugin\EntityBrowser\Widget; use Drupal\Component\Plugin\Exception\PluginNotFoundException; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface; use Drupal\Core\Render\Element; use Drupal\entity_browser\WidgetBase; use Drupal\Core\Url; @@ -13,6 +14,7 @@ use Drupal\Core\Session\AccountInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Drupal\Core\Entity\EntityManagerInterface; +use Symfony\Component\HttpFoundation\Request; /** * Uses a view to provide entity listing in a browser's widget. @@ -53,7 +55,9 @@ class View extends WidgetBase implements ContainerFactoryPluginInterface { $plugin_definition, $container->get('event_dispatcher'), $container->get('entity.manager'), - $container->get('current_user') + $container->get('current_user'), + $container->get('keyvalue.expirable')->get('entity_browser'), + $container->get('request_stack')->getCurrentRequest() ); } @@ -71,9 +75,9 @@ class View extends WidgetBase implements ContainerFactoryPluginInterface { * @param \Drupal\Core\Session\AccountInterface $current_user * The current user. */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, EventDispatcherInterface $event_dispatcher, EntityManagerInterface $entity_manager, AccountInterface $current_user) { - parent::__construct($configuration, $plugin_id, $plugin_definition, $event_dispatcher, $entity_manager); - $this->currentUser = $current_user; + public function __construct(array $configuration, $plugin_id, $plugin_definition, EventDispatcherInterface $event_dispatcher, EntityManagerInterface $entity_manager, AccountInterface $current_user, KeyValueStoreExpirableInterface $key_value, Request $request) { + parent::__construct($configuration, $plugin_id, $plugin_definition, $event_dispatcher, $entity_manager, $key_value, $request); + $this->currentUser = $current_user; } /** diff --git a/src/Plugin/EntityBrowser/WidgetValidation/Cardinality.php b/src/Plugin/EntityBrowser/WidgetValidation/Cardinality.php new file mode 100644 index 0000000..c688853 --- /dev/null +++ b/src/Plugin/EntityBrowser/WidgetValidation/Cardinality.php @@ -0,0 +1,39 @@ +getPluginDefinition(); + $data_definition->addConstraint($plugin_definition['constraint'], $options); + + $ids = []; + foreach ($entities as $entity) { + $ids[] = $entity->id(); + } + + $typed_data = \Drupal::typedDataManager()->create($data_definition, $ids); + return $typed_data->validate(); + } +} diff --git a/src/Plugin/EntityBrowser/WidgetValidation/EntityType.php b/src/Plugin/EntityBrowser/WidgetValidation/EntityType.php new file mode 100644 index 0000000..588a89f --- /dev/null +++ b/src/Plugin/EntityBrowser/WidgetValidation/EntityType.php @@ -0,0 +1,38 @@ +createDataDefinition('entity_reference'); + $plugin_definition = $this->getPluginDefinition(); + $data_definition->addConstraint($plugin_definition['constraint'], $options); + + $violations = new ConstraintViolationList([]); + foreach ($entities as $entity) { + $validation_result = \Drupal::typedDataManager()->create($data_definition, $entity)->validate(); + $violations->addAll($validation_result); + } + + return $violations; + } +} diff --git a/src/Plugin/Field/FieldWidget/EntityReference.php b/src/Plugin/Field/FieldWidget/EntityReference.php index 1f8520d..f2164d8 100644 --- a/src/Plugin/Field/FieldWidget/EntityReference.php +++ b/src/Plugin/Field/FieldWidget/EntityReference.php @@ -12,6 +12,7 @@ use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Field\WidgetBase; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Url; use Drupal\Core\Validation\Plugin\Validation\Constraint\NotNullConstraint; @@ -50,6 +51,11 @@ class EntityReference extends WidgetBase implements ContainerFactoryPluginInterf protected $fieldDisplayManager; /** + * @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface + */ + protected $keyValue; + + /** * The depth of the delete button. * * This property exists so it can be changed if subclasses. @@ -77,11 +83,14 @@ class EntityReference extends WidgetBase implements ContainerFactoryPluginInterf * Event dispatcher. * @param \Drupal\entity_browser\FieldWidgetDisplayManager $field_display_manager * Field widget display plugin manager. + * @param \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface $key_value + * The key value store. */ - public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, EntityManagerInterface $entity_manager, EventDispatcherInterface $event_dispatcher, FieldWidgetDisplayManager $field_display_manager) { + public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, EntityManagerInterface $entity_manager, EventDispatcherInterface $event_dispatcher, FieldWidgetDisplayManager $field_display_manager, KeyValueStoreExpirableInterface $key_value) { parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings); $this->entityManager = $entity_manager; $this->fieldDisplayManager = $field_display_manager; + $this->keyValue = $key_value; } /** @@ -96,7 +105,8 @@ class EntityReference extends WidgetBase implements ContainerFactoryPluginInterf $configuration['third_party_settings'], $container->get('entity.manager'), $container->get('event_dispatcher'), - $container->get('plugin.manager.entity_browser.field_widget_display') + $container->get('plugin.manager.entity_browser.field_widget_display'), + $container->get('keyvalue.expirable')->get('entity_browser') ); } @@ -375,12 +385,20 @@ class EntityReference extends WidgetBase implements ContainerFactoryPluginInterf ], ]; + + // Gather and set validators. + // @todo Is there a better place to do that? $cardinality = $this->fieldDefinition->getFieldStorageDefinition()->getCardinality(); + $validators = $this->prepareValidators([ + 'cardinality' => ['min' => $cardinality], + 'entity_type' => ['type' => $entity_type], + ]); + if ($cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED || count($ids) < $cardinality) { $entity_browser_uuid = sha1(implode('-', array_merge($form['#parents'], [$this->fieldDefinition->getName(), $delta]))); $entity_browser_display = $entity_browser->getDisplay(); $entity_browser_display->setUuid($entity_browser_uuid); - $element['entity_browser'] = $entity_browser_display->displayEntityBrowser($form_state); + $element['entity_browser'] = $entity_browser_display->displayEntityBrowser($form_state, $validators); $element['#attached']['library'][] = 'entity_browser/entity_reference'; $element['#attached']['drupalSettings']['entity_browser'] = [ $entity_browser->getDisplay()->getUuid() => [ @@ -540,4 +558,40 @@ class EntityReference extends WidgetBase implements ContainerFactoryPluginInterf ]; } + /** + * Prepare validators. + * + * Saves Entity Browser Widget validators in key/value storage if an identical + * set of constraints is not already stored there. + * + * @param array $validators + * An array where keys are validator ids and values configurations for them. + * + * @return string + * The hash generated from hashing the validators array. + */ + public function prepareValidators(array $validators) { + // Generate the hash that we use as key for the key/value. + $hash = md5(serialize($validators)); + + if (!$this->keyValue->has($hash)) { + $this->keyValue->set($hash, $validators); + } + + return $hash; + } + + /** + * Get validators. + * + * @param \Drupal\entity_browser\string $hash + * The hash generated from hashing the validators array. + * + * @return mixed + * An array where keys are validator ids and values configurations for them + * or empty array if no validators are stored. + */ + public function getValidators($hash) { + return $this->keyValue->get($hash, []); + } } diff --git a/src/WidgetBase.php b/src/WidgetBase.php index e1d702d..f35fe19 100644 --- a/src/WidgetBase.php +++ b/src/WidgetBase.php @@ -2,6 +2,7 @@ namespace Drupal\entity_browser; +use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface; use Drupal\Core\Plugin\PluginBase; use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Form\FormStateInterface; @@ -10,6 +11,7 @@ use Drupal\entity_browser\Events\EntitySelectionEvent; use Drupal\entity_browser\Events\Events; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\Request; /** * Base class for widget plugins. @@ -60,6 +62,20 @@ abstract class WidgetBase extends PluginBase implements WidgetInterface, Contain protected $entityManager; /** + * The Expirable key value store. + * + * @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface + */ + private $keyValue; + + /** + * The Request object. + * + * @var \Symfony\Component\HttpFoundation\Request + */ + private $request; + + /** * Constructs widget plugin. * * @param array $configuration @@ -71,11 +87,13 @@ abstract class WidgetBase extends PluginBase implements WidgetInterface, Contain * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher * Event dispatcher service. */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, EventDispatcherInterface $event_dispatcher, EntityManagerInterface $entity_manager) { + public function __construct(array $configuration, $plugin_id, $plugin_definition, EventDispatcherInterface $event_dispatcher, EntityManagerInterface $entity_manager, KeyValueStoreExpirableInterface $key_value, Request $request) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->eventDispatcher = $event_dispatcher; $this->entityManager = $entity_manager; $this->setConfiguration($configuration); + $this->keyValue = $key_value; + $this->request = $request; } /** @@ -87,7 +105,9 @@ abstract class WidgetBase extends PluginBase implements WidgetInterface, Contain $plugin_id, $plugin_definition, $container->get('event_dispatcher'), - $container->get('entity.manager') + $container->get('entity.manager'), + $container->get('keyvalue.expirable')->get('entity_browser'), + $container->get('request_stack')->getCurrentRequest() ); } @@ -187,7 +207,51 @@ abstract class WidgetBase extends PluginBase implements WidgetInterface, Contain /** * {@inheritdoc} */ - public function validate(array &$form, FormStateInterface $form_state) {} + public function prepareEntities(FormStateInterface $form_state) { + return []; + } + + /** + * {@inheritdoc} + */ + public function validate(array &$form, FormStateInterface $form_state) { + $trigger = $form_state->getTriggeringElement(); + + if (in_array('submit', $trigger['#array_parents'])) { + $entities = $this->prepareEntities($form_state); + $violations = $this->runWidgetValidators($entities); + if (!empty($violations)) { + /** @var \Symfony\Component\Validator\ConstraintViolationListInterface $violation */ + foreach ($violations as $violation) { + $form_state->setError($form['widget']['upload'], $violation->getMessage()); + } + } + } + } + + /** + * {@inheritdoc} + */ + public function runWidgetValidators(array $entities, $validators = []) { + // @todo Implement a centralized way to get arguments from path for all widgets? + if ($validators_hash = $this->request->get('validators')) { + $passed_validators = $this->keyValue->get($validators_hash); + + if (!empty($passed_validators)) { + $validators += $passed_validators; + } + } + + foreach ($validators as $validator_id => $options) { + /** @var \Drupal\entity_browser\WidgetValidationInterface $widget_validator */ + $widget_validator = \Drupal::service('plugin.manager.entity_browser.widget_validation')->createInstance($validator_id, []); + } + + if ($widget_validator) { + return $widget_validator->validate($entities, $options); + } + return []; + } /** * {@inheritdoc} diff --git a/src/WidgetInterface.php b/src/WidgetInterface.php index ddd3763..0146e7f 100644 --- a/src/WidgetInterface.php +++ b/src/WidgetInterface.php @@ -88,6 +88,20 @@ interface WidgetInterface extends PluginInspectionInterface, ConfigurablePluginI public function getForm(array &$original_form, FormStateInterface $form_state, array $aditional_widget_parameters); /** + * Prepares the entities without saving them. + * + * We need this method when we want to validation or perform other operations + * before submit. + * + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The form state object. + * + * @return \Drupal\Core\Entity\ContentEntityInterface[] + * Array of entities. + */ + public function prepareEntities(FormStateInterface $form_state); + + /** * Validates form. * * @param array $form @@ -98,6 +112,20 @@ interface WidgetInterface extends PluginInspectionInterface, ConfigurablePluginI public function validate(array &$form, FormStateInterface $form_state); /** + * Run widget validators. + * + * @param array $entities + * Array of entity ids to validate. + * @param array $validators + * Array of additional widget validator ids. + * + * @return \Symfony\Component\Validator\ConstraintViolationListInterface + * A list of constraint violations. If the list is empty, validation + * succeeded. + */ + public function runWidgetValidators(array $entities, $validators = []); + + /** * Submits form. * * @param array $element diff --git a/src/WidgetValidationBase.php b/src/WidgetValidationBase.php new file mode 100644 index 0000000..5200e58 --- /dev/null +++ b/src/WidgetValidationBase.php @@ -0,0 +1,64 @@ +label; + } + + /** + * {@inheritdoc} + */ + public function validate(array $entities, $options = []) {} + + /** + * {@inheritdoc} + */ + public function getConfiguration() { + return $this->configuration; + } + + /** + * {@inheritdoc} + */ + public function setConfiguration(array $configuration) { + $this->configuration = $configuration; + } + + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + return []; + } +} diff --git a/src/WidgetValidationInterface.php b/src/WidgetValidationInterface.php new file mode 100644 index 0000000..3ae0653 --- /dev/null +++ b/src/WidgetValidationInterface.php @@ -0,0 +1,40 @@ +alterInfo('entity_browser_widget_validation_info'); + $this->setCacheBackend($cache_backend, 'entity_browser_widget_validation_plugins'); + } + +}