diff --git a/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionInterface.php b/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionInterface.php index f240c19..286793b 100644 --- a/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionInterface.php +++ b/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionInterface.php @@ -33,7 +33,7 @@ public function getReferenceableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0); /** - * Counts entities that are referenceable by a given field. + * Counts entities that are referenceable. * * @return int * The number of referenceable entities. @@ -41,7 +41,7 @@ public function getReferenceableEntities($match = NULL, $match_operator = 'CONTA public function countReferenceableEntities($match = NULL, $match_operator = 'CONTAINS'); /** - * Validates that entities can be referenced by this field. + * Validates that existing entities can be referenced. * * @return array * An array of valid entity IDs. @@ -49,10 +49,14 @@ public function countReferenceableEntities($match = NULL, $match_operator = 'CON public function validateReferenceableEntities(array $ids); /** - * Validates that entities can be referenced by this field. + * Validates that entities can be referenced. * * @todo naming... * + * This method should replicate the logic implemented by + * validateReferenceableEntities(), but applied to newly created entities that + * have not been saved yet. + * * @param \Drupal\Core\Entity\EntityInterface[] $entities * An array of entities to check. * diff --git a/core/lib/Drupal/Core/Entity/Plugin/EntityReferenceSelection/SelectionBase.php b/core/lib/Drupal/Core/Entity/Plugin/EntityReferenceSelection/SelectionBase.php index 7522d86..33ffc07 100644 --- a/core/lib/Drupal/Core/Entity/Plugin/EntityReferenceSelection/SelectionBase.php +++ b/core/lib/Drupal/Core/Entity/Plugin/EntityReferenceSelection/SelectionBase.php @@ -254,12 +254,12 @@ public function countReferenceableEntities($match = NULL, $match_operator = 'CON /** * {@inheritdoc} */ - public function validateReferenceableEntities(array $ids, $strict = TRUE) { + public function validateReferenceableEntities(array $ids) { $result = array(); if ($ids) { $target_type = $this->configuration['target_type']; $entity_type = $this->entityManager->getDefinition($target_type); - $query = $this->buildEntityQuery(NULL, 'CONTAINS', $strict); + $query = $this->buildEntityQuery(); $result = $query ->condition($entity_type->getKey('id'), $ids, 'IN') ->execute(); diff --git a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/ValidReferenceConstraint.php b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/ValidReferenceConstraint.php index 79cf120..3178e7a 100644 --- a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/ValidReferenceConstraint.php +++ b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/ValidReferenceConstraint.php @@ -29,7 +29,21 @@ class ValidReferenceConstraint extends Constraint { public $message = 'This entity (%type: %id) cannot be referenced.'; /** - * Validation message when the target_id is empty. + * Violation message when the entity does not exist. + * + * @var string + */ + public $nonExistingMessage = 'The referenced entity (%type: %id) does not exist.'; + + /** + * Violation message when a new entity ("autocreate") is invalid. + * + * @var string + */ + public $invalidAutocreateMessage = 'This entity (%type: %label)cannot be referenced.'; + + /** + * Violation message when the target_id is empty. * * @var string */ diff --git a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/ValidReferenceConstraintValidator.php b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/ValidReferenceConstraintValidator.php index 557ebb0..b43c28c 100644 --- a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/ValidReferenceConstraintValidator.php +++ b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/ValidReferenceConstraintValidator.php @@ -7,13 +7,54 @@ namespace Drupal\Core\Entity\Plugin\Validation\Constraint; +use Drupal\Core\DependencyInjection\ContainerInjectionInterface; +use Drupal\Core\Entity\EntityManagerInterface; +use Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; /** * Checks if referenced entities are valid. */ -class ValidReferenceConstraintValidator extends ConstraintValidator { +class ValidReferenceConstraintValidator extends ConstraintValidator implements ContainerInjectionInterface { + + /** + * The selection plugin manager. + * + * @var \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface + */ + protected $selectionManager; + + /** + * The entity manager. + * + * @var \Drupal\Core\Entity\EntityManagerInterface + */ + protected $entityManager; + + /** + * Constructs a ValidReferenceConstraintValidator object. + * + * @param \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface $selection_manager + * The selection plugin manager. + * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager + * The entity manager. + */ + public function __construct(SelectionPluginManagerInterface $selection_manager, EntityManagerInterface $entity_manager) { + $this->selectionManager = $selection_manager; + $this->entityManager = $entity_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin.manager.entity_reference_selection'), + $container->get('entity.manager') + ); + } /** * {@inheritdoc} @@ -49,30 +90,43 @@ public function validate($value, Constraint $constraint) { } } - if ($new_entities || $target_ids) { - /** @var \Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface $handler * */ - $handler = \Drupal::service('plugin.manager.entity_reference_selection')->getSelectionHandler($value->getFieldDefinition()); - $target_type = $value->getFieldDefinition()->getSetting('target_type'); + // Early opt-out if nothing to validate. + if (!$new_entities && !$target_ids) { + return; + } - // Add violations on deltas with a new entity that is not valid. - if ($new_entities) { - $valid_new_entities = $handler->validateReferenceableNewEntities($new_entities); - foreach (array_diff_key($new_entities, $valid_new_entities) as $delta => $entity) { - // @todo adjust message - $this->context->buildViolation('Invalid new entity') - ->setParameter('%type', $target_type) - ->atPath((string) $delta . '.entity') - ->setInvalidValue($entity) - ->addViolation(); - } + /** @var \Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface $handler * */ + $handler = $this->selectionManager->getSelectionHandler($value->getFieldDefinition()); + $target_type_id = $value->getFieldDefinition()->getSetting('target_type'); + + // Add violations on deltas with a new entity that is not valid. + if ($new_entities) { + $valid_new_entities = $handler->validateReferenceableNewEntities($new_entities); + $invalid_new_entities = array_diff_key($new_entities, $valid_new_entities); + foreach ($invalid_new_entities as $delta => $entity) { + $this->context->buildViolation($constraint->invalidAutocreateMessage) + ->setParameter('%type', $target_type_id) + ->setParameter('%label', $entity->label()) + ->atPath((string) $delta . '.entity') + ->setInvalidValue($entity) + ->addViolation(); } + } - // Add violations on deltas with a target_id that is not valid. - if ($target_ids) { - $valid_target_ids = $handler->validateReferenceableEntities($target_ids, FALSE); - foreach (array_diff($target_ids, $valid_target_ids) as $delta => $target_id) { - $this->context->buildViolation($constraint->message) - ->setParameter('%type', $target_type) + // Add violations on deltas with a target_id that is not valid. + if ($target_ids) { + $valid_target_ids = $handler->validateReferenceableEntities($target_ids); + if ($invalid_target_ids = array_diff($target_ids, $valid_target_ids)) { + // For accuracy of the error message, differentiate non-referenceable + // and non-existent entities. + $target_type = $this->entityManager->getDefinition($target_type_id); + $existing_ids = $this->entityManager->getStorage($target_type_id)->getQuery() + ->condition($target_type->getKey('id'), $invalid_target_ids, 'IN') + ->execute(); + foreach ($invalid_target_ids as $delta => $target_id) { + $message = in_array($target_id, $existing_ids) ? $constraint->message : $constraint->nonExistingMessage; + $this->context->buildViolation($message) + ->setParameter('%type', $target_type_id) ->setParameter('%id', $target_id) ->atPath((string) $delta . '.target_id') ->setInvalidValue($target_id) @@ -80,7 +134,6 @@ public function validate($value, Constraint $constraint) { } } } - } } diff --git a/core/modules/file/src/Plugin/EntityReferenceSelection/FileSelection.php b/core/modules/file/src/Plugin/EntityReferenceSelection/FileSelection.php index 1a59d58..9250733 100644 --- a/core/modules/file/src/Plugin/EntityReferenceSelection/FileSelection.php +++ b/core/modules/file/src/Plugin/EntityReferenceSelection/FileSelection.php @@ -25,11 +25,18 @@ class FileSelection extends SelectionBase { /** * {@inheritdoc} */ - protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS', $strict = TRUE) { + protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') { $query = parent::buildEntityQuery($match, $match_operator); - if ($strict) { - $query->condition('status', FILE_STATUS_PERMANENT); - } + // Allow referencing : + // - files with status "permanent" + // - or files uploaded by the current user with the last 2 seconds (since + // newly uploaded files only become "permanent" after the containing + // entity gets validated and saved.) + $query->condition($query->orConditionGroup() + ->condition('status', FILE_STATUS_PERMANENT) + ->condition($query->andConditionGroup() + ->condition('uid', $this->currentUser->id()) + ->condition('created', time() - (60 * 2), '>'))); return $query; } diff --git a/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php b/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php index 35e5004..0d7fe13 100644 --- a/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php +++ b/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php @@ -346,7 +346,7 @@ public static function generateSampleValue(FieldDefinitionInterface $field_defin if ($path = $random->image(drupal_realpath($destination), $min_resolution, $max_resolution)) { $image = File::create(); $image->setFileUri($path); - // $image->setOwner($account); + $image->setOwnerId(\Drupal::currentUser()->id()); $image->setMimeType('image/' . pathinfo($path, PATHINFO_EXTENSION)); $image->setFileName(drupal_basename($path)); $destination_dir = $settings['uri_scheme'] . '://' . $settings['file_directory']; diff --git a/core/modules/system/src/Tests/Entity/EntityReferenceFieldTest.php b/core/modules/system/src/Tests/Entity/EntityReferenceFieldTest.php index 3195e1d..f74410f 100644 --- a/core/modules/system/src/Tests/Entity/EntityReferenceFieldTest.php +++ b/core/modules/system/src/Tests/Entity/EntityReferenceFieldTest.php @@ -103,7 +103,7 @@ public function testEntityReferenceFieldValidation() { $entity->{$this->fieldName}->target_id = 9999; $violations = $entity->{$this->fieldName}->validate(); $this->assertEqual($violations->count(), 1, 'Validation throws a violation.'); - $this->assertEqual($violations[0]->getMessage(), t('This entity (%type: %id) cannot be referenced.', array('%type' => $this->referencedEntityType, '%id' => 9999))); + $this->assertEqual($violations[0]->getMessage(), t('The referenced entity (%type: %id) does not exist.', array('%type' => $this->referencedEntityType, '%id' => 9999))); // @todo Implement a test case for invalid bundle references after // https://www.drupal.org/node/2064191 is fixed. diff --git a/core/modules/system/src/Tests/Entity/EntityValidationTest.php b/core/modules/system/src/Tests/Entity/EntityValidationTest.php index 81951df..aa85fa7 100644 --- a/core/modules/system/src/Tests/Entity/EntityValidationTest.php +++ b/core/modules/system/src/Tests/Entity/EntityValidationTest.php @@ -165,7 +165,7 @@ protected function checkValidation($entity_type) { $test_entity->set('user_id', 9999); $violations = $test_entity->validate(); $this->assertEqual($violations->count(), 1, 'Validation failed.'); - $this->assertEqual($violations[0]->getMessage(), t('This entity (%type: %id) cannot be referenced.', array('%type' => 'user', '%id' => 9999))); + $this->assertEqual($violations[0]->getMessage(), t('The referenced entity (%type: %id) does not exist.', array('%type' => 'user', '%id' => 9999))); $test_entity = clone $entity; $test_entity->field_test_text->format = $this->randomString(33); diff --git a/core/modules/system/src/Tests/Entity/ValidReferenceConstraintValidatorTest.php b/core/modules/system/src/Tests/Entity/ValidReferenceConstraintValidatorTest.php index d81a124..ef38189 100644 --- a/core/modules/system/src/Tests/Entity/ValidReferenceConstraintValidatorTest.php +++ b/core/modules/system/src/Tests/Entity/ValidReferenceConstraintValidatorTest.php @@ -65,7 +65,7 @@ public function testValidation() { // Make sure the information provided by a violation is correct. $violation = $violations[0]; - $this->assertEqual($violation->getMessage(), t('This entity (%type: %id) cannot be referenced.', array( + $this->assertEqual($violation->getMessage(), t('The referenced entity (%type: %id) does not exist.', array( '%type' => 'user', '%id' => $entity->id(), )), 'The message for invalid value is correct.'); diff --git a/core/modules/taxonomy/src/Tests/TermValidationTest.php b/core/modules/taxonomy/src/Tests/TermValidationTest.php index a0db7fc..b369737 100644 --- a/core/modules/taxonomy/src/Tests/TermValidationTest.php +++ b/core/modules/taxonomy/src/Tests/TermValidationTest.php @@ -63,7 +63,7 @@ public function testValidation() { $term->set('parent', 9999); $violations = $term->validate(); $this->assertEqual(count($violations), 1, 'Violation found when term parent is invalid.'); - $this->assertEqual($violations[0]->getMessage(), format_string('This entity (%type: %id) cannot be referenced.', array('%type' => 'taxonomy_term', '%id' => 9999))); + $this->assertEqual($violations[0]->getMessage(), format_string('The referenced entity (%type: %id) does not exist.', array('%type' => 'taxonomy_term', '%id' => 9999))); $term->set('parent', 0); $violations = $term->validate(); diff --git a/core/modules/user/src/Tests/UserValidationTest.php b/core/modules/user/src/Tests/UserValidationTest.php index 1be7b0c..6bd25cc 100644 --- a/core/modules/user/src/Tests/UserValidationTest.php +++ b/core/modules/user/src/Tests/UserValidationTest.php @@ -179,7 +179,7 @@ function testValidation() { $violations = $user->validate(); $this->assertEqual(count($violations), 1); $this->assertEqual($violations[0]->getPropertyPath(), 'roles.1.target_id'); - $this->assertEqual($violations[0]->getMessage(), t('This entity (%entity_type: %name) cannot be referenced.', array('%entity_type' => 'user_role', '%name' => 'unknown_role'))); + $this->assertEqual($violations[0]->getMessage(), t('The referenced entity (%entity_type: %name) does not exist.', array('%entity_type' => 'user_role', '%name' => 'unknown_role'))); } /**