diff --git a/core/modules/workspaces/src/WorkspaceAssociation.php b/core/modules/workspaces/src/WorkspaceAssociation.php index 57b87224e1d33b0a451fc0ce3eaff788ea9e38db..0ca0ca93bfc24a6cb8d1a899dd0659c404ece94c 100644 --- a/core/modules/workspaces/src/WorkspaceAssociation.php +++ b/core/modules/workspaces/src/WorkspaceAssociation.php @@ -214,16 +214,31 @@ public function postPublish(WorkspaceInterface $workspace) { /** * {@inheritdoc} */ - public function deleteAssociations($workspace_id, $entity_type_id = NULL, $entity_ids = NULL) { - $query = $this->database->delete(static::TABLE) - ->condition('workspace', $workspace_id); + public function deleteAssociations($workspace_id = NULL, $entity_type_id = NULL, $entity_ids = NULL, $revision_ids = NULL) { + if (!$workspace_id && !$entity_type_id) { + throw new \InvalidArgumentException('A workspace ID or an entity type ID must be provided.'); + } + + $query = $this->database->delete(static::TABLE); + + if ($workspace_id) { + $query->condition('workspace', $workspace_id); + } if ($entity_type_id) { + if (!$entity_ids && !$revision_ids) { + throw new \InvalidArgumentException('A list of entity IDs or revision IDs must be provided for an entity type.'); + } + $query->condition('target_entity_type_id', $entity_type_id, '='); if ($entity_ids) { $query->condition('target_entity_id', $entity_ids, 'IN'); } + + if ($revision_ids) { + $query->condition('target_entity_revision_id', $revision_ids, 'IN'); + } } $query->execute(); diff --git a/core/modules/workspaces/src/WorkspaceAssociationInterface.php b/core/modules/workspaces/src/WorkspaceAssociationInterface.php index 254499d4649df5422b3e2deba6986f7040e15586..2ed9a8256771e717e5d24128d757ca6a4d9a3a5e 100644 --- a/core/modules/workspaces/src/WorkspaceAssociationInterface.php +++ b/core/modules/workspaces/src/WorkspaceAssociationInterface.php @@ -97,16 +97,22 @@ public function postPublish(WorkspaceInterface $workspace); /** * Deletes all the workspace association records for the given workspace. * - * @param string $workspace_id - * A workspace entity ID. + * @param string|null $workspace_id + * (optional) A workspace entity ID. Defaults to NULL. * @param string|null $entity_type_id * (optional) The target entity type of the associations to delete. Defaults * to NULL. * @param int[]|string[]|null $entity_ids * (optional) The target entity IDs of the associations to delete. Defaults * to NULL. + * @param int[]|string[]|null $revision_ids + * (optional) The target entity revision IDs of the associations to delete. + * Defaults to NULL. + * + * @throws \InvalidArgumentException + * If neither $workspace_id nor $entity_type_id arguments were provided. */ - public function deleteAssociations($workspace_id, $entity_type_id = NULL, $entity_ids = NULL); + public function deleteAssociations($workspace_id = NULL, $entity_type_id = NULL, $entity_ids = NULL, $revision_ids = NULL); /** * Initializes a workspace with all the associations of its parent. diff --git a/core/modules/workspaces/tests/src/Kernel/WorkspaceIntegrationTest.php b/core/modules/workspaces/tests/src/Kernel/WorkspaceIntegrationTest.php index 99e5ddd54cf9a4c6bf43dfc3c9fd52a50dca8fe2..5cfe9a6418163733d9924815cbccb392f4be978e 100644 --- a/core/modules/workspaces/tests/src/Kernel/WorkspaceIntegrationTest.php +++ b/core/modules/workspaces/tests/src/Kernel/WorkspaceIntegrationTest.php @@ -367,6 +367,55 @@ public function testWorkspaces() { $this->assertEmpty($workspace_publisher->getDifferringRevisionIdsOnSource()); } + /** + * Tests the workspace association data integrity for entity CRUD operations. + * + * @covers ::workspaces_entity_presave + * @covers ::workspaces_entity_insert + * @covers ::workspaces_entity_delete + * @covers ::workspaces_entity_revision_delete + */ + public function testWorkspaceAssociationDataIntegrity() { + $this->initializeWorkspacesModule(); + + // Check the initial empty state. + $expected_workspace_association = ['stage' => []]; + $this->assertWorkspaceAssociation($expected_workspace_association, 'node'); + + // Add a new unpublished node in 'stage' and check that new revision is + // tracked in the workspace association data. + $this->switchToWorkspace('stage'); + $unpublished_node = $this->createNode(['title' => 'stage - 3 - r3 - unpublished', 'created' => $this->createdTimestamp++, 'status' => FALSE]); + $expected_workspace_association = ['stage' => [3]]; + $this->assertWorkspaceAssociation($expected_workspace_association, 'node'); + + // Add a new revision for the unpublished node. + $unpublished_node->title = 'stage - 3 - r4 - unpublished'; + $unpublished_node->save(); + $expected_workspace_association = ['stage' => [4]]; + $this->assertWorkspaceAssociation($expected_workspace_association, 'node'); + + // Delete the unpublished node and check that the association data has been + // updated. + $unpublished_node->delete(); + $expected_workspace_association = ['stage' => []]; + $this->assertWorkspaceAssociation($expected_workspace_association, 'node'); + + // Add a new published node in 'stage' and check that new workspace-specific + // revision is tracked in the workspace association data. Note that revision + // '5' has been created as an unpublished default revision in Live, so it is + // not tracked. + $this->createNode(['title' => 'stage - 4 - r6 - published', 'created' => $this->createdTimestamp++, 'status' => TRUE]); + $expected_workspace_association = ['stage' => [6]]; + $this->assertWorkspaceAssociation($expected_workspace_association, 'node'); + + // Delete revision '6' and check that the workspace association does not + // track it anymore. + $this->entityTypeManager->getStorage('node')->deleteRevision(6); + $expected_workspace_association = ['stage' => []]; + $this->assertWorkspaceAssociation($expected_workspace_association, 'node'); + } + /** * Tests entity tracking in workspace descendants. */ diff --git a/core/modules/workspaces/workspaces.module b/core/modules/workspaces/workspaces.module index 0155e25bc3b1395fecfe104cb4aef04fde148d99..05b2b2144ca75ba796e5dfedfebeb28067480ed7 100644 --- a/core/modules/workspaces/workspaces.module +++ b/core/modules/workspaces/workspaces.module @@ -143,6 +143,26 @@ function workspaces_entity_predelete(EntityInterface $entity) { ->entityPredelete($entity); } +/** + * Implements hook_entity_delete(). + */ +function workspaces_entity_delete(EntityInterface $entity) { + if (\Drupal::service('workspaces.manager')->isEntityTypeSupported($entity->getEntityType())) { + \Drupal::service('workspaces.association') + ->deleteAssociations(NULL, $entity->getEntityTypeId(), [$entity->id()]); + } +} + +/** + * Implements hook_entity_revision_delete(). + */ +function workspaces_entity_revision_delete(EntityInterface $entity) { + if (\Drupal::service('workspaces.manager')->isEntityTypeSupported($entity->getEntityType())) { + \Drupal::service('workspaces.association') + ->deleteAssociations(NULL, $entity->getEntityTypeId(), [$entity->id()], [$entity->getRevisionId()]); + } +} + /** * Implements hook_entity_access(). *