diff --git a/core/modules/comment/src/Plugin/migrate/destination/EntityComment.php b/core/modules/comment/src/Plugin/migrate/destination/EntityComment.php
index 410fd6701a..c684ebc0a9 100644
--- a/core/modules/comment/src/Plugin/migrate/destination/EntityComment.php
+++ b/core/modules/comment/src/Plugin/migrate/destination/EntityComment.php
@@ -5,6 +5,7 @@
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Field\FieldTypePluginManagerInterface;
+use Drupal\Core\Session\AccountSwitcherInterface;
use Drupal\Core\State\StateInterface;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\Plugin\migrate\destination\EntityContentBase;
@@ -53,9 +54,15 @@ class EntityComment extends EntityContentBase {
* The field type plugin manager service.
* @param \Drupal\Core\State\StateInterface $state
* The state storage object.
+ * @param \Drupal\Core\Session\AccountSwitcherInterface $account_switcher
+ * The account switcher service.
*/
- public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityFieldManagerInterface $entity_field_manager, FieldTypePluginManagerInterface $field_type_manager, StateInterface $state) {
- parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles, $entity_field_manager, $field_type_manager);
+ public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityFieldManagerInterface $entity_field_manager, FieldTypePluginManagerInterface $field_type_manager, StateInterface $state, AccountSwitcherInterface $account_switcher = NULL) {
+ if ($account_switcher === NULL) {
+ @trigger_error('The account_switcher service must be passed to ' . __NAMESPACE__ . '\EntityComment::__construct(). It was added in drupal:9.1.0 and will be required before drupal:10.0.0.', E_USER_DEPRECATED);
+ $account_switcher = \Drupal::service('account_switcher');
+ }
+ parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles, $entity_field_manager, $field_type_manager, $account_switcher);
$this->state = $state;
}
@@ -73,7 +80,8 @@ public static function create(ContainerInterface $container, array $configuratio
array_keys($container->get('entity_type.bundle.info')->getBundleInfo($entity_type)),
$container->get('entity_field.manager'),
$container->get('plugin.manager.field.field_type'),
- $container->get('state')
+ $container->get('state'),
+ $container->get('account_switcher')
);
}
diff --git a/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php b/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php
index f3262a811a..b12ae11056 100644
--- a/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php
+++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php
@@ -8,6 +8,7 @@
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Field\FieldTypePluginManagerInterface;
+use Drupal\Core\Session\AccountSwitcherInterface;
use Drupal\Core\TypedData\TranslatableInterface;
use Drupal\Core\TypedData\TypedDataInterface;
use Drupal\migrate\Audit\HighestIdInterface;
@@ -17,6 +18,7 @@
use Drupal\migrate\MigrateException;
use Drupal\migrate\Plugin\MigrateIdMapInterface;
use Drupal\migrate\Row;
+use Drupal\user\EntityOwnerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@@ -102,6 +104,13 @@ class EntityContentBase extends Entity implements HighestIdInterface, MigrateVal
*/
protected $fieldTypeManager;
+ /**
+ * The account switcher service.
+ *
+ * @var \Drupal\Core\Session\AccountSwitcherInterface
+ */
+ protected $accountSwitcher;
+
/**
* Constructs a content entity.
*
@@ -121,11 +130,18 @@ class EntityContentBase extends Entity implements HighestIdInterface, MigrateVal
* The entity field manager.
* @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager
* The field type plugin manager service.
+ * @param \Drupal\Core\Session\AccountSwitcherInterface $account_switcher
+ * The account switcher service.
*/
- public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityFieldManagerInterface $entity_field_manager, FieldTypePluginManagerInterface $field_type_manager) {
+ public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityFieldManagerInterface $entity_field_manager, FieldTypePluginManagerInterface $field_type_manager, AccountSwitcherInterface $account_switcher = NULL) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles);
$this->entityFieldManager = $entity_field_manager;
$this->fieldTypeManager = $field_type_manager;
+ if ($account_switcher === NULL) {
+ @trigger_error('The account_switcher service must be passed to ' . __NAMESPACE__ . '\EntityContentBase::__construct(). It was added in drupal:9.1.0 and will be required before drupal:10.0.0.', E_USER_DEPRECATED);
+ $account_switcher = \Drupal::service('account_switcher');
+ }
+ $this->accountSwitcher = $account_switcher;
}
/**
@@ -141,7 +157,8 @@ public static function create(ContainerInterface $container, array $configuratio
$container->get('entity_type.manager')->getStorage($entity_type),
array_keys($container->get('entity_type.bundle.info')->getBundleInfo($entity_type)),
$container->get('entity_field.manager'),
- $container->get('plugin.manager.field.field_type')
+ $container->get('plugin.manager.field.field_type'),
+ $container->get('account_switcher')
);
}
@@ -184,8 +201,19 @@ public function isEntityValidationRequired(FieldableEntityInterface $entity) {
* {@inheritdoc}
*/
public function validateEntity(FieldableEntityInterface $entity) {
+ // Entity validation can require the user that owns the entity. Switch to
+ // use that user during validation.
+ $account = $entity instanceof EntityOwnerInterface ? $entity->getOwner() : NULL;
+ if ($account) {
+ $this->getAccountSwitcher()->switchTo($account);
+ }
+
$violations = $entity->validate();
+ if ($account) {
+ $this->getAccountSwitcher()->switchBack();
+ }
+
if (count($violations) > 0) {
throw new EntityValidationException($violations);
}
@@ -375,4 +403,17 @@ public function getHighestId() {
return (int) current($values);
}
+ /**
+ * Gets the account switcher service.
+ *
+ * @return \Drupal\Core\Session\AccountSwitcherInterface
+ * The account switcher service.
+ */
+ protected function getAccountSwitcher(): AccountSwitcherInterface {
+ if (!isset($this->accountSwitcher)) {
+ $this->accountSwitcher = \Drupal::service('account_switcher');
+ }
+ return $this->accountSwitcher;
+ }
+
}
diff --git a/core/modules/migrate/src/Plugin/migrate/destination/EntityRevision.php b/core/modules/migrate/src/Plugin/migrate/destination/EntityRevision.php
index 55ac460b5d..b387815724 100644
--- a/core/modules/migrate/src/Plugin/migrate/destination/EntityRevision.php
+++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityRevision.php
@@ -6,6 +6,7 @@
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Field\FieldTypePluginManagerInterface;
+use Drupal\Core\Session\AccountSwitcherInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\migrate\MigrateException;
use Drupal\migrate\Plugin\MigrationInterface;
@@ -114,11 +115,11 @@ class EntityRevision extends EntityContentBase {
/**
* {@inheritdoc}
*/
- public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityFieldManagerInterface $entity_field_manager, FieldTypePluginManagerInterface $field_type_manager) {
+ public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityFieldManagerInterface $entity_field_manager, FieldTypePluginManagerInterface $field_type_manager, AccountSwitcherInterface $account_switcher) {
$plugin_definition += [
'label' => new TranslatableMarkup('@entity_type revisions', ['@entity_type' => $storage->getEntityType()->getSingularLabel()]),
];
- parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles, $entity_field_manager, $field_type_manager);
+ parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles, $entity_field_manager, $field_type_manager, $account_switcher);
}
/**
diff --git a/core/modules/migrate/tests/src/Kernel/MigrateEntityContentValidationTest.php b/core/modules/migrate/tests/src/Kernel/MigrateEntityContentValidationTest.php
index c0cf029412..b84172e5d7 100644
--- a/core/modules/migrate/tests/src/Kernel/MigrateEntityContentValidationTest.php
+++ b/core/modules/migrate/tests/src/Kernel/MigrateEntityContentValidationTest.php
@@ -2,10 +2,15 @@
namespace Drupal\Tests\migrate\Kernel;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\filter\Entity\FilterFormat;
use Drupal\KernelTests\KernelTestBase;
use Drupal\migrate\Event\MigrateEvents;
use Drupal\migrate\Event\MigrateIdMapMessageEvent;
use Drupal\migrate\MigrateExecutable;
+use Drupal\user\Entity\Role;
+use Drupal\user\Entity\User;
use Drupal\user\Plugin\Validation\Constraint\UserNameConstraint;
/**
@@ -18,7 +23,16 @@ class MigrateEntityContentValidationTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
- protected static $modules = ['migrate', 'system', 'user', 'entity_test'];
+ protected static $modules = [
+ 'entity_test',
+ 'field',
+ 'filter',
+ 'filter_test',
+ 'migrate',
+ 'system',
+ 'text',
+ 'user',
+ ];
/**
* Messages accumulated during the migration run.
@@ -33,9 +47,11 @@ class MigrateEntityContentValidationTest extends KernelTestBase {
protected function setUp(): void {
parent::setUp();
- $this->installConfig(['system', 'user']);
$this->installEntitySchema('user');
+ $this->installEntitySchema('user_role');
$this->installEntitySchema('entity_test');
+ $this->installSchema('system', ['sequences']);
+ $this->installConfig(['field', 'filter_test', 'system', 'user']);
$this->container
->get('event_dispatcher')
@@ -140,6 +156,96 @@ public function test2() {
$this->assertArrayNotHasKey(3, $this->messages, 'Fourth message should not exist.');
}
+ /**
+ * Tests validation for entities that are instances of EntityOwnerInterface.
+ */
+ public function testEntityOwnerValidation() {
+ // Filter format access is impacted by user permissions.
+ /* @var \Drupal\filter\FilterFormatInterface $filter_test_format */
+ $filter_test_format = FilterFormat::load('filter_test');
+
+ // Create 2 users, an admin user who has filter permission and another who
+ // does not have said access.
+ /* @var \Drupal\user\RoleInterface $role */
+ $role = Role::create([
+ 'id' => 'admin',
+ 'label' => 'admin',
+ 'is_admin' => TRUE,
+ ]);
+ $role->grantPermission($filter_test_format->getPermissionName());
+ $role->save();
+ $admin_user = User::create([
+ 'name' => 'foobar',
+ 'mail' => 'foobar@example.com',
+ ]);
+ $admin_user->addRole($role->id());
+ $admin_user->save();
+ $normal_user = User::create([
+ 'name' => 'normal user',
+ 'mail' => 'normal@example.com',
+ ]);
+ $normal_user->save();
+
+ // Add a "body" field with the filter format.
+ $field_name = mb_strtolower($this->randomMachineName());
+ $field_storage = FieldStorageConfig::create([
+ 'field_name' => $field_name,
+ 'entity_type' => 'entity_test',
+ 'type' => 'text',
+ ]);
+ $field_storage->save();
+ FieldConfig::create([
+ 'field_storage' => $field_storage,
+ 'bundle' => 'entity_test',
+ ])->save();
+
+ // Attempt to migrate entities. One owned by the admin user, the other is a
+ // non-permissioned user.
+ $definition = [
+ 'source' => [
+ 'plugin' => 'embedded_data',
+ 'data_rows' => [
+ [
+ 'id' => 1,
+ 'uid' => $admin_user->id(),
+ 'body' => [
+ 'value' => 'foo',
+ 'format' => 'filter_test',
+ ],
+ ],
+ [
+ 'id' => 2,
+ 'uid' => $normal_user->id(),
+ 'body' => [
+ 'value' => 'bar',
+ 'format' => 'filter_test',
+ ],
+ ],
+ ],
+ 'ids' => [
+ 'id' => ['type' => 'integer'],
+ ],
+ ],
+ 'process' => [
+ 'id' => 'id',
+ 'user_id' => 'uid',
+ "$field_name/value" => 'body/value',
+ "$field_name/format" => 'body/format',
+ ],
+ 'destination' => [
+ 'plugin' => 'entity:entity_test',
+ 'validate' => TRUE,
+ ],
+ ];
+ $this->container->get('current_user')->setAccount($normal_user);
+ $this->runImport($definition);
+
+ // The second user import should fail validation because they do not have
+ // access to use "filter_test" filter.
+ $this->assertSame(sprintf('2: [entity_test: 2]: user_id.0.target_id=This entity (user: %s) cannot be referenced.||%s.0.format=The value you selected is not a valid choice.', $normal_user->id(), $field_name), $this->messages[0], 'First message should have 2 validation errors.');
+ $this->assertArrayNotHasKey(1, $this->messages, 'Second message should not exist.');
+ }
+
/**
* Reacts to map message event.
*
diff --git a/core/modules/user/src/Plugin/migrate/destination/EntityUser.php b/core/modules/user/src/Plugin/migrate/destination/EntityUser.php
index 5ee10f7685..07d3f59cfb 100644
--- a/core/modules/user/src/Plugin/migrate/destination/EntityUser.php
+++ b/core/modules/user/src/Plugin/migrate/destination/EntityUser.php
@@ -8,6 +8,7 @@
use Drupal\Core\Field\FieldTypePluginManagerInterface;
use Drupal\Core\Field\Plugin\Field\FieldType\EmailItem;
use Drupal\Core\Password\PasswordInterface;
+use Drupal\Core\Session\AccountSwitcherInterface;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\Plugin\migrate\destination\EntityContentBase;
use Drupal\migrate\Row;
@@ -95,8 +96,14 @@ class EntityUser extends EntityContentBase {
* The field type plugin manager service.
* @param \Drupal\Core\Password\PasswordInterface $password
* The password service.
+ * @param \Drupal\Core\Session\AccountSwitcherInterface $account_switcher
+ * The account switcher service.
*/
- public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityFieldManagerInterface $entity_field_manager, FieldTypePluginManagerInterface $field_type_manager, PasswordInterface $password) {
+ public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityFieldManagerInterface $entity_field_manager, FieldTypePluginManagerInterface $field_type_manager, PasswordInterface $password, AccountSwitcherInterface $account_switcher = NULL) {
+ if ($account_switcher === NULL) {
+ @trigger_error('The account_switcher service must be passed to ' . __NAMESPACE__ . '\EntityUser::__construct(). It was added in drupal:9.1.0 and will be required before drupal:10.0.0.', E_USER_DEPRECATED);
+ $account_switcher = \Drupal::service('account_switcher');
+ }
parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles, $entity_field_manager, $field_type_manager);
$this->password = $password;
}
@@ -115,7 +122,8 @@ public static function create(ContainerInterface $container, array $configuratio
array_keys($container->get('entity_type.bundle.info')->getBundleInfo($entity_type)),
$container->get('entity_field.manager'),
$container->get('plugin.manager.field.field_type'),
- $container->get('password')
+ $container->get('password'),
+ $container->get('account_switcher')
);
}