diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..8bf5a7ba5fd4b5838974427e677b2e2f62e025d1 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,43 @@ +################ +# GitLabCI template for Drupal projects. +# +# This template is designed to give any Contrib maintainer everything they need to test, without requiring modification. +# It is also designed to keep up to date with Core Development automatically through the use of include files that can be centrally maintained. +# As long as you include the project, ref and three files below, any future updates added by the Drupal Association will be used in your +# pipelines automatically. However, you can modify this template if you have additional needs for your project. +# The full documentation is on https://project.pages.drupalcode.org/gitlab_templates/ +################ + +# For information on alternative values for 'ref' see https://project.pages.drupalcode.org/gitlab_templates/info/templates-version/ +# To test a Drupal 7 project, change the first include filename from .main.yml to .main-d7.yml +include: + - project: $_GITLAB_TEMPLATES_REPO + ref: $_GITLAB_TEMPLATES_REF + file: + - '/includes/include.drupalci.main.yml' + - '/includes/include.drupalci.variables.yml' + - '/includes/include.drupalci.workflows.yml' +# +################ +# Pipeline configuration variables are defined with default values and descriptions in the file +# https://git.drupalcode.org/project/gitlab_templates/-/blob/main/includes/include.drupalci.variables.yml +# Uncomment the lines below if you want to override any of the variables. The following is just an example. +################ +variables: + # @todo https://www.drupal.org/project/entity_reference_integrity/issues/3455209 + OPT_IN_TEST_CURRENT: 0 + OPT_IN_TEST_PREVIOUS_MAJOR: 1 + OPT_IN_TEST_MAX_PHP: 1 + _PHPUNIT_CONCURRENT: 1 + +phpunit: + parallel: + matrix: + - _TARGET_DB_TYPE: 'mysql' + _TARGET_DB_VERSION: '8' + - _TARGET_DB_TYPE: 'pgsql' + _TARGET_DB_VERSION: '16' + - _TARGET_DB_TYPE: 'mariadb' + _TARGET_DB_VERSION: '10.6' + - _TARGET_DB_TYPE: 'sqlite' + _TARGET_DB_VERSION: '3' diff --git a/entity_reference_integrity.info.yml b/entity_reference_integrity.info.yml index 39b89c821f5f0f37cb2484684b14d58f1ddcb66a..599bc2424753d19d311aa189ec231146aa832a29 100644 --- a/entity_reference_integrity.info.yml +++ b/entity_reference_integrity.info.yml @@ -1,6 +1,6 @@ name: Entity Reference Integrity description: Protect entities from being deleted if they are the target of an entity reference field. -core_version_requirement: ^8.8 || ^9 || ^10 +core_version_requirement: ^10.2 type: module package: Entity Reference Integrity diff --git a/modules/entity_reference_integrity_enforce/entity_reference_integrity_enforce.info.yml b/modules/entity_reference_integrity_enforce/entity_reference_integrity_enforce.info.yml index ce81670fa5bbd76f76a916c62e1efb666672e7f6..0695757168a9450405608c6e54bea9a5d45ef9b3 100644 --- a/modules/entity_reference_integrity_enforce/entity_reference_integrity_enforce.info.yml +++ b/modules/entity_reference_integrity_enforce/entity_reference_integrity_enforce.info.yml @@ -1,7 +1,7 @@ name: Entity Reference Integrity Enforce description: Enforce reference integrity on entities. package: Entity Reference Integrity -core_version_requirement: ^8.8 || ^9 || ^10 +core_version_requirement: ^10.2 type: module dependencies: diff --git a/modules/entity_reference_integrity_enforce/src/Form/SettingsForm.php b/modules/entity_reference_integrity_enforce/src/Form/SettingsForm.php index 215a2f34c8cd91b7d4ac103ac5278d2cc501b015..d4fc9e697320f3fc532d181086e7565024b92d78 100644 --- a/modules/entity_reference_integrity_enforce/src/Form/SettingsForm.php +++ b/modules/entity_reference_integrity_enforce/src/Form/SettingsForm.php @@ -3,6 +3,8 @@ namespace Drupal\entity_reference_integrity_enforce\Form; use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\Config\TypedConfigManagerInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Form\ConfigFormBase; use Drupal\Core\Form\FormStateInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -21,10 +23,17 @@ class SettingsForm extends ConfigFormBase { /** * Create a SettingsForm. + * + * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory + * Defines the configuration object factory. + * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config_manager + * The typed config manager. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager service. */ - public function __construct(ConfigFactoryInterface $config_factory, $entity_type_definitions) { - parent::__construct($config_factory); - $this->entityTypeDefinitions = $entity_type_definitions; + public function __construct(ConfigFactoryInterface $config_factory, TypedConfigManagerInterface $typed_config_manager, EntityTypeManagerInterface $entity_type_manager) { + parent::__construct($config_factory, $typed_config_manager); + $this->entityTypeDefinitions = $entity_type_manager->getDefinitions(); } /** @@ -33,7 +42,8 @@ class SettingsForm extends ConfigFormBase { public static function create(ContainerInterface $container) { return new static( $container->get('config.factory'), - $container->get('entity_type.manager')->getDefinitions() + $container->get('config.typed'), + $container->get('entity_type.manager'), ); } diff --git a/modules/entity_reference_integrity_enforce/src/Plugin/Action/DeleteAction.php b/modules/entity_reference_integrity_enforce/src/Plugin/Action/DeleteAction.php index 6b6adccd3feaae71d4112cdaad390e03dbcc3c88..b212355c47541371e14ad25dbf464a9c71d2083d 100644 --- a/modules/entity_reference_integrity_enforce/src/Plugin/Action/DeleteAction.php +++ b/modules/entity_reference_integrity_enforce/src/Plugin/Action/DeleteAction.php @@ -4,6 +4,7 @@ namespace Drupal\entity_reference_integrity_enforce\Plugin\Action; use Drupal\Core\Access\AccessResult; use Drupal\Core\Action\Plugin\Action\DeleteAction as CoreDeleteAction; +use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Session\AccountInterface; @@ -17,10 +18,55 @@ use Symfony\Component\DependencyInjection\ContainerInterface; */ class DeleteAction extends CoreDeleteAction { + /** + * The config factory service. + * + * @var \Drupal\Core\Config\ConfigFactoryInterface + */ + protected $configFactory; + + /** + * Constructs a new DeleteAction object. + * + * @param array $configuration + * A configuration array containing information about the plugin instance. + * @param string $plugin_id + * The plugin ID for the plugin instance. + * @param mixed $plugin_definition + * The plugin implementation definition. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager. + * @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory + * The tempstore factory. + * @param \Drupal\Core\Session\AccountInterface $current_user + * Current user. + * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory + * The config factory service. + */ + public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, PrivateTempStoreFactory $temp_store_factory, AccountInterface $current_user, ConfigFactoryInterface $config_factory) { + parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager, $temp_store_factory, $current_user); + $this->configFactory = $config_factory; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('entity_type.manager'), + $container->get('tempstore.private'), + $container->get('current_user'), + $container->get('config.factory'), + ); + } + /** * {@inheritdoc} */ - public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) { + public function access($object, ?AccountInterface $account = NULL, $return_as_object = FALSE) { // First check if the account has access. $access = parent::access($object, $account, TRUE); @@ -31,7 +77,7 @@ class DeleteAction extends CoreDeleteAction { // Check for dependent entities. $has_dependents = FALSE; - $enabled_entity_type_ids = \Drupal::config('entity_reference_integrity_enforce.settings')->get('enabled_entity_type_ids'); + $enabled_entity_type_ids = $this->configFactory->get('entity_reference_integrity_enforce.settings')->get('enabled_entity_type_ids'); if (in_array($object->getEntityTypeId(), $enabled_entity_type_ids, TRUE)) { $has_dependents = $this->entityTypeManager->getHandler($object->getEntityTypeId(), 'entity_reference_integrity')->hasDependents($object); } diff --git a/modules/entity_reference_integrity_enforce/tests/src/Functional/JsonApiTest.php b/modules/entity_reference_integrity_enforce/tests/src/Functional/JsonApiTest.php index 917ced00c13ba87ae104d95b77aacd690018017d..bdc939e37f186de901c6f467b5136fd234a71207 100644 --- a/modules/entity_reference_integrity_enforce/tests/src/Functional/JsonApiTest.php +++ b/modules/entity_reference_integrity_enforce/tests/src/Functional/JsonApiTest.php @@ -8,7 +8,7 @@ use Drupal\Core\Url; use Drupal\entity_reference_integrity\EntityReferenceIntegrityEntityHandler; use Drupal\node\Entity\NodeType; use Drupal\Tests\BrowserTestBase; -use Drupal\Tests\field\Traits\EntityReferenceTestTrait; +use Drupal\Tests\field\Traits\EntityReferenceFieldCreationTrait; use Drupal\Tests\jsonapi\Functional\JsonApiRequestTestTrait; use GuzzleHttp\RequestOptions; @@ -19,7 +19,7 @@ use GuzzleHttp\RequestOptions; */ class JsonApiTest extends BrowserTestBase { - use EntityReferenceTestTrait; + use EntityReferenceFieldCreationTrait; use JsonApiRequestTestTrait; use StringTranslationTrait; @@ -151,7 +151,7 @@ class JsonApiTest extends BrowserTestBase { // Assert valid response body. Should be a JSONAPI response with errors. $response_data = Json::decode((string) $response->getBody()); $this->assertArrayHasKey('errors', $response_data); - $this->assertEquals(1, sizeof($response_data['errors'])); + $this->assertEquals(1, count($response_data['errors'])); $this->assertArrayHasKey('status', $response_data['errors'][0]); $this->assertEquals('403', $response_data['errors'][0]['status']); @@ -176,4 +176,5 @@ class JsonApiTest extends BrowserTestBase { $response = $this->request('DELETE', $node_url, $request_options); $this->assertEquals(204, $response->getStatusCode()); } + } diff --git a/modules/entity_reference_integrity_enforce/tests/src/Kernel/DeleteActionTest.php b/modules/entity_reference_integrity_enforce/tests/src/Kernel/DeleteActionTest.php index c4baa50bc5a9c2f7fbd7e770534393314fdc7e16..3b23a591589980c357893c9aaea8e0a3c70e35d4 100644 --- a/modules/entity_reference_integrity_enforce/tests/src/Kernel/DeleteActionTest.php +++ b/modules/entity_reference_integrity_enforce/tests/src/Kernel/DeleteActionTest.php @@ -6,7 +6,7 @@ use Drupal\entity_reference_integrity_enforce\Plugin\Action\DeleteAction; use Drupal\KernelTests\KernelTestBase; use Drupal\node\Entity\Node; use Drupal\node\Entity\NodeType; -use Drupal\Tests\field\Traits\EntityReferenceTestTrait; +use Drupal\Tests\field\Traits\EntityReferenceFieldCreationTrait; use Drupal\user\Entity\User; /** @@ -18,7 +18,7 @@ use Drupal\user\Entity\User; */ class DeleteActionTest extends KernelTestBase { - use EntityReferenceTestTrait; + use EntityReferenceFieldCreationTrait; /** * {@inheritdoc} @@ -107,9 +107,9 @@ class DeleteActionTest extends KernelTestBase { // Enable reference integrity for nodes. \Drupal::configFactory() - ->getEditable('entity_reference_integrity_enforce.settings') - ->set('enabled_entity_type_ids', ['node' => 'node']) - ->save(); + ->getEditable('entity_reference_integrity_enforce.settings') + ->set('enabled_entity_type_ids', ['node' => 'node']) + ->save(); // A test user is required for testing access. $this->testUser = User::create([ @@ -141,4 +141,5 @@ class DeleteActionTest extends KernelTestBase { $this->assertFalse($this->dependencyManager->hasDependents($this->referencedNode)); $this->assertTrue($action->access($this->referencedNode, $this->testUser)); } + } diff --git a/modules/entity_reference_integrity_enforce/tests/src/Kernel/EntityPredeleteHookTest.php b/modules/entity_reference_integrity_enforce/tests/src/Kernel/EntityPredeleteHookTest.php index 4c237c3896e6f8202d138907a245c6c0ca651cd4..b9590ebbc7e3d82fa7a236e7d8e43a4122af2f86 100644 --- a/modules/entity_reference_integrity_enforce/tests/src/Kernel/EntityPredeleteHookTest.php +++ b/modules/entity_reference_integrity_enforce/tests/src/Kernel/EntityPredeleteHookTest.php @@ -23,22 +23,18 @@ class EntityPredeleteHookTest extends KernelTestBase { /** * Test the entity_predelete implementations. - * - * @requires function \Drupal\Core\Extension\ModuleHandlerInterface::hasImplementations */ public function testHook() { - $this->assertTrue(\Drupal::moduleHandler()->hasImplementations('entity_predelete', 'entity_reference_integrity_enforce')); - } - /** - * Test the weight of entity_predelete implementations. - * - * @legacy - * @requires function \Drupal\Core\Extension\ModuleHandlerInterface::getImplementations - */ - public function testHookWeightLegacy() { - $implementations = \Drupal::moduleHandler()->getImplementations('entity_predelete'); - $this->assertEquals('entity_reference_integrity_enforce', array_shift($implementations)); + // Get a list of all implementors of hook_entity_predelete(). + $implementors = []; + \Drupal::moduleHandler()->invokeAllWith('entity_predelete', function (callable $hook, string $module) use (&$implementors) { + $implementors[] = $module; + }); + + // Confirm that our hook is the first in the list, and comment is second. + $this->assertEquals('entity_reference_integrity_enforce', reset($implementors)); + $this->assertEquals('comment', next($implementors)); } } diff --git a/src/DependencyFieldMapGenerator.php b/src/DependencyFieldMapGenerator.php index 19ee82ff6d837513af6485109b2f2865e0745f8c..01bc269effdb2fd870b11c893c5553d08d262072 100644 --- a/src/DependencyFieldMapGenerator.php +++ b/src/DependencyFieldMapGenerator.php @@ -90,7 +90,7 @@ class DependencyFieldMapGenerator implements DependencyFieldMapGeneratorInterfac */ public function getReferencingFields($entity_type_id) { $map = $this->getReferentialFieldMap(); - return isset($map[$entity_type_id]) ? $map[$entity_type_id] : []; + return $map[$entity_type_id] ?? []; } } diff --git a/src/EntityReferenceDependencyManagerInterface.php b/src/EntityReferenceDependencyManagerInterface.php index 532d0f20e47c435e5f88715d28bd1272f835114a..988a5ccf7997d7e5f164052f03d9cfe6db3f8cb1 100644 --- a/src/EntityReferenceDependencyManagerInterface.php +++ b/src/EntityReferenceDependencyManagerInterface.php @@ -10,7 +10,7 @@ use Drupal\Core\Entity\EntityInterface; interface EntityReferenceDependencyManagerInterface { /** - * Check if an entity has dependent entties. + * Check if an entity has dependent entities. * * @param \Drupal\Core\Entity\EntityInterface $entity * An entity. @@ -51,6 +51,7 @@ interface EntityReferenceDependencyManagerInterface { * Optional boolean to translate the string. Defaults to TRUE. * * @return string + * The access denied reason string. */ public static function getAccessDeniedReason(EntityInterface $entity, bool $translate = TRUE); diff --git a/src/EntityReferenceIntegrityEntityHandler.php b/src/EntityReferenceIntegrityEntityHandler.php index aeb5a4fc8dc64ad31eead5e6ca6adf619ea2c813..9185e9c4417616105eb8b395ff8fa83b004ac0db 100644 --- a/src/EntityReferenceIntegrityEntityHandler.php +++ b/src/EntityReferenceIntegrityEntityHandler.php @@ -103,7 +103,7 @@ class EntityReferenceIntegrityEntityHandler implements EntityHandlerInterface, E * source field list that match the given target ID. * * @param string $entity_type - * The entityt type. + * The entity type. * @param array $source_fields * An array of source fields. * @param string|int $target_id