Loading .recipes.gitlab-ci.yml +1 −1 Original line number Diff line number Diff line Loading @@ -20,7 +20,7 @@ variables: MYSQL_PASSWORD: drupaltestbotpw # Note if you add anything to the lists below you will need to change the root # phpunit.xml.dist file. TEST_DIRECTORIES: "core/tests/Drupal/Tests/Core/Recipe core/tests/Drupal/KernelTests/Core/Recipe core/tests/Drupal/FunctionalTests/Core/Recipe core/tests/Drupal/KernelTests/Core/Config/Action core/tests/Drupal/KernelTests/Core/Config/Storage/Checkpoint core/tests/Drupal/Tests/Core/Config/Checkpoint core/tests/Drupal/Tests/Core/Config/Action core/modules/content_moderation/tests/src/Kernel/ConfigAction core/modules/ckeditor5/tests/src/Kernel/ConfigAction core/tests/Drupal/Tests/Core/DefaultContent core/tests/Drupal/FunctionalTests/DefaultContent" TEST_DIRECTORIES: "core/tests/Drupal/Tests/Core/Recipe core/tests/Drupal/KernelTests/Core/Recipe core/tests/Drupal/FunctionalTests/Core/Recipe core/tests/Drupal/KernelTests/Core/Config/Action core/tests/Drupal/KernelTests/Core/Config/Storage/Checkpoint core/tests/Drupal/Tests/Core/Config/Checkpoint core/tests/Drupal/Tests/Core/Config/Action core/modules/content_moderation/tests/src/Kernel/ConfigAction core/modules/ckeditor5/tests/src/Kernel/ConfigAction core/tests/Drupal/Tests/Core/DefaultContent core/tests/Drupal/KernelTests/Core/DefaultContent core/tests/Drupal/FunctionalTests/DefaultContent" CODE_DIRECTORIES: "core/lib/Drupal/Core/Recipe core/lib/Drupal/Core/Config/Action core/modules/config/tests/config_action_duplicate_test core/tests/fixtures/recipes core/lib/Drupal/Core/Config/Checkpoint core/modules/content_moderation/src/Plugin/ConfigAction core/modules/ckeditor5/src/Plugin/ConfigAction core/lib/Drupal/Core/DefaultContent" ALL_DIRECTORIES: "${CODE_DIRECTORIES} ${TEST_DIRECTORIES}" Loading core/core.services.yml +5 −0 Original line number Diff line number Diff line Loading @@ -73,6 +73,11 @@ services: arguments: ['@config.manager', '@config.storage', '@config.typed', '@config.factory'] Drupal\Core\DefaultContent\Importer: autowire: true Drupal\Core\DefaultContent\AdminAccountSwitcher: arguments: $isSuperUserAccessEnabled: '%security.enable_super_user%' autowire: true public: false # Simple cache contexts, directly derived from the request context. cache_context.ip: class: Drupal\Core\Cache\Context\IpCacheContext Loading core/lib/Drupal/Core/DefaultContent/AdminAccountSwitcher.php 0 → 100644 +83 −0 Original line number Diff line number Diff line <?php declare(strict_types=1); namespace Drupal\Core\DefaultContent; use Drupal\Core\Access\AccessException; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Session\AccountSwitcherInterface; /** * @internal * This API is experimental. */ final class AdminAccountSwitcher implements AccountSwitcherInterface { public function __construct( private readonly AccountSwitcherInterface $decorated, private readonly EntityTypeManagerInterface $entityTypeManager, private readonly bool $isSuperUserAccessEnabled, ) {} /** * Switches to an administrative account. * * This will switch to the first available account with a role that has the * `is_admin` flag. If there are no such roles, or no such users, this will * try to switch to user 1 if superuser access is enabled. * * @return \Drupal\Core\Session\AccountInterface * The account that was switched to. * * @throws \Drupal\Core\Access\AccessException * Thrown if there are no users with administrative roles. */ public function switchToAdministrator(): AccountInterface { $admin_roles = $this->entityTypeManager->getStorage('user_role') ->getQuery() ->condition('is_admin', TRUE) ->execute(); $user_storage = $this->entityTypeManager->getStorage('user'); if ($admin_roles) { $accounts = $user_storage->getQuery() ->accessCheck(FALSE) ->condition('roles', $admin_roles, 'IN') ->condition('status', 1) ->sort('uid') ->range(0, 1) ->execute(); } else { $accounts = []; } $account = $user_storage->load(reset($accounts) ?: 1); assert($account instanceof AccountInterface); if (array_intersect($account->getRoles(), $admin_roles) || ((int) $account->id() === 1 && $this->isSuperUserAccessEnabled)) { $this->switchTo($account); return $account; } throw new AccessException("There are no user accounts with administrative roles."); } /** * {@inheritdoc} */ public function switchTo(AccountInterface $account): AccountSwitcherInterface { $this->decorated->switchTo($account); return $this; } /** * {@inheritdoc} */ public function switchBack(): AccountSwitcherInterface { $this->decorated->switchBack(); return $this; } } core/lib/Drupal/Core/DefaultContent/Importer.php +37 −36 Original line number Diff line number Diff line Loading @@ -13,7 +13,6 @@ use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Installer\InstallerKernel; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Session\AccountSwitcherInterface; use Drupal\file\FileInterface; use Drupal\link\Plugin\Field\FieldType\LinkItem; use Drupal\user\EntityOwnerInterface; Loading Loading @@ -42,7 +41,7 @@ final class Importer implements LoggerAwareInterface { public function __construct( private readonly EntityTypeManagerInterface $entityTypeManager, private readonly AccountSwitcherInterface $accountSwitcher, private readonly AdminAccountSwitcher $accountSwitcher, private readonly FileSystemInterface $fileSystem, private readonly LanguageManagerInterface $languageManager, private readonly EntityRepositoryInterface $entityRepository, Loading Loading @@ -71,10 +70,9 @@ public function importContent(Finder $content, Existing $existing = Existing::Er return; } /** @var \Drupal\user\UserInterface $root_user */ $root_user = $this->entityTypeManager->getStorage('user')->load(1); $this->accountSwitcher->switchTo($root_user); $account = $this->accountSwitcher->switchToAdministrator(); try { /** @var array{_meta: array<mixed>} $decoded */ foreach ($content->data as $decoded) { ['uuid' => $uuid, 'entity_type' => $entity_type_id, 'path' => $path] = $decoded['_meta']; Loading Loading @@ -102,7 +100,7 @@ public function importContent(Finder $content, Existing $existing = Existing::Er // Ensure that the entity is not owned by the anonymous user. if ($entity instanceof EntityOwnerInterface && empty($entity->getOwnerId())) { $entity->setOwner($root_user); $entity->setOwnerId($account->id()); } // If a file exists in the same folder, copy it to the designated Loading @@ -112,8 +110,11 @@ public function importContent(Finder $content, Existing $existing = Existing::Er } $entity->save(); } } finally { $this->accountSwitcher->switchBack(); } } private function copyFileAssociatedWithEntity(string $path, FileInterface $entity): void { $destination = $entity->getFileUri(); Loading core/tests/Drupal/FunctionalTests/Core/Recipe/CoreRecipesTest.php +1 −0 Original line number Diff line number Diff line Loading @@ -64,6 +64,7 @@ public function providerApplyRecipe(): iterable { * @dataProvider providerApplyRecipe */ public function testApplyRecipe(string $path): void { $this->setUpCurrentUser(admin: TRUE); $this->applyRecipe($path); } Loading Loading
.recipes.gitlab-ci.yml +1 −1 Original line number Diff line number Diff line Loading @@ -20,7 +20,7 @@ variables: MYSQL_PASSWORD: drupaltestbotpw # Note if you add anything to the lists below you will need to change the root # phpunit.xml.dist file. TEST_DIRECTORIES: "core/tests/Drupal/Tests/Core/Recipe core/tests/Drupal/KernelTests/Core/Recipe core/tests/Drupal/FunctionalTests/Core/Recipe core/tests/Drupal/KernelTests/Core/Config/Action core/tests/Drupal/KernelTests/Core/Config/Storage/Checkpoint core/tests/Drupal/Tests/Core/Config/Checkpoint core/tests/Drupal/Tests/Core/Config/Action core/modules/content_moderation/tests/src/Kernel/ConfigAction core/modules/ckeditor5/tests/src/Kernel/ConfigAction core/tests/Drupal/Tests/Core/DefaultContent core/tests/Drupal/FunctionalTests/DefaultContent" TEST_DIRECTORIES: "core/tests/Drupal/Tests/Core/Recipe core/tests/Drupal/KernelTests/Core/Recipe core/tests/Drupal/FunctionalTests/Core/Recipe core/tests/Drupal/KernelTests/Core/Config/Action core/tests/Drupal/KernelTests/Core/Config/Storage/Checkpoint core/tests/Drupal/Tests/Core/Config/Checkpoint core/tests/Drupal/Tests/Core/Config/Action core/modules/content_moderation/tests/src/Kernel/ConfigAction core/modules/ckeditor5/tests/src/Kernel/ConfigAction core/tests/Drupal/Tests/Core/DefaultContent core/tests/Drupal/KernelTests/Core/DefaultContent core/tests/Drupal/FunctionalTests/DefaultContent" CODE_DIRECTORIES: "core/lib/Drupal/Core/Recipe core/lib/Drupal/Core/Config/Action core/modules/config/tests/config_action_duplicate_test core/tests/fixtures/recipes core/lib/Drupal/Core/Config/Checkpoint core/modules/content_moderation/src/Plugin/ConfigAction core/modules/ckeditor5/src/Plugin/ConfigAction core/lib/Drupal/Core/DefaultContent" ALL_DIRECTORIES: "${CODE_DIRECTORIES} ${TEST_DIRECTORIES}" Loading
core/core.services.yml +5 −0 Original line number Diff line number Diff line Loading @@ -73,6 +73,11 @@ services: arguments: ['@config.manager', '@config.storage', '@config.typed', '@config.factory'] Drupal\Core\DefaultContent\Importer: autowire: true Drupal\Core\DefaultContent\AdminAccountSwitcher: arguments: $isSuperUserAccessEnabled: '%security.enable_super_user%' autowire: true public: false # Simple cache contexts, directly derived from the request context. cache_context.ip: class: Drupal\Core\Cache\Context\IpCacheContext Loading
core/lib/Drupal/Core/DefaultContent/AdminAccountSwitcher.php 0 → 100644 +83 −0 Original line number Diff line number Diff line <?php declare(strict_types=1); namespace Drupal\Core\DefaultContent; use Drupal\Core\Access\AccessException; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Session\AccountSwitcherInterface; /** * @internal * This API is experimental. */ final class AdminAccountSwitcher implements AccountSwitcherInterface { public function __construct( private readonly AccountSwitcherInterface $decorated, private readonly EntityTypeManagerInterface $entityTypeManager, private readonly bool $isSuperUserAccessEnabled, ) {} /** * Switches to an administrative account. * * This will switch to the first available account with a role that has the * `is_admin` flag. If there are no such roles, or no such users, this will * try to switch to user 1 if superuser access is enabled. * * @return \Drupal\Core\Session\AccountInterface * The account that was switched to. * * @throws \Drupal\Core\Access\AccessException * Thrown if there are no users with administrative roles. */ public function switchToAdministrator(): AccountInterface { $admin_roles = $this->entityTypeManager->getStorage('user_role') ->getQuery() ->condition('is_admin', TRUE) ->execute(); $user_storage = $this->entityTypeManager->getStorage('user'); if ($admin_roles) { $accounts = $user_storage->getQuery() ->accessCheck(FALSE) ->condition('roles', $admin_roles, 'IN') ->condition('status', 1) ->sort('uid') ->range(0, 1) ->execute(); } else { $accounts = []; } $account = $user_storage->load(reset($accounts) ?: 1); assert($account instanceof AccountInterface); if (array_intersect($account->getRoles(), $admin_roles) || ((int) $account->id() === 1 && $this->isSuperUserAccessEnabled)) { $this->switchTo($account); return $account; } throw new AccessException("There are no user accounts with administrative roles."); } /** * {@inheritdoc} */ public function switchTo(AccountInterface $account): AccountSwitcherInterface { $this->decorated->switchTo($account); return $this; } /** * {@inheritdoc} */ public function switchBack(): AccountSwitcherInterface { $this->decorated->switchBack(); return $this; } }
core/lib/Drupal/Core/DefaultContent/Importer.php +37 −36 Original line number Diff line number Diff line Loading @@ -13,7 +13,6 @@ use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Installer\InstallerKernel; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Session\AccountSwitcherInterface; use Drupal\file\FileInterface; use Drupal\link\Plugin\Field\FieldType\LinkItem; use Drupal\user\EntityOwnerInterface; Loading Loading @@ -42,7 +41,7 @@ final class Importer implements LoggerAwareInterface { public function __construct( private readonly EntityTypeManagerInterface $entityTypeManager, private readonly AccountSwitcherInterface $accountSwitcher, private readonly AdminAccountSwitcher $accountSwitcher, private readonly FileSystemInterface $fileSystem, private readonly LanguageManagerInterface $languageManager, private readonly EntityRepositoryInterface $entityRepository, Loading Loading @@ -71,10 +70,9 @@ public function importContent(Finder $content, Existing $existing = Existing::Er return; } /** @var \Drupal\user\UserInterface $root_user */ $root_user = $this->entityTypeManager->getStorage('user')->load(1); $this->accountSwitcher->switchTo($root_user); $account = $this->accountSwitcher->switchToAdministrator(); try { /** @var array{_meta: array<mixed>} $decoded */ foreach ($content->data as $decoded) { ['uuid' => $uuid, 'entity_type' => $entity_type_id, 'path' => $path] = $decoded['_meta']; Loading Loading @@ -102,7 +100,7 @@ public function importContent(Finder $content, Existing $existing = Existing::Er // Ensure that the entity is not owned by the anonymous user. if ($entity instanceof EntityOwnerInterface && empty($entity->getOwnerId())) { $entity->setOwner($root_user); $entity->setOwnerId($account->id()); } // If a file exists in the same folder, copy it to the designated Loading @@ -112,8 +110,11 @@ public function importContent(Finder $content, Existing $existing = Existing::Er } $entity->save(); } } finally { $this->accountSwitcher->switchBack(); } } private function copyFileAssociatedWithEntity(string $path, FileInterface $entity): void { $destination = $entity->getFileUri(); Loading
core/tests/Drupal/FunctionalTests/Core/Recipe/CoreRecipesTest.php +1 −0 Original line number Diff line number Diff line Loading @@ -64,6 +64,7 @@ public function providerApplyRecipe(): iterable { * @dataProvider providerApplyRecipe */ public function testApplyRecipe(string $path): void { $this->setUpCurrentUser(admin: TRUE); $this->applyRecipe($path); } Loading