Loading entity_reference_manager.permissions.yml +1 −0 Changes for entity_reference_manager.permissions.yml: 1 added line, 0 removed lines. Original line number Diff line number Diff line administer entity reference manager: title: 'Administer Entity Reference manager' description: 'Merge entities (taxonomy, nodes, media) with impact analysis.' restrict access: true entity_reference_manager.routing.yml +3 −0 Changes for entity_reference_manager.routing.yml: 3 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ entity_reference_manager.node_merge: entity_type: 'node' requirements: _entity_access: 'node.view' _permission: 'administer entity reference manager' options: parameters: node: Loading @@ -26,6 +27,7 @@ entity_reference_manager.taxonomy_merge: entity_type: 'taxonomy_term' requirements: _entity_access: 'taxonomy_term.view' _permission: 'administer entity reference manager' options: parameters: taxonomy_term: Loading @@ -39,6 +41,7 @@ entity_reference_manager.media_merge: entity_type: 'media' requirements: _entity_access: 'media.view' _permission: 'administer entity reference manager' options: parameters: media: Loading entity_reference_manager.services.yml +1 −0 Changes for entity_reference_manager.services.yml: 1 added line, 0 removed lines. Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ services: - '@database' - '@logger.channel.default' - '@lock' - '@current_user' - !tagged_iterator entity_reference_manager.handler entity_reference_manager.node_merge_handler: Loading src/Service/ContentMergeManager.php +20 −0 Changes for src/Service/ContentMergeManager.php: 20 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -6,8 +6,10 @@ use Drupal\Core\Database\Connection; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Lock\LockBackendInterface; use Drupal\Core\Session\AccountProxyInterface; use Drupal\entity_reference_manager\Handler\ContentMergeHandlerInterface; use Psr\Log\LoggerInterface; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; /** * Manages content merging operations. Loading @@ -26,6 +28,7 @@ class ContentMergeManager { protected Connection $database, protected LoggerInterface $logger, protected LockBackendInterface $lock, protected AccountProxyInterface $currentUser, iterable $handlers = [], ) { foreach ($handlers as $handler) { Loading Loading @@ -60,6 +63,23 @@ class ContentMergeManager { * Executes the merge between source and target entities. */ public function execute(EntityInterface $source, EntityInterface $target, array $options = []): void { // Enforce access at the point of mutation, regardless of caller // (form, Drush, or other code). Route-level checks alone are not // sufficient because the source/target IDs are attacker-controlled // form input, not necessarily the entity the route was accessed for. if (!$this->currentUser->hasPermission('administer entity reference manager')) { throw new AccessDeniedHttpException('You do not have permission to administer entity reference merges.'); } if (!$source->access('view', $this->currentUser)) { throw new AccessDeniedHttpException('You do not have permission to view the source entity.'); } if (!$target->access('view', $this->currentUser)) { throw new AccessDeniedHttpException('You do not have permission to view the target entity.'); } if (!($options['keep_source'] ?? FALSE) && !$source->access('delete', $this->currentUser)) { throw new AccessDeniedHttpException('You do not have permission to delete the source entity.'); } if (!$this->lock->acquire('entity_reference_manager')) { throw new \RuntimeException('Merge already running.'); } Loading Loading
entity_reference_manager.permissions.yml +1 −0 Changes for entity_reference_manager.permissions.yml: 1 added line, 0 removed lines. Original line number Diff line number Diff line administer entity reference manager: title: 'Administer Entity Reference manager' description: 'Merge entities (taxonomy, nodes, media) with impact analysis.' restrict access: true
entity_reference_manager.routing.yml +3 −0 Changes for entity_reference_manager.routing.yml: 3 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ entity_reference_manager.node_merge: entity_type: 'node' requirements: _entity_access: 'node.view' _permission: 'administer entity reference manager' options: parameters: node: Loading @@ -26,6 +27,7 @@ entity_reference_manager.taxonomy_merge: entity_type: 'taxonomy_term' requirements: _entity_access: 'taxonomy_term.view' _permission: 'administer entity reference manager' options: parameters: taxonomy_term: Loading @@ -39,6 +41,7 @@ entity_reference_manager.media_merge: entity_type: 'media' requirements: _entity_access: 'media.view' _permission: 'administer entity reference manager' options: parameters: media: Loading
entity_reference_manager.services.yml +1 −0 Changes for entity_reference_manager.services.yml: 1 added line, 0 removed lines. Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ services: - '@database' - '@logger.channel.default' - '@lock' - '@current_user' - !tagged_iterator entity_reference_manager.handler entity_reference_manager.node_merge_handler: Loading
src/Service/ContentMergeManager.php +20 −0 Changes for src/Service/ContentMergeManager.php: 20 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -6,8 +6,10 @@ use Drupal\Core\Database\Connection; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Lock\LockBackendInterface; use Drupal\Core\Session\AccountProxyInterface; use Drupal\entity_reference_manager\Handler\ContentMergeHandlerInterface; use Psr\Log\LoggerInterface; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; /** * Manages content merging operations. Loading @@ -26,6 +28,7 @@ class ContentMergeManager { protected Connection $database, protected LoggerInterface $logger, protected LockBackendInterface $lock, protected AccountProxyInterface $currentUser, iterable $handlers = [], ) { foreach ($handlers as $handler) { Loading Loading @@ -60,6 +63,23 @@ class ContentMergeManager { * Executes the merge between source and target entities. */ public function execute(EntityInterface $source, EntityInterface $target, array $options = []): void { // Enforce access at the point of mutation, regardless of caller // (form, Drush, or other code). Route-level checks alone are not // sufficient because the source/target IDs are attacker-controlled // form input, not necessarily the entity the route was accessed for. if (!$this->currentUser->hasPermission('administer entity reference manager')) { throw new AccessDeniedHttpException('You do not have permission to administer entity reference merges.'); } if (!$source->access('view', $this->currentUser)) { throw new AccessDeniedHttpException('You do not have permission to view the source entity.'); } if (!$target->access('view', $this->currentUser)) { throw new AccessDeniedHttpException('You do not have permission to view the target entity.'); } if (!($options['keep_source'] ?? FALSE) && !$source->access('delete', $this->currentUser)) { throw new AccessDeniedHttpException('You do not have permission to delete the source entity.'); } if (!$this->lock->acquire('entity_reference_manager')) { throw new \RuntimeException('Merge already running.'); } Loading