From ade7b950a1c2281c577e4203be73f18ec37d6945 Mon Sep 17 00:00:00 2001 From: catch <catch@35733.no-reply.drupal.org> Date: Fri, 20 Sep 2019 08:07:19 +0100 Subject: [PATCH] Revert "Issue #3062434 by amateescu, pmelab, blazey, catch, Sam152, Leksat: Track the workspace of a revision in a base field and convert the workspace_association entity type to a custom index" This reverts commit 17bd9ce08f70e75ec71d885a16331729a8401721. --- core/modules/system/system.module | 22 --- .../src/Entity/WorkspaceAssociation.php | 77 ++++++++ .../workspaces/src/EntityOperations.php | 96 +++++++--- .../modules/workspaces/src/EntityTypeInfo.php | 33 ---- .../EntitySchemaSubscriber.php | 147 --------------- .../src/Form/WorkspaceDeleteForm.php | 47 +---- .../DeletedWorkspaceConstraintValidator.php | 27 ++- ...tyWorkspaceConflictConstraintValidator.php | 20 +- .../workspaces/src/WorkspaceAssociation.php | 163 ----------------- .../src/WorkspaceAssociationInterface.php | 103 ----------- .../src/WorkspaceAssociationStorage.php | 59 ++++++ .../WorkspaceAssociationStorageInterface.php | 48 +++++ .../workspaces/src/WorkspaceManager.php | 80 +++++--- .../src/WorkspaceOperationFactory.php | 16 +- .../workspaces/src/WorkspacePublisher.php | 32 ++-- .../drupal-8.6.0-workspaces_installed.php | Bin 42910 -> 0 bytes .../Update/WorkspacesUpdateTest.php | 105 ----------- .../Functional/WorkspacesUninstallTest.php | 6 - .../tests/src/Kernel/WorkspaceAccessTest.php | 1 + .../tests/src/Kernel/WorkspaceCRUDTest.php | 171 +++++------------- .../src/Kernel/WorkspaceIntegrationTest.php | 15 +- .../Kernel/WorkspaceInternalResourceTest.php | 43 +++++ .../tests/src/Kernel/WorkspaceTestTrait.php | 31 +--- core/modules/workspaces/workspaces.install | 103 ----------- core/modules/workspaces/workspaces.module | 10 - .../workspaces/workspaces.post_update.php | 124 ------------- .../workspaces/workspaces.services.yml | 15 +- 27 files changed, 447 insertions(+), 1147 deletions(-) create mode 100644 core/modules/workspaces/src/Entity/WorkspaceAssociation.php delete mode 100644 core/modules/workspaces/src/EventSubscriber/EntitySchemaSubscriber.php delete mode 100644 core/modules/workspaces/src/WorkspaceAssociation.php delete mode 100644 core/modules/workspaces/src/WorkspaceAssociationInterface.php create mode 100644 core/modules/workspaces/src/WorkspaceAssociationStorage.php create mode 100644 core/modules/workspaces/src/WorkspaceAssociationStorageInterface.php delete mode 100644 core/modules/workspaces/tests/fixtures/update/drupal-8.6.0-workspaces_installed.php delete mode 100644 core/modules/workspaces/tests/src/Functional/Update/WorkspacesUpdateTest.php create mode 100644 core/modules/workspaces/tests/src/Kernel/WorkspaceInternalResourceTest.php diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 4d2a7f6cbf04..c7b2937684da 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -1454,25 +1454,3 @@ function system_element_info_alter(&$type) { $type['page']['#theme_wrappers']['off_canvas_page_wrapper'] = ['#weight' => -1000]; } } - -/** - * Implements hook_modules_uninstalled(). - */ -function system_modules_uninstalled($modules) { - // @todo Remove this when modules are able to maintain their revision metadata - // keys. - // @see https://www.drupal.org/project/drupal/issues/3074333 - if (!in_array('workspaces', $modules, TRUE)) { - return; - } - - $entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager(); - foreach ($entity_definition_update_manager->getEntityTypes() as $entity_type) { - $revision_metadata_keys = $entity_type->get('revision_metadata_keys'); - if ($revision_metadata_keys && array_key_exists('workspace', $revision_metadata_keys)) { - unset($revision_metadata_keys['workspace']); - $entity_type->set('revision_metadata_keys', $revision_metadata_keys); - $entity_definition_update_manager->updateEntityType($entity_type); - } - } -} diff --git a/core/modules/workspaces/src/Entity/WorkspaceAssociation.php b/core/modules/workspaces/src/Entity/WorkspaceAssociation.php new file mode 100644 index 000000000000..6c65c81ed024 --- /dev/null +++ b/core/modules/workspaces/src/Entity/WorkspaceAssociation.php @@ -0,0 +1,77 @@ +<?php + +namespace Drupal\workspaces\Entity; + +use Drupal\Core\Entity\ContentEntityBase; +use Drupal\Core\Entity\EntityTypeInterface; +use Drupal\Core\Field\BaseFieldDefinition; +use Drupal\Core\StringTranslation\TranslatableMarkup; + +/** + * Defines the Workspace association entity. + * + * @ContentEntityType( + * id = "workspace_association", + * label = @Translation("Workspace association"), + * label_collection = @Translation("Workspace associations"), + * label_singular = @Translation("workspace association"), + * label_plural = @Translation("workspace associations"), + * label_count = @PluralTranslation( + * singular = "@count workspace association", + * plural = "@count workspace associations" + * ), + * handlers = { + * "storage" = "Drupal\workspaces\WorkspaceAssociationStorage" + * }, + * base_table = "workspace_association", + * revision_table = "workspace_association_revision", + * internal = TRUE, + * entity_keys = { + * "id" = "id", + * "revision" = "revision_id", + * "uuid" = "uuid", + * } + * ) + * + * @internal + * This entity is marked internal because it should not be used directly to + * alter the workspace an entity belongs to. + */ +class WorkspaceAssociation extends ContentEntityBase { + + /** + * {@inheritdoc} + */ + public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { + $fields = parent::baseFieldDefinitions($entity_type); + + $fields['workspace'] = BaseFieldDefinition::create('entity_reference') + ->setLabel(new TranslatableMarkup('workspace')) + ->setDescription(new TranslatableMarkup('The workspace of the referenced content.')) + ->setSetting('target_type', 'workspace') + ->setRequired(TRUE) + ->setRevisionable(TRUE); + + $fields['target_entity_type_id'] = BaseFieldDefinition::create('string') + ->setLabel(new TranslatableMarkup('Content entity type ID')) + ->setDescription(new TranslatableMarkup('The ID of the content entity type associated with this workspace.')) + ->setSetting('max_length', EntityTypeInterface::ID_MAX_LENGTH) + ->setRequired(TRUE) + ->setRevisionable(TRUE); + + $fields['target_entity_id'] = BaseFieldDefinition::create('integer') + ->setLabel(new TranslatableMarkup('Content entity ID')) + ->setDescription(new TranslatableMarkup('The ID of the content entity associated with this workspace.')) + ->setRequired(TRUE) + ->setRevisionable(TRUE); + + $fields['target_entity_revision_id'] = BaseFieldDefinition::create('integer') + ->setLabel(new TranslatableMarkup('Content entity revision ID')) + ->setDescription(new TranslatableMarkup('The revision ID of the content entity associated with this workspace.')) + ->setRequired(TRUE) + ->setRevisionable(TRUE); + + return $fields; + } + +} diff --git a/core/modules/workspaces/src/EntityOperations.php b/core/modules/workspaces/src/EntityOperations.php index 4409162a282b..6182da200f2c 100644 --- a/core/modules/workspaces/src/EntityOperations.php +++ b/core/modules/workspaces/src/EntityOperations.php @@ -34,13 +34,6 @@ class EntityOperations implements ContainerInjectionInterface { */ protected $workspaceManager; - /** - * The workspace association service. - * - * @var \Drupal\workspaces\WorkspaceAssociationInterface - */ - protected $workspaceAssociation; - /** * Constructs a new EntityOperations instance. * @@ -48,13 +41,10 @@ class EntityOperations implements ContainerInjectionInterface { * The entity type manager service. * @param \Drupal\workspaces\WorkspaceManagerInterface $workspace_manager * The workspace manager service. - * @param \Drupal\workspaces\WorkspaceAssociationInterface $workspace_association - * The workspace association service. */ - public function __construct(EntityTypeManagerInterface $entity_type_manager, WorkspaceManagerInterface $workspace_manager, WorkspaceAssociationInterface $workspace_association) { + public function __construct(EntityTypeManagerInterface $entity_type_manager, WorkspaceManagerInterface $workspace_manager) { $this->entityTypeManager = $entity_type_manager; $this->workspaceManager = $workspace_manager; - $this->workspaceAssociation = $workspace_association; } /** @@ -63,8 +53,7 @@ public function __construct(EntityTypeManagerInterface $entity_type_manager, Wor public static function create(ContainerInterface $container) { return new static( $container->get('entity_type.manager'), - $container->get('workspaces.manager'), - $container->get('workspaces.association') + $container->get('workspaces.manager') ); } @@ -85,13 +74,31 @@ public function entityPreload(array $ids, $entity_type_id) { // Get a list of revision IDs for entities that have a revision set for the // current active workspace. If an entity has multiple revisions set for a // workspace, only the one with the highest ID is returned. - if ($tracked_entities = $this->workspaceAssociation->getTrackedEntities($this->workspaceManager->getActiveWorkspace()->id(), $entity_type_id, $ids)) { + $max_revision_id = 'max_target_entity_revision_id'; + $query = $this->entityTypeManager + ->getStorage('workspace_association') + ->getAggregateQuery() + ->accessCheck(FALSE) + ->allRevisions() + ->aggregate('target_entity_revision_id', 'MAX', NULL, $max_revision_id) + ->groupBy('target_entity_id') + ->condition('target_entity_type_id', $entity_type_id) + ->condition('workspace', $this->workspaceManager->getActiveWorkspace()->id()); + + if ($ids) { + $query->condition('target_entity_id', $ids, 'IN'); + } + + $results = $query->execute(); + + if ($results) { /** @var \Drupal\Core\Entity\RevisionableStorageInterface $storage */ $storage = $this->entityTypeManager->getStorage($entity_type_id); // Swap out every entity which has a revision set for the current active // workspace. - foreach ($storage->loadMultipleRevisions(array_keys($tracked_entities[$entity_type_id])) as $revision) { + $swap_revision_ids = array_column($results, $max_revision_id); + foreach ($storage->loadMultipleRevisions($swap_revision_ids) as $revision) { $entities[$revision->id()] = $revision; } } @@ -135,10 +142,6 @@ public function entityPresave(EntityInterface $entity) { // become the default revision only when it is replicated to the default // workspace. $entity->isDefaultRevision(FALSE); - - // Track the workspaces in which the new revision was saved. - $field_name = $entity_type->getRevisionMetadataKey('workspace'); - $entity->{$field_name}->target_id = $this->workspaceManager->getActiveWorkspace()->id(); } // When a new published entity is inserted in a non-default workspace, we @@ -171,7 +174,7 @@ public function entityInsert(EntityInterface $entity) { return; } - $this->workspaceAssociation->trackEntity($entity, $this->workspaceManager->getActiveWorkspace()); + $this->trackEntity($entity); // When an entity is newly created in a workspace, it should be published in // that workspace, but not yet published on the live workspace. It is first @@ -208,7 +211,7 @@ public function entityUpdate(EntityInterface $entity) { // Only track new revisions. /** @var \Drupal\Core\Entity\RevisionableInterface $entity */ if ($entity->getLoadedRevisionId() != $entity->getRevisionId()) { - $this->workspaceAssociation->trackEntity($entity, $this->workspaceManager->getActiveWorkspace()); + $this->trackEntity($entity); } } @@ -237,6 +240,51 @@ public function entityPredelete(EntityInterface $entity) { } } + /** + * Updates or creates a WorkspaceAssociation entity for a given entity. + * + * If the passed-in entity can belong to a workspace and already has a + * WorkspaceAssociation entity, then a new revision of this will be created with + * the new information. Otherwise, a new WorkspaceAssociation entity is created to + * store the passed-in entity's information. + * + * @param \Drupal\Core\Entity\RevisionableInterface $entity + * The entity to update or create from. + */ + protected function trackEntity(RevisionableInterface $entity) { + // If the entity is not new, check if there's an existing + // WorkspaceAssociation entity for it. + $workspace_association_storage = $this->entityTypeManager->getStorage('workspace_association'); + if (!$entity->isNew()) { + $workspace_associations = $workspace_association_storage->loadByProperties([ + 'target_entity_type_id' => $entity->getEntityTypeId(), + 'target_entity_id' => $entity->id(), + ]); + + /** @var \Drupal\Core\Entity\ContentEntityInterface $workspace_association */ + $workspace_association = reset($workspace_associations); + } + + // If there was a WorkspaceAssociation entry create a new revision, + // otherwise create a new entity with the type and ID. + if (!empty($workspace_association)) { + $workspace_association->setNewRevision(TRUE); + } + else { + $workspace_association = $workspace_association_storage->create([ + 'target_entity_type_id' => $entity->getEntityTypeId(), + 'target_entity_id' => $entity->id(), + ]); + } + + // Add the revision ID and the workspace ID. + $workspace_association->set('target_entity_revision_id', $entity->getRevisionId()); + $workspace_association->set('workspace', $this->workspaceManager->getActiveWorkspace()->id()); + + // Save without updating the tracked content entity. + $workspace_association->save(); + } + /** * Alters entity forms to disallow concurrent editing in multiple workspaces. * @@ -250,7 +298,7 @@ public function entityPredelete(EntityInterface $entity) { * @see hook_form_alter() */ public function entityFormAlter(array &$form, FormStateInterface $form_state, $form_id) { - /** @var \Drupal\Core\Entity\RevisionableInterface $entity */ + /** @var \Drupal\Core\Entity\EntityInterface $entity */ $entity = $form_state->getFormObject()->getEntity(); if (!$this->workspaceManager->isEntityTypeSupported($entity->getEntityType())) { return; @@ -270,7 +318,9 @@ public function entityFormAlter(array &$form, FormStateInterface $form_state, $f $form['#entity_builders'][] = [get_called_class(), 'entityFormEntityBuild']; } - if ($workspace_ids = $this->workspaceAssociation->getEntityTrackingWorkspaceIds($entity)) { + /** @var \Drupal\workspaces\WorkspaceAssociationStorageInterface $workspace_association_storage */ + $workspace_association_storage = $this->entityTypeManager->getStorage('workspace_association'); + if ($workspace_ids = $workspace_association_storage->getEntityTrackingWorkspaceIds($entity)) { // An entity can only be edited in one workspace. $workspace_id = reset($workspace_ids); diff --git a/core/modules/workspaces/src/EntityTypeInfo.php b/core/modules/workspaces/src/EntityTypeInfo.php index 7a72eb246d26..5495c7fa4e7c 100644 --- a/core/modules/workspaces/src/EntityTypeInfo.php +++ b/core/modules/workspaces/src/EntityTypeInfo.php @@ -3,10 +3,7 @@ namespace Drupal\workspaces; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; -use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; -use Drupal\Core\Field\BaseFieldDefinition; -use Drupal\Core\StringTranslation\TranslatableMarkup; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -69,10 +66,6 @@ public function entityTypeBuild(array &$entity_types) { foreach ($entity_types as $entity_type) { if ($this->workspaceManager->isEntityTypeSupported($entity_type)) { $entity_type->addConstraint('EntityWorkspaceConflict'); - - $revision_metadata_keys = $entity_type->get('revision_metadata_keys'); - $revision_metadata_keys['workspace'] = 'workspace'; - $entity_type->set('revision_metadata_keys', $revision_metadata_keys); } } } @@ -91,30 +84,4 @@ public function fieldInfoAlter(&$definitions) { } } - /** - * Provides custom base field definitions for a content entity type. - * - * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type - * The entity type definition. - * - * @return \Drupal\Core\Field\FieldDefinitionInterface[] - * An array of field definitions, keyed by field name. - * - * @see hook_entity_base_field_info() - */ - public function entityBaseFieldInfo(EntityTypeInterface $entity_type) { - if ($this->workspaceManager->isEntityTypeSupported($entity_type)) { - $field_name = $entity_type->getRevisionMetadataKey('workspace'); - $fields[$field_name] = BaseFieldDefinition::create('entity_reference') - ->setLabel(new TranslatableMarkup('Workspace')) - ->setDescription(new TranslatableMarkup('Indicates the workspace that this revision belongs to.')) - ->setSetting('target_type', 'workspace') - ->setInternal(TRUE) - ->setTranslatable(FALSE) - ->setRevisionable(TRUE); - - return $fields; - } - } - } diff --git a/core/modules/workspaces/src/EventSubscriber/EntitySchemaSubscriber.php b/core/modules/workspaces/src/EventSubscriber/EntitySchemaSubscriber.php deleted file mode 100644 index 30fd77aa6445..000000000000 --- a/core/modules/workspaces/src/EventSubscriber/EntitySchemaSubscriber.php +++ /dev/null @@ -1,147 +0,0 @@ -<?php - -namespace Drupal\workspaces\EventSubscriber; - -use Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface; -use Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface; -use Drupal\Core\Entity\EntityTypeEventSubscriberTrait; -use Drupal\Core\Entity\EntityTypeInterface; -use Drupal\Core\Entity\EntityTypeListenerInterface; -use Drupal\Core\Field\BaseFieldDefinition; -use Drupal\Core\StringTranslation\StringTranslationTrait; -use Drupal\workspaces\WorkspaceManagerInterface; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; - -/** - * Defines a class for listening to entity schema changes. - */ -class EntitySchemaSubscriber implements EntityTypeListenerInterface, EventSubscriberInterface { - - use EntityTypeEventSubscriberTrait; - use StringTranslationTrait; - - /** - * The definition update manager. - * - * @var \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface - */ - protected $entityDefinitionUpdateManager; - - /** - * The last installed schema definitions. - * - * @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface - */ - protected $entityLastInstalledSchemaRepository; - - /** - * The workspace manager. - * - * @var \Drupal\workspaces\WorkspaceManagerInterface - */ - protected $workspaceManager; - - /** - * Constructs a new EntitySchemaSubscriber. - * - * @param \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface $entityDefinitionUpdateManager - * Definition update manager. - * @param \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface $entityLastInstalledSchemaRepository - * Last definitions. - * @param \Drupal\workspaces\WorkspaceManagerInterface $workspace_manager - * The workspace manager. - */ - public function __construct(EntityDefinitionUpdateManagerInterface $entityDefinitionUpdateManager, EntityLastInstalledSchemaRepositoryInterface $entityLastInstalledSchemaRepository, WorkspaceManagerInterface $workspace_manager) { - $this->entityDefinitionUpdateManager = $entityDefinitionUpdateManager; - $this->entityLastInstalledSchemaRepository = $entityLastInstalledSchemaRepository; - $this->workspaceManager = $workspace_manager; - } - - /** - * {@inheritdoc} - */ - public static function getSubscribedEvents() { - return static::getEntityTypeEvents(); - } - - /** - * {@inheritdoc} - */ - public function onEntityTypeCreate(EntityTypeInterface $entity_type) { - // Nothing to do here. - } - - /** - * {@inheritdoc} - */ - public function onEntityTypeUpdate(EntityTypeInterface $entity_type, EntityTypeInterface $original) { - // If the entity type is now supported by Workspaces, add the revision - // metadata field. - if ($this->workspaceManager->isEntityTypeSupported($entity_type) && !$this->workspaceManager->isEntityTypeSupported($original)) { - $revision_metadata_keys = $entity_type->get('revision_metadata_keys'); - - if (!isset($revision_metadata_keys['workspace'])) { - // Bail out if there's an existing field called 'workspace'. - if ($this->entityDefinitionUpdateManager->getFieldStorageDefinition('workspace', $entity_type->id())) { - throw new \RuntimeException("An existing 'workspace' field was found for the '{$entity_type->id()}' entity type. Set the 'workspace' revision metadata key to use a different field name and run this update function again."); - } - - $revision_metadata_keys['workspace'] = 'workspace'; - $entity_type->set('revision_metadata_keys', $revision_metadata_keys); - - // We are only adding a revision metadata key so we don't need to go - // through the entity update process. - $this->entityLastInstalledSchemaRepository->setLastInstalledDefinition($entity_type); - } - - $this->entityDefinitionUpdateManager->installFieldStorageDefinition($revision_metadata_keys['workspace'], $entity_type->id(), 'workspaces', $this->getWorkspaceFieldDefinition()); - } - - // If the entity type is no longer supported by Workspaces, remove the - // revision metadata field. - if ($this->workspaceManager->isEntityTypeSupported($original) && !$this->workspaceManager->isEntityTypeSupported($entity_type)) { - $revision_metadata_keys = $original->get('revision_metadata_keys'); - $field_storage_definition = $this->entityLastInstalledSchemaRepository->getLastInstalledFieldStorageDefinitions($entity_type->id())[$revision_metadata_keys['workspace']]; - $this->entityDefinitionUpdateManager->uninstallFieldStorageDefinition($field_storage_definition); - - $revision_metadata_keys = $entity_type->get('revision_metadata_keys'); - unset($revision_metadata_keys['workspace']); - $entity_type->set('revision_metadata_keys', $revision_metadata_keys); - - // We are only removing a revision metadata key so we don't need to go - // through the entity update process. - $this->entityLastInstalledSchemaRepository->setLastInstalledDefinition($entity_type); - } - } - - /** - * {@inheritdoc} - */ - public function onFieldableEntityTypeUpdate(EntityTypeInterface $entity_type, EntityTypeInterface $original, array $field_storage_definitions, array $original_field_storage_definitions, array &$sandbox = NULL) { - $this->onEntityTypeUpdate($entity_type, $original); - } - - /** - * {@inheritdoc} - */ - public function onEntityTypeDelete(EntityTypeInterface $entity_type) { - // Nothing to do here. - } - - /** - * Gets the base field definition for the 'workspace' revision metadata field. - * - * @return \Drupal\Core\Field\BaseFieldDefinition - * The base field definition. - */ - protected function getWorkspaceFieldDefinition() { - return BaseFieldDefinition::create('entity_reference') - ->setLabel($this->t('Workspace')) - ->setDescription($this->t('Indicates the workspace that this revision belongs to.')) - ->setSetting('target_type', 'workspace') - ->setInternal(TRUE) - ->setTranslatable(FALSE) - ->setRevisionable(TRUE); - } - -} diff --git a/core/modules/workspaces/src/Form/WorkspaceDeleteForm.php b/core/modules/workspaces/src/Form/WorkspaceDeleteForm.php index 195cac732fd2..8086873f9ade 100644 --- a/core/modules/workspaces/src/Form/WorkspaceDeleteForm.php +++ b/core/modules/workspaces/src/Form/WorkspaceDeleteForm.php @@ -2,13 +2,8 @@ namespace Drupal\workspaces\Form; -use Drupal\Component\Datetime\TimeInterface; use Drupal\Core\Entity\ContentEntityDeleteForm; -use Drupal\Core\Entity\EntityRepositoryInterface; -use Drupal\Core\Entity\EntityTypeBundleInfoInterface; use Drupal\Core\Form\FormStateInterface; -use Drupal\workspaces\WorkspaceAssociationInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; /** * Provides a form for deleting a workspace. @@ -24,52 +19,14 @@ class WorkspaceDeleteForm extends ContentEntityDeleteForm implements WorkspaceFo */ protected $entity; - /** - * The workspace association service. - * - * @var \Drupal\workspaces\WorkspaceAssociationInterface - */ - protected $workspaceAssociation; - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static( - $container->get('entity.repository'), - $container->get('workspaces.association'), - $container->get('entity_type.bundle.info'), - $container->get('datetime.time') - ); - } - - /** - * Constructs a WorkspaceDeleteForm object. - * - * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository - * The entity repository service. - * @param \Drupal\workspaces\WorkspaceAssociationInterface $workspace_association - * The workspace association service to check how many revisions will be - * deleted. - * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info - * The entity type bundle service. - * @param \Drupal\Component\Datetime\TimeInterface $time - * The time service. - */ - public function __construct(EntityRepositoryInterface $entity_repository, WorkspaceAssociationInterface $workspace_association, EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL, TimeInterface $time = NULL) { - parent::__construct($entity_repository, $entity_type_bundle_info, $time); - $this->workspaceAssociation = $workspace_association; - } - /** * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state) { $form = parent::buildForm($form, $form_state); - $tracked_entities = $this->workspaceAssociation->getTrackedEntities($this->entity->id()); + $source_rev_diff = $this->entityTypeManager->getStorage('workspace_association')->getTrackedEntities($this->entity->id()); $items = []; - foreach (array_keys($tracked_entities) as $entity_type_id => $entity_ids) { - $revision_ids = $this->workspaceAssociation->getAssociatedRevisions($this->entity->id(), $entity_type_id, $entity_ids); + foreach ($source_rev_diff as $entity_type_id => $revision_ids) { $label = $this->entityTypeManager->getDefinition($entity_type_id)->getLabel(); $items[] = $this->formatPlural(count($revision_ids), '1 @label revision.', '@count @label revisions.', ['@label' => $label]); } diff --git a/core/modules/workspaces/src/Plugin/Validation/Constraint/DeletedWorkspaceConstraintValidator.php b/core/modules/workspaces/src/Plugin/Validation/Constraint/DeletedWorkspaceConstraintValidator.php index 1543b5fc2b30..070e89050bb8 100644 --- a/core/modules/workspaces/src/Plugin/Validation/Constraint/DeletedWorkspaceConstraintValidator.php +++ b/core/modules/workspaces/src/Plugin/Validation/Constraint/DeletedWorkspaceConstraintValidator.php @@ -3,7 +3,7 @@ namespace Drupal\workspaces\Plugin\Validation\Constraint; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; -use Drupal\workspaces\WorkspaceAssociationInterface; +use Drupal\workspaces\WorkspaceAssociationStorageInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; @@ -14,20 +14,20 @@ class DeletedWorkspaceConstraintValidator extends ConstraintValidator implements ContainerInjectionInterface { /** - * The workspace association service. + * The workspace association storage. * - * @var \Drupal\workspaces\WorkspaceAssociationInterface + * @var \Drupal\workspaces\WorkspaceAssociationStorageInterface */ - protected $workspaceAssociation; + protected $workspaceAssociationStorage; /** * Creates a new DeletedWorkspaceConstraintValidator instance. * - * @param \Drupal\workspaces\WorkspaceAssociationInterface $workspace_association - * The workspace association service. + * @param \Drupal\workspaces\WorkspaceAssociationStorageInterface $workspace_association_storage + * The workspace association storage. */ - public function __construct(WorkspaceAssociationInterface $workspace_association) { - $this->workspaceAssociation = $workspace_association; + public function __construct(WorkspaceAssociationStorageInterface $workspace_association_storage) { + $this->workspaceAssociationStorage = $workspace_association_storage; } /** @@ -35,7 +35,7 @@ public function __construct(WorkspaceAssociationInterface $workspace_association */ public static function create(ContainerInterface $container) { return new static( - $container->get('workspaces.association') + $container->get('entity_type.manager')->getStorage('workspace_association') ); } @@ -49,7 +49,14 @@ public function validate($value, Constraint $constraint) { return; } - if ($this->workspaceAssociation->getTrackedEntities($value->getEntity()->id())) { + $count = $this->workspaceAssociationStorage + ->getQuery() + ->allRevisions() + ->accessCheck(FALSE) + ->condition('workspace', $value->getEntity()->id()) + ->count() + ->execute(); + if ($count) { $this->context->addViolation($constraint->message); } } diff --git a/core/modules/workspaces/src/Plugin/Validation/Constraint/EntityWorkspaceConflictConstraintValidator.php b/core/modules/workspaces/src/Plugin/Validation/Constraint/EntityWorkspaceConflictConstraintValidator.php index 66bb887e7b12..17adc7c0d2d8 100644 --- a/core/modules/workspaces/src/Plugin/Validation/Constraint/EntityWorkspaceConflictConstraintValidator.php +++ b/core/modules/workspaces/src/Plugin/Validation/Constraint/EntityWorkspaceConflictConstraintValidator.php @@ -4,7 +4,6 @@ use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; -use Drupal\workspaces\WorkspaceAssociationInterface; use Drupal\workspaces\WorkspaceManagerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Validator\Constraint; @@ -29,13 +28,6 @@ class EntityWorkspaceConflictConstraintValidator extends ConstraintValidator imp */ protected $workspaceManager; - /** - * The workspace association service. - * - * @var \Drupal\workspaces\WorkspaceAssociationInterface - */ - protected $workspaceAssociation; - /** * Constructs an EntityUntranslatableFieldsConstraintValidator object. * @@ -43,13 +35,10 @@ class EntityWorkspaceConflictConstraintValidator extends ConstraintValidator imp * The entity type manager service. * @param \Drupal\workspaces\WorkspaceManagerInterface $workspace_manager * The workspace manager service. - * @param \Drupal\workspaces\WorkspaceAssociationInterface $workspace_association - * The workspace association service. */ - public function __construct(EntityTypeManagerInterface $entity_type_manager, WorkspaceManagerInterface $workspace_manager, WorkspaceAssociationInterface $workspace_association) { + public function __construct(EntityTypeManagerInterface $entity_type_manager, WorkspaceManagerInterface $workspace_manager) { $this->entityTypeManager = $entity_type_manager; $this->workspaceManager = $workspace_manager; - $this->workspaceAssociation = $workspace_association; } /** @@ -58,8 +47,7 @@ public function __construct(EntityTypeManagerInterface $entity_type_manager, Wor public static function create(ContainerInterface $container) { return new static( $container->get('entity_type.manager'), - $container->get('workspaces.manager'), - $container->get('workspaces.association') + $container->get('workspaces.manager') ); } @@ -69,7 +57,9 @@ public static function create(ContainerInterface $container) { public function validate($entity, Constraint $constraint) { /** @var \Drupal\Core\Entity\EntityInterface $entity */ if (isset($entity) && !$entity->isNew()) { - $workspace_ids = $this->workspaceAssociation->getEntityTrackingWorkspaceIds($entity); + /** @var \Drupal\workspaces\WorkspaceAssociationStorageInterface $workspace_association_storage */ + $workspace_association_storage = $this->entityTypeManager->getStorage('workspace_association'); + $workspace_ids = $workspace_association_storage->getEntityTrackingWorkspaceIds($entity); $active_workspace = $this->workspaceManager->getActiveWorkspace(); if ($workspace_ids && (!$active_workspace || !in_array($active_workspace->id(), $workspace_ids, TRUE))) { diff --git a/core/modules/workspaces/src/WorkspaceAssociation.php b/core/modules/workspaces/src/WorkspaceAssociation.php deleted file mode 100644 index a67f95469372..000000000000 --- a/core/modules/workspaces/src/WorkspaceAssociation.php +++ /dev/null @@ -1,163 +0,0 @@ -<?php - -namespace Drupal\workspaces; - -use Drupal\Core\Database\Connection; -use Drupal\Core\Entity\EntityTypeManagerInterface; -use Drupal\Core\Entity\RevisionableInterface; -use Drupal\Core\Entity\Sql\SqlContentEntityStorage; - -/** - * Provides a class for CRUD operations on workspace associations. - */ -class WorkspaceAssociation implements WorkspaceAssociationInterface { - - /** - * The table for the workspace association storage. - */ - const TABLE = 'workspace_association'; - - /** - * The database connection. - * - * @var \Drupal\Core\Database\Connection - */ - protected $database; - - /** - * The entity type manager. - * - * @var \Drupal\Core\Entity\EntityTypeManagerInterface - */ - protected $entityTypeManager; - - /** - * Constructs a WorkspaceAssociation object. - * - * @param \Drupal\Core\Database\Connection $connection - * A database connection for reading and writing path aliases. - * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager - * The entity type manager for querying revisions. - */ - public function __construct(Connection $connection, EntityTypeManagerInterface $entity_type_manager) { - $this->database = $connection; - $this->entityTypeManager = $entity_type_manager; - } - - /** - * {@inheritdoc} - */ - public function trackEntity(RevisionableInterface $entity, WorkspaceInterface $workspace) { - $this->database->merge(static::TABLE) - ->fields([ - 'target_entity_revision_id' => $entity->getRevisionId(), - ]) - ->keys([ - 'workspace' => $workspace->id(), - 'target_entity_type_id' => $entity->getEntityTypeId(), - 'target_entity_id' => $entity->id(), - ]) - ->execute(); - } - - /** - * {@inheritdoc} - */ - public function getTrackedEntities($workspace_id, $entity_type_id = NULL, $entity_ids = NULL) { - $query = $this->database->select(static::TABLE); - $query - ->fields(static::TABLE, ['target_entity_type_id', 'target_entity_id', 'target_entity_revision_id']) - ->orderBy('target_entity_revision_id', 'ASC') - ->condition('workspace', $workspace_id); - - if ($entity_type_id) { - $query->condition('target_entity_type_id', $entity_type_id, '='); - - if ($entity_ids) { - $query->condition('target_entity_id', $entity_ids, 'IN'); - } - } - - $tracked_revisions = []; - foreach ($query->execute() as $record) { - $tracked_revisions[$record->target_entity_type_id][$record->target_entity_revision_id] = $record->target_entity_id; - } - - return $tracked_revisions; - } - - /** - * {@inheritdoc} - */ - public function getAssociatedRevisions($workspace_id, $entity_type_id, $entity_ids = NULL) { - /** @var \Drupal\Core\Entity\EntityStorageInterface $storage */ - $storage = $this->entityTypeManager->getStorage($entity_type_id); - - // If the entity type is not using core's default entity storage, we can't - // assume the table mapping layout so we have to return only the latest - // tracked revisions. - if (!$storage instanceof SqlContentEntityStorage) { - return $this->getTrackedEntities($workspace_id, $entity_type_id, $entity_ids)[$entity_type_id]; - } - - $entity_type = $storage->getEntityType(); - $table_mapping = $storage->getTableMapping(); - $workspace_field = $table_mapping->getColumnNames($entity_type->get('revision_metadata_keys')['workspace'])['target_id']; - $id_field = $table_mapping->getColumnNames($entity_type->getKey('id'))['value']; - $revision_id_field = $table_mapping->getColumnNames($entity_type->getKey('revision'))['value']; - - $query = $this->database->select($entity_type->getRevisionTable(), 'revision'); - $query->leftJoin($entity_type->getBaseTable(), 'base', "revision.$id_field = base.$id_field"); - - $query - ->fields('revision', [$revision_id_field, $id_field]) - ->condition("revision.$workspace_field", $workspace_id) - ->where("revision.$revision_id_field > base.$revision_id_field") - ->orderBy("revision.$revision_id_field", 'ASC'); - - // Restrict the result to a set of entity ID's if provided. - if ($entity_ids) { - $query->condition("revision.$id_field", $entity_ids, 'IN'); - } - - return $query->execute()->fetchAllKeyed(); - } - - /** - * {@inheritdoc} - */ - public function getEntityTrackingWorkspaceIds(RevisionableInterface $entity) { - $query = $this->database->select(static::TABLE) - ->fields(static::TABLE, ['workspace']) - ->condition('target_entity_type_id', $entity->getEntityTypeId()) - ->condition('target_entity_id', $entity->id()); - - return $query->execute()->fetchCol(); - } - - /** - * {@inheritdoc} - */ - public function postPublish(WorkspaceInterface $workspace) { - $this->deleteAssociations($workspace->id()); - } - - /** - * {@inheritdoc} - */ - public function deleteAssociations($workspace_id, $entity_type_id = NULL, $entity_ids = NULL) { - $query = $this->database->delete(static::TABLE) - ->condition('workspace', $workspace_id); - - if ($entity_type_id) { - $query->condition('target_entity_type_id', $entity_type_id, '='); - - if ($entity_ids) { - $query->condition('target_entity_id', $entity_ids, 'IN'); - } - } - - $query->execute(); - } - -} diff --git a/core/modules/workspaces/src/WorkspaceAssociationInterface.php b/core/modules/workspaces/src/WorkspaceAssociationInterface.php deleted file mode 100644 index 1c93ca4aaa19..000000000000 --- a/core/modules/workspaces/src/WorkspaceAssociationInterface.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php - -namespace Drupal\workspaces; - -use Drupal\Core\Entity\RevisionableInterface; - -/** - * Defines an interface for the workspace_association service. - * - * The canonical workspace association data is stored in a revision metadata - * field on each entity revision that is tracked by a workspace. - * - * For the purpose of optimizing workspace-specific queries, the default - * implementation of this interface defines a custom 'workspace_association' - * index table which stores only the latest revisions tracked by a workspace. - * - * @internal - */ -interface WorkspaceAssociationInterface { - - /** - * Updates or creates the association for a given entity and a workspace. - * - * @param \Drupal\Core\Entity\RevisionableInterface $entity - * The entity to update or create from. - * @param \Drupal\workspaces\WorkspaceInterface $workspace - * The workspace in which the entity will be tracked. - */ - public function trackEntity(RevisionableInterface $entity, WorkspaceInterface $workspace); - - /** - * Retrieves the entities tracked by a given workspace. - * - * @param string $workspace_id - * The ID of the workspace. - * @param string|null $entity_type_id - * (optional) An entity type ID to filter the results by. Defaults to NULL. - * @param int[]|string[]|null $entity_ids - * (optional) An array of entity IDs to filter the results by. Defaults to - * NULL. - * - * @return array - * Returns a multidimensional array where the first level keys are entity - * type IDs and the values are an array of entity IDs keyed by revision IDs. - */ - public function getTrackedEntities($workspace_id, $entity_type_id = NULL, $entity_ids = NULL); - - /** - * Retrieves all content revisions tracked by a given workspace. - * - * Since the 'workspace_association' index table only tracks the latest - * associated revisions, this method retrieves all the tracked revisions by - * querying the entity type's revision table directly. - * - * @param string $workspace_id - * The ID of the workspace. - * @param string $entity_type_id - * An entity type ID to find revisions for. - * @param int[]|string[]|null $entity_ids - * (optional) An array of entity IDs to filter the results by. Defaults to - * NULL. - * - * @return array - * Returns an array where the values are an array of entity IDs keyed by - * revision IDs. - */ - public function getAssociatedRevisions($workspace_id, $entity_type_id, $entity_ids = NULL); - - /** - * Gets a list of workspace IDs in which an entity is tracked. - * - * @param \Drupal\Core\Entity\RevisionableInterface $entity - * An entity object. - * - * @return string[] - * An array of workspace IDs where the given entity is tracked, or an empty - * array if it is not tracked anywhere. - */ - public function getEntityTrackingWorkspaceIds(RevisionableInterface $entity); - - /** - * Triggers clean-up operations after publishing a workspace. - * - * @param \Drupal\workspaces\WorkspaceInterface $workspace - * A workspace entity. - */ - 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 $entity_type_id - * (optional) The target entity type of the associations to delete. Defaults - * to NULL. - * @param string|null $entity_ids - * (optional) The target entity IDs of the associations to delete. Defaults - * to NULL. - */ - public function deleteAssociations($workspace_id, $entity_type_id = NULL, $entity_ids = NULL); - -} diff --git a/core/modules/workspaces/src/WorkspaceAssociationStorage.php b/core/modules/workspaces/src/WorkspaceAssociationStorage.php new file mode 100644 index 000000000000..6355e79220e4 --- /dev/null +++ b/core/modules/workspaces/src/WorkspaceAssociationStorage.php @@ -0,0 +1,59 @@ +<?php + +namespace Drupal\workspaces; + +use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Entity\Sql\SqlContentEntityStorage; + +/** + * Defines the storage handler class for the Workspace association entity type. + */ +class WorkspaceAssociationStorage extends SqlContentEntityStorage implements WorkspaceAssociationStorageInterface { + + /** + * {@inheritdoc} + */ + public function postPush(WorkspaceInterface $workspace) { + $this->database + ->delete($this->entityType->getBaseTable()) + ->condition('workspace', $workspace->id()) + ->execute(); + $this->database + ->delete($this->entityType->getRevisionTable()) + ->condition('workspace', $workspace->id()) + ->execute(); + } + + /** + * {@inheritdoc} + */ + public function getTrackedEntities($workspace_id, $all_revisions = FALSE) { + $table = $all_revisions ? $this->getRevisionTable() : $this->getBaseTable(); + $query = $this->database->select($table, 'base_table'); + $query + ->fields('base_table', ['target_entity_type_id', 'target_entity_id', 'target_entity_revision_id']) + ->orderBy('target_entity_revision_id', 'ASC') + ->condition('workspace', $workspace_id); + + $tracked_revisions = []; + foreach ($query->execute() as $record) { + $tracked_revisions[$record->target_entity_type_id][$record->target_entity_revision_id] = $record->target_entity_id; + } + + return $tracked_revisions; + } + + /** + * {@inheritdoc} + */ + public function getEntityTrackingWorkspaceIds(EntityInterface $entity) { + $query = $this->database->select($this->getBaseTable(), 'base_table'); + $query + ->fields('base_table', ['workspace']) + ->condition('target_entity_type_id', $entity->getEntityTypeId()) + ->condition('target_entity_id', $entity->id()); + + return $query->execute()->fetchCol(); + } + +} diff --git a/core/modules/workspaces/src/WorkspaceAssociationStorageInterface.php b/core/modules/workspaces/src/WorkspaceAssociationStorageInterface.php new file mode 100644 index 000000000000..24663206e30c --- /dev/null +++ b/core/modules/workspaces/src/WorkspaceAssociationStorageInterface.php @@ -0,0 +1,48 @@ +<?php + +namespace Drupal\workspaces; + +use Drupal\Core\Entity\ContentEntityStorageInterface; +use Drupal\Core\Entity\EntityInterface; + +/** + * Defines an interface for workspace association entity storage classes. + */ +interface WorkspaceAssociationStorageInterface extends ContentEntityStorageInterface { + + /** + * Triggers clean-up operations after pushing. + * + * @param \Drupal\workspaces\WorkspaceInterface $workspace + * A workspace entity. + */ + public function postPush(WorkspaceInterface $workspace); + + /** + * Retrieves the content revisions tracked by a given workspace. + * + * @param string $workspace_id + * The ID of the workspace. + * @param bool $all_revisions + * (optional) Whether to return all the tracked revisions for each entity or + * just the latest tracked revision. Defaults to FALSE. + * + * @return array + * Returns a multidimensional array where the first level keys are entity + * type IDs and the values are an array of entity IDs keyed by revision IDs. + */ + public function getTrackedEntities($workspace_id, $all_revisions = FALSE); + + /** + * Gets a list of workspace IDs in which an entity is tracked. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * An entity object. + * + * @return string[] + * An array of workspace IDs where the given entity is tracked, or an empty + * array if it is not tracked anywhere. + */ + public function getEntityTrackingWorkspaceIds(EntityInterface $entity); + +} diff --git a/core/modules/workspaces/src/WorkspaceManager.php b/core/modules/workspaces/src/WorkspaceManager.php index b53be3613025..5528988a80ba 100644 --- a/core/modules/workspaces/src/WorkspaceManager.php +++ b/core/modules/workspaces/src/WorkspaceManager.php @@ -83,13 +83,6 @@ class WorkspaceManager implements WorkspaceManagerInterface { */ protected $classResolver; - /** - * The workspace association service. - * - * @var \Drupal\workspaces\WorkspaceAssociationInterface - */ - protected $workspaceAssociation; - /** * The workspace negotiator service IDs. * @@ -121,12 +114,10 @@ class WorkspaceManager implements WorkspaceManagerInterface { * A logger instance. * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver * The class resolver. - * @param \Drupal\workspaces\WorkspaceAssociationInterface $workspace_association - * The workspace association service. * @param array $negotiator_ids * The workspace negotiator service IDs. */ - public function __construct(RequestStack $request_stack, EntityTypeManagerInterface $entity_type_manager, MemoryCacheInterface $entity_memory_cache, AccountProxyInterface $current_user, StateInterface $state, LoggerInterface $logger, ClassResolverInterface $class_resolver, WorkspaceAssociationInterface $workspace_association, array $negotiator_ids) { + public function __construct(RequestStack $request_stack, EntityTypeManagerInterface $entity_type_manager, MemoryCacheInterface $entity_memory_cache, AccountProxyInterface $current_user, StateInterface $state, LoggerInterface $logger, ClassResolverInterface $class_resolver, array $negotiator_ids) { $this->requestStack = $request_stack; $this->entityTypeManager = $entity_type_manager; $this->entityMemoryCache = $entity_memory_cache; @@ -134,7 +125,6 @@ public function __construct(RequestStack $request_stack, EntityTypeManagerInterf $this->state = $state; $this->logger = $logger; $this->classResolver = $class_resolver; - $this->workspaceAssociation = $workspace_association; $this->negotiatorIds = $negotiator_ids; } @@ -315,35 +305,67 @@ public function purgeDeletedWorkspacesBatch() { $batch_size = Settings::get('entity_update_batch_size', 50); + /** @var \Drupal\workspaces\WorkspaceAssociationStorageInterface $workspace_association_storage */ + $workspace_association_storage = $this->entityTypeManager->getStorage('workspace_association'); + // Get the first deleted workspace from the list and delete the revisions - // associated with it, along with the workspace association records. + // associated with it, along with the workspace_association entries. $workspace_id = reset($deleted_workspace_ids); - $tracked_entities = $this->workspaceAssociation->getTrackedEntities($workspace_id); - - $count = 1; - foreach ($tracked_entities as $entity_type_id => $entities) { - $associated_entity_storage = $this->entityTypeManager->getStorage($entity_type_id); - $associated_revisions = $this->workspaceAssociation->getAssociatedRevisions($workspace_id, $entity_type_id); - foreach (array_keys($associated_revisions) as $revision_id) { - if ($count > $batch_size) { - continue 2; - } + $workspace_association_ids = $this->getWorkspaceAssociationRevisionsToPurge($workspace_id, $batch_size); + if ($workspace_association_ids) { + $workspace_associations = $workspace_association_storage->loadMultipleRevisions(array_keys($workspace_association_ids)); + foreach ($workspace_associations as $workspace_association) { + $associated_entity_storage = $this->entityTypeManager->getStorage($workspace_association->target_entity_type_id->value); // Delete the associated entity revision. - $associated_entity_storage->deleteRevision($revision_id); - $count++; + if ($entity = $associated_entity_storage->loadRevision($workspace_association->target_entity_revision_id->value)) { + if ($entity->isDefaultRevision()) { + $entity->delete(); + } + else { + $associated_entity_storage->deleteRevision($workspace_association->target_entity_revision_id->value); + } + } + + // Delete the workspace_association revision. + if ($workspace_association->isDefaultRevision()) { + $workspace_association->delete(); + } + else { + $workspace_association_storage->deleteRevision($workspace_association->getRevisionId()); + } } - // Delete the workspace association entries. - $this->workspaceAssociation->deleteAssociations($workspace_id, $entity_type_id, $entities); } // The purging operation above might have taken a long time, so we need to - // request a fresh list of tracked entities. If it is empty, we can go ahead - // and remove the deleted workspace ID entry from state. - if (!$this->workspaceAssociation->getTrackedEntities($workspace_id)) { + // request a fresh list of workspace association IDs. If it is empty, we can + // go ahead and remove the deleted workspace ID entry from state. + if (!$this->getWorkspaceAssociationRevisionsToPurge($workspace_id, $batch_size)) { unset($deleted_workspace_ids[$workspace_id]); $this->state->set('workspace.deleted', $deleted_workspace_ids); } } + /** + * Gets a list of workspace association IDs to purge. + * + * @param string $workspace_id + * The ID of the workspace. + * @param int $batch_size + * The maximum number of records that will be purged. + * + * @return array + * An array of workspace association IDs, keyed by their revision IDs. + */ + protected function getWorkspaceAssociationRevisionsToPurge($workspace_id, $batch_size) { + return $this->entityTypeManager->getStorage('workspace_association') + ->getQuery() + ->allRevisions() + ->accessCheck(FALSE) + ->condition('workspace', $workspace_id) + ->sort('revision_id', 'ASC') + ->range(0, $batch_size) + ->execute(); + } + } diff --git a/core/modules/workspaces/src/WorkspaceOperationFactory.php b/core/modules/workspaces/src/WorkspaceOperationFactory.php index 7b761a4a3994..d523365667ac 100644 --- a/core/modules/workspaces/src/WorkspaceOperationFactory.php +++ b/core/modules/workspaces/src/WorkspaceOperationFactory.php @@ -36,13 +36,6 @@ class WorkspaceOperationFactory { */ protected $workspaceManager; - /** - * The workspace association service. - * - * @var \Drupal\workspaces\WorkspaceAssociationInterface - */ - protected $workspaceAssociation; - /** * Constructs a new WorkspacePublisher. * @@ -50,16 +43,11 @@ class WorkspaceOperationFactory { * The entity type manager. * @param \Drupal\Core\Database\Connection $database * Database connection. - * @param \Drupal\workspaces\WorkspaceManagerInterface $workspace_manager - * The workspace manager service. - * @param \Drupal\workspaces\WorkspaceAssociationInterface $workspace_association - * The workspace association service. */ - public function __construct(EntityTypeManagerInterface $entity_type_manager, Connection $database, WorkspaceManagerInterface $workspace_manager, WorkspaceAssociationInterface $workspace_association) { + public function __construct(EntityTypeManagerInterface $entity_type_manager, Connection $database, WorkspaceManagerInterface $workspace_manager) { $this->entityTypeManager = $entity_type_manager; $this->database = $database; $this->workspaceManager = $workspace_manager; - $this->workspaceAssociation = $workspace_association; } /** @@ -72,7 +60,7 @@ public function __construct(EntityTypeManagerInterface $entity_type_manager, Con * A workspace publisher object. */ public function getPublisher(WorkspaceInterface $source) { - return new WorkspacePublisher($this->entityTypeManager, $this->database, $this->workspaceManager, $this->workspaceAssociation, $source); + return new WorkspacePublisher($this->entityTypeManager, $this->database, $this->workspaceManager, $source); } } diff --git a/core/modules/workspaces/src/WorkspacePublisher.php b/core/modules/workspaces/src/WorkspacePublisher.php index d38e97383c39..c7da6b8be782 100644 --- a/core/modules/workspaces/src/WorkspacePublisher.php +++ b/core/modules/workspaces/src/WorkspacePublisher.php @@ -37,18 +37,18 @@ class WorkspacePublisher implements WorkspacePublisherInterface { protected $database; /** - * The workspace manager. + * The workspace association storage. * - * @var \Drupal\workspaces\WorkspaceManagerInterface + * @var \Drupal\workspaces\WorkspaceAssociationStorageInterface */ - protected $workspaceManager; + protected $workspaceAssociationStorage; /** - * The workspace association service. + * The workspace manager. * - * @var \Drupal\workspaces\WorkspaceAssociationInterface + * @var \Drupal\workspaces\WorkspaceManagerInterface */ - protected $workspaceAssociation; + protected $workspaceManager; /** * Constructs a new WorkspacePublisher. @@ -59,14 +59,12 @@ class WorkspacePublisher implements WorkspacePublisherInterface { * Database connection. * @param \Drupal\workspaces\WorkspaceManagerInterface $workspace_manager * The workspace manager. - * @param \Drupal\workspaces\WorkspaceAssociationInterface $workspace_association - * The workspace association service. */ - public function __construct(EntityTypeManagerInterface $entity_type_manager, Connection $database, WorkspaceManagerInterface $workspace_manager, WorkspaceAssociationInterface $workspace_association, WorkspaceInterface $source) { + public function __construct(EntityTypeManagerInterface $entity_type_manager, Connection $database, WorkspaceManagerInterface $workspace_manager, WorkspaceInterface $source) { $this->entityTypeManager = $entity_type_manager; $this->database = $database; + $this->workspaceAssociationStorage = $entity_type_manager->getStorage('workspace_association'); $this->workspaceManager = $workspace_manager; - $this->workspaceAssociation = $workspace_association; $this->sourceWorkspace = $source; } @@ -97,11 +95,6 @@ public function publish() { // revisions. $entity->setSyncing(TRUE); $entity->isDefaultRevision(TRUE); - - // The default revision is not workspace-specific anymore. - $field_name = $entity->getEntityType()->getRevisionMetadataKey('workspace'); - $entity->{$field_name}->target_id = NULL; - $entity->original = $default_revisions[$entity->id()]; $entity->save(); } @@ -114,8 +107,9 @@ public function publish() { throw $e; } - // Notify the workspace association that a workspace has been published. - $this->workspaceAssociation->postPublish($this->sourceWorkspace); + // Notify the workspace association storage that a workspace has been + // pushed. + $this->workspaceAssociationStorage->postPush($this->sourceWorkspace); } /** @@ -147,7 +141,7 @@ public function checkConflictsOnTarget() { public function getDifferringRevisionIdsOnTarget() { $target_revision_difference = []; - $tracked_entities = $this->workspaceAssociation->getTrackedEntities($this->sourceWorkspace->id()); + $tracked_entities = $this->workspaceAssociationStorage->getTrackedEntities($this->sourceWorkspace->id()); foreach ($tracked_entities as $entity_type_id => $tracked_revisions) { $entity_type = $this->entityTypeManager->getDefinition($entity_type_id); @@ -177,7 +171,7 @@ public function getDifferringRevisionIdsOnTarget() { */ public function getDifferringRevisionIdsOnSource() { // Get the Workspace association revisions which haven't been pushed yet. - return $this->workspaceAssociation->getTrackedEntities($this->sourceWorkspace->id()); + return $this->workspaceAssociationStorage->getTrackedEntities($this->sourceWorkspace->id()); } /** diff --git a/core/modules/workspaces/tests/fixtures/update/drupal-8.6.0-workspaces_installed.php b/core/modules/workspaces/tests/fixtures/update/drupal-8.6.0-workspaces_installed.php deleted file mode 100644 index 7e6276abfead76a94a69250aa7db85f9d06582ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 42910 zcmeHQ`E%PwmY$#WSD;j>qLeiyUb?7_cRY4d<C@HR%g4+QafSsFO$ucaQ~*%2<I4ZO z-+O(aaqt2G+Hy2iyODuD-ud-=ue-tj{-4{M+k?|n=dYu==LeHFu^V`9=tVCl!8{Z{ z`BQOl`u+C@&iBq=$Mg^U@q8Y{t{+5>=f>{PjfCTRo*(;jJc{Rz7cOqyY4h>~?We*! ziPVrMnCWElyEC4LPAsArv(aerjc^vXlhE~qbL+-8kux9j!++1i57DhV5|J~TdyA<! zLG#mtg9Rph!E?NS4iewLP*j*dt`82r8_k12jEE-Z2S>N)_a`C-_3F*hF<OH4HzMW< zqtT6+xz3#kBlJ752WdW?iEtv08Xv@`!JRu@h{o}O<20X+eKGZ-qhIh3$7#qn&JRzW zhTp$zwOZGWC-P0;&P0P>+#9y^yU`qSVp?U3=u;Gn*@+~F8~rLL15;AYM-8jb6Q_~x zk!#G8qnS0eY$HC3(IOUF5btiGQQ?dr)f4eC7J-t3`8$FzEzvfgMuI3Oa)PlxNy|h( zeB%7lAWLbi<3e?}xYUHFDN^kW@lp{Y9^L#z+8}PeOOd<?A`$xT)c;)^S#Q$qerZT6 zYyA3)EvR3?GHWtz4ht4lG2_Y@m}gRX@#M-dEJd<;Xro9BCc`}St4os7A6}BUVkhyx zhGWd$5kVaKBFcCN&;VW1?qQt64MX?S5y(ziE&W^Sm;RET^W@<8_@Mce{}CM(_G2M| z2tWRG;)$^zNZ>gE1+hzb!vd5er~U0j_8P>WZiQ@Qx$CQb_q=~-!^)352OuVZBgFDw zZ_)Lz-|O^0NBz!O|L`y0|0NW6KDo(YCSsRVK7g#E!)v$SrdDl?YMOvvcQK6*ua`al zBkFhCYS`Zveki=xYWRPu(f_g2;B84WBo+Ik!N>*R9bOOnZ6Zjs6L=!byt-nNZV4BG zOhKG^-Q(AI4xJ?2w^&I1L*FBQJNl2yL>?>$uxsQan_{@dBKIfAo4P|WrB3$WU9^^s zlfUqOejkXC+9pq-iQyvfreHbhxB7?lA5b%Og9+9?`za|+>1trVUiQ2L0U0LZ&<&y~ z1Pq&S$78^wkP=wm2<n$wkPM-NL2M$HriaDNLx182?)3T8jbLbC5fW3DYuZQerNzEC zUd$s%<GR5bKZQ2QO3&F!p`IypI~XqfDfG{yV*2;_Va<3yqy68K=Ag|TVXbD0dKX0$ znXb>cA$e9fpZ;AE!{mXHaE=9Oa2w9=d?rNs5#HRyGpT*(P7}rffa3HudLpKH|IC~D z!QauC9*F-lBSE?t1<}XM@C`C8pX}nFdSFYCKcPn=M*0_v2mE@h*sZ#O0)aK0N7!aM zw#cc7*-U#d>a~Qvz|tHGO)d=GMTl{S^_}MK!Oqr>@Jw~kV=xW3)A=VJVKW-2x$F<| zgW)Kihwenufbj|d_swt9_xQgQ?!HNSwPC7vzPL}BYVk<YF8+na#!f**8+HvZa>+^t zz_ghk5q=X-9k>TSCzuPJ#0L1Kbj_s405NN@n7Scgj~cYmTY(@gG4u#h$5c~4_>lM- zX~OfGV{Jze$|+BJiogd!)G19np3d+24;cWX0h<~~C+jht&o=5ja|4L{^t0jTk5g@f zM6<_+l8lK-))Q1PrW}VQ0o%~lX2w0oRHeq%BWW3Xf{e<Hdsd!aZOd&SN)m_BJY8_G zme|8<GAF2jbWCd#*2U>`=#D-dUjMH$C%lr2&LCwP^qMR=c|9M?PHG)ioz#-Y%|Q}1 zN4_;c+9j7U@NTq9ba&*KW-;P`#I)`JeN6KKr!E8Cl%*?q(T|d8+A!Lrqkhj_BvqMt zAkU<^oQGO)2bcla@DWvoSi~s&Xpu0acj?7b@~2*bN%V+r=J$r$!NS))m5<5T;8nke zf7__?2H|Qjd7CWC@Acm_$3NZh!{YW3QNrh_ujb(nL@NIMZ=I|$=~B3EI9be~O?GKS zIb;N27$nYbISHoag`Ov1qpZW^d571P5=zq>N_hcRwzU<hwjoE589`gcgl5gP44KkU zq(pD0i;!%na%mdtEiFyB8Z1I9ei2~Fccq!ZKojx8cCdJ%_<|P6f<xAZ$C^Dff^xEr zm^3`cvJoTQCHNqVlk!=Feg=)CpG**6-O}*7BN2vPl3ad^keB1^N`;E))QY~XCvh63 zsU<e0H00v$&`RqlrpVzG07}eR_ydMZjwz{Q2jfT_J~0|TNh5y;a);tZ^Vtjmd?>~u z6v2of7xf>^E-^_>!!a|bQ3Is~8x#~WQCuzC#O{ROgC$?0UJ8(DG`XfoZD7a<fC#AM zUYvEdfwz+N=ZNAblv?mo1FGfYcL=Iw0z&x41rhX?rXjIf7wgDzBv9!<WmGz&NaA&9 z$3}n5ZLDj68o`}E5-&aGM?t5Ql3E^Bpl7}BNN9y1(lE|EA&sxIU?8nXg{%Xs+K?+B zxCr$=!@gvei(&!{&0d9A5AXkmwVVHXVG0+A$oS9^&0%ypY=kl;za%`hDsgR+6xLJi zVYZ7|z>;DYf!}jI!{7{zNDqkN@7!>7Lx9f6G8MrjzB#=15rtgJM?>V4Ap%)4a9+Bw zP%i@iw}qhUkEIa|=A%gPQCQCi$Pv*Be-flxCu?KAi{#lysnZe%p!^II=VkX6j_$oc z5b>TR_>#KeUshL0ACja{1)^ltSud}{eg}M#`{1pY$X}zfB`pG5?mr;n`iUwqbR=R* zGMRM_8pzsb?#Dq^qG;<!12-D^CQ>1g9Z~oeQDqg$7;*%bHs}J+8F!cEBRqdTpWRNy zN8*-rn^U%a@LEj7N7Dj4pqfrWmiEBY|NP=Me{VIf2EYE-cc%av{@)UrT#+ncQgy5a zWGp6JabCV)0!zeJkc}szQRs8nRdo~q0lvl7gjpv*=t)URVGE&<^*quzm)r6Rm*$J- z@7U|u%q6;0h#=E>&&W+-I)+|@y|7Pd%v5Y81{6q1qm!#4*F@=EHbBF~x@M+x5U#ko zQ@IPmRKUq$*|KK0EA*LxssTRocZGb~7k9uv?P&&Q@jx!*3HTCp!gTU*_W%&GceV^7 z**0y61j80Y>U!-C0Lf0V3?O+uDuV8d8es~=yn9y~o#08KaJ}X{nDU}Z{FQK4%O48r zOJ&`ahSFL5fQrq#x-DCLj0K1Tj4DDFXd8hqVG^W<4Ln2^6zod$gj6DI=FwXq6gH}7 z6wH2;QOgB|XN6%)&L<%#%O|PAqV$n*OD_3uELGk@L9LX8jkSdS1Kl9xu^vDPw@8)z z%(G}Qwx1#1Si(x3ZcMiRTJ55h4>l@t=bYnJx(Y)tycf2sH&eW!0t1r(leRbYYqXQ; zGGmJ>dl2iXa1D_NjF4iimoN0057~`2nQ4EQ=7g*3EM^5H<X2r@tFwfd^*a4xC2%~1 z)TZ5Dn%rbjf=VD3N))L%0~a#Qf%cS6wK`~NLPk$Y!kB7LQT+=Emc7%hULx^HvL~*P zE=2^SRiA51KoGpV()+_@N{|TU>>`WI;$Y5rt{9imu~mTsFA$PSYfVm(-=*avO2}JD zdOxxkB$Q?ysL}!*)mhT{4sW5CO?kkq=SR0w7r{@mg)29`T$!n(iB&Mq*(qE!Yn%gj z5zkSXLpm(CF@aO}s5#sqPVoC@T>gX(kVs=64V7WuB%z3b%nVk&ZKkC0v{I0^lj_GZ zQL<A4`$J_WpEtow==t8tMD4S4AlhaT7ihDvwEjZTQn;+OTmzstWd}!B5z63ZtpG;* z`e^_oQp@5Ov8OYn1V<P}vZb67z>;7<rO*O%M2%PO3hWvK+GA}V`GGOJI8qqCt&FxX zZ5{%ki?6-7+`ig40;3AGg&Ia<Y+U&WX$cnbenVnMmBs>4g4tcd^bkJ~c&okST0Ol7 zKq60%{Tcq>jVn)Yo1n_McOwToS+PT>TyOxLV$w29EQf$?1BtXVQ6{898y!mnyA1tA zE&`j4>aH{}%1nI3unLdYV?BEO;o9w_PHB%%q?}2$WKOii<LfMvkjE!fm2sAOJzgaz zt2|!$L0NAvv5MQy2c1>t)l`4c!3S+S%T&4o{~pfgQ{e`LFhCrDy1ZjTDcUC6%Gn$( zDaZhkbwL(?VuuuI-#59=cg}dD0-Qs7P>s5OKoZPKC$r95vd?%ucf>QCH>qR070cKw zR(T1xhLz06<66jq2w#nH=&{7wyu6SMC;VJn*+_1~vNt-~JxkFq!6H=*tu}sx%AYyo zsXK8HO#35TMhGU({f&rkaI87L@gt6rTt{Jx;fC4-j*pXQ2%#ePZinCr5T*~#6eLXc z@lSRrP+U|}Ba_vl&ZJ@6#$d&uS|M)#QSWIa>|aOok<S-P9<MU#bB_DC%EW;e3{l?7 zF@FHDwP`X6VExzs=6w(AR7C1eE4DW~Y8cDDXr%Vp$Yhm^e#v`CbS=VUC$<?f+n^Ck zVeV2A-7r~S?tYfow3Z<TQ`-f}AuB{$3QVX~@zAD3WsRwh6H+ITY28msh_i1H$%6aR z+ey%TJvUGI1Sd!W>$_YpQ3om^EDWSh9`E}?!b)3NbvuEN7fI5M7nzF-T{uLc1)2*1 zY&|L}!$CQHb%95P`9dJMB}WRpaMe-4@!oG7ko1flI~L$Gd$2$7u~i*LGNNpuuImS+ z)<UY`z$KzPbg@l1IV)djjFd9eDnu>KNs3w@+^?2mh}k%SO>t>4BUMX~f^HyC8pGSY zl%bd_AabKVd;P$sKmn{~8y#SHt2!{r&@#5_9`7#Dg$zoT6+Ft?aOUm;FA;L7-Otqo zx(8vb&0h2eYpyA921dQ*YFf#CgyI!nPLN#9wHAH`9*O&F@&xGv5AAA#tq^=?1z)`w z=xi$~N<=kZQ-J?khs@NPxzjy`q@V-<`V$}eD_dE%)IkVtowoUNo%zE``K1r;f&`f_ z7Sx4Pv4`!-Y8~BZ>j%K~c;p@*Vv=hoW6=Y=l+@k)un7W8Yuoto$bIgzHC7<(Vby%( zz7pRhOfXsQ<N5oq`PSh173Huc_In=J;$Wh~@YfIP{Cy=p4m2|P)9x2+s1&TujC@T; z^4H(5>zv_SZthOppX+pftGg+NRChW*ZGLqe(XaE&X_opDTi3M4*0;slQzuf4i|ZRK z#ydTSi(5gw!-<sa&S{0)p*aadTnb?Hp|nb1kOol~N37v1xpW9fOpaWGAJj6pJI}(U zPCBomKG%gLV%cXBx|EV*!r{06m4tS0^8+rBmz9wuy2#WXmV76HMT({ye<pzs6ct5c zbs`ZJWuMD$fU0Y0@|!cI>kIYb(mAq3Ivbl@sZ%JFB3|z-3D3Fr;~RA(M1Sk*>jbP7 zKvH;!qZ0I@uq%yT4=h`wZ>s^}^x-_P^G)BDZeLt%8fs;Z=WUm+Ef?!Pu1nX6ceMAK z!Mm>&>Qo4}6zU!#$`u1neE>&au}%ZrL#<0Kcgb(_AaZ;2$}xg0D^7bvI$4;aJ?ae7 zY1=RVors#FJ7hT&-C1<%yfYK#Jw)tVgGKd$Ac3wH>EnG1hD)rid}c1YrH1H$p6~58 zHkA*{)!!fQTW~?de9ah%^W%MsdcGBSff~W%eG3(1m=6RLf@)nLzV+n`eOyXb3{Rrp zrt-8Dk)QdYCVksNHXYEH%nMFd@hz@&RaM;yqnO|}^k?|IDt#PPeGo-{I8YrEp$}uB zRpxuE@+cjem@4zY9C%irRzef?DPeRsYZw(N<RvMDeo1Hj-rQDOMH#)*9eA?hO&r<* zN@mDXrP%{9+un}ZMnUQX9G^X7{hX*<^_%L6B@Kh+f%x75Kf!8euIJ}3_<?i8(zM43 zDF^e|!LLE658AI0eht&@TfXwPaDjZ8x2zwmU|3%}-d4VQX4qFhEM@&>l%jY;UX?4T zo3@+nIuw4KIcIM4eIo*prM|vI`ZZIuX9e-e`vQ9`#N{OtK{14fA%Nry(N(I6KC4|u zP4yGCXHA(^Mcr*HP#WuJYlAM%<J6S~OO*+5TcvS&&-d1I{>6&>QZm{ZcXI1^-n0{W z075o9c3mu_5+JlE@)#rctV4W@bD3#p*9-=!eEb2$@CQAW%1BnJBMtIqPI|vjR&%%i zgl0~o^0RpefvgWQv0u_p6eb~`jJ@!KJl;V4NJcgY)La1m_N{^4RRCC!M{V<Nv;OeY z=Ii&0_&>F*YiYB%?N4pVEWl7(v#jky>szEpEB0h7$(87r5EIMPHBkB`#x3)vO)i{n zKyeP-igPF`d@doIGCBT!W;$K0*OOA(&F5Byu*L|sQol=LuBB0E6|0g8jW<*07bCDv zR9vIWO=Gd0B=}I{vQ1@ItwZEMBfs)g;TkF(*-f$88c{u@)*igAQFMBPykc!5>dTH_ zlzuK%`SN}5Ttrui%J)t07X-(&UJO)LYXEC@J-6_vJzK(;BmAo|2|758Y^Na0D<2L4 zQqy*)CMg01Bxl<kSgr*SR-DVs+ioQUe?j$ADp;UxjvQA8ze^H)ldt7PwV&*g?lBan z9$#aona@QG?FCv{?}JOUlC92gDR}qd%X*E<(+&l@N!i~NlXV!x1crY*gc%G*mu*jY zz3vbnShUi|G-F62aawh$0qFIPM)=bU%3uzD_j0s|#nJKg!NKXNBfpx91N6ciu&3`7 zMh-q$&QBXgLk-6bJSX|Lt+57;CGW@Z^@8Z+;5(e?3dD$hQM&n5Zap6zH=oMS+`gqr zj*_t&PaHQ4-A_jcj?-W-iFgy+Jj7!lm-EBZq@9ERHE3~>+udp*VL#$mdPkFAzkU7g z$0rz^-^ky(l-*F`QeBSa`-Zj~S{PSNh`rM#ll}DU=Qqi0baq|NC6BHroph->L!l;w z!Fm-1sb2<6hAqoDtqrTDTh4Gwx~2J*J7B9QSgqr<*jFoA@2{_#Z8^W`nU*lD)u=w~ zTvD`~PsQ^!tb-ca8Oejl#$`k6tVXV(@%cf170ot=6Pl<(jg3di2zKSNps($1(gB$~ zNu#RVXS-RWYx+{v`)qq6QSq)Ql*7+H;n>UorVzR#{L(D_yc2hAEaLIyY}kXej)9X~ zA(>(KAwn8{4$F7tL83Jfy2R#5((1PkS&|7bu*C8D8+CGH#(Gb<T414_T?O!#d#v(x zLr}`;*E>}4s<(spZ0SDMrbh)Hv<HK>1zoMuzw&UwKzsq*=E5=ri})q+B{52Rjk6bS zBowyU13wCD3!6}|_1xXg%BkTJHL6>~Wt!bR+1iE>lR}ow!U@Z}D+n#@(agGA3}Ck8 zN`x7ynUdtRzycDl$W2pAxM5SKnXCXfm&d-zk`8Yy9R_Gpi?x-&#~uJY(by_~-Xzz0 z#4+UOjnuOB<#7yFYK-ln2WPgk7kf@RPy_GAy6tj}U0|yP!&0TCPzcA65%rM+_;&&r z)xeJ*Oi&u(vXUq68kwSzAHe%#yGnF?(0nSZLr11Eq_f&Y%Sr}GT57UCm3<I0H*^I+ z{=*yxKc+eOU{aKxgTO&iPTh@7sW_m~=4e4q%5BG9=c3iQ8a3O!3(@Sk7njYe;i%ml z4$s`-@WLIPwbeZgU3k%BBso&!XBL%BFOQ8M`RsJg&acj8c+{|K42{&w)%-{eun2Mt z$?(Tw=xW@#?6}@Vb2J){n!VOlyLs8`^qS*gXV@FH#JSg5&(Iszc}fG7azu7i%uhF~ zptg4|MEml**&B8{&E7>9{D{j=bJQAMdacXhaMYp_0F=E#K`-=t+MX<KE?So@IY5Da z)JxWnRg_H?8*(0CqMXKdt{^QcCH1tBG%`}fV>bub-gu4WX(FTuA-UDGY+$?=F`F7* zS!&iAo<+-MMze+z%V<(?D^R<iPUrXhn@RecIQG}>qdv87IxN4w6Zl@=X+Lr9=)WEL z3e4~e=TbetTJpTzO6T`^i?!CQEsfTc6_Z6wdQ&<nQv1tAuk=cg?qKA~uUFfcLUp@~ zu}%U8gAAPu(Q3Ea-DY>xBXAsbnr^py-aNk;U3D*qo;&KPvCLA)E3aAv*&EYjYQA{B zVD4ebBrHld%B()=mpQ5l(pU4VKtI<r2cDOgUb{0C?Pjk{tJ`_+tm%$Duh|}7wtDR= z(LEol#UR74@sl*ITX8G5Y%-$A(cC8RWp-^>Sp%?^xt0angy8~(X+8^HZDOr<<e}km zole)-a6&FynuV%6Xh_YO9wDcpthH&5jEj{sRyAyfvHcm|mGo4@ZbfV<AN*o$u|E|V KU|L<4v;9A098nYi diff --git a/core/modules/workspaces/tests/src/Functional/Update/WorkspacesUpdateTest.php b/core/modules/workspaces/tests/src/Functional/Update/WorkspacesUpdateTest.php deleted file mode 100644 index 0fffd21e3784..000000000000 --- a/core/modules/workspaces/tests/src/Functional/Update/WorkspacesUpdateTest.php +++ /dev/null @@ -1,105 +0,0 @@ -<?php - -namespace Drupal\Tests\workspaces\Functional\Update; - -use Drupal\FunctionalTests\Update\UpdatePathTestBase; - -/** - * Tests the upgrade path for the Workspaces module. - * - * @group workspaces - * @group Update - * @group legacy - */ -class WorkspacesUpdateTest extends UpdatePathTestBase { - - /** - * {@inheritdoc} - */ - protected static $modules = ['workspaces']; - - /** - * {@inheritdoc} - */ - public function setDatabaseDumpFiles() { - $this->databaseDumpFiles = [ - __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.filled.standard.php.gz', - __DIR__ . '/../../../fixtures/update/drupal-8.6.0-workspaces_installed.php', - ]; - } - - /** - * Tests the move of workspace association data to a custom table. - * - * @see workspaces_update_8801() - * @see workspaces_post_update_move_association_data() - */ - public function testWorkspaceAssociationRemoval() { - $database = \Drupal::database(); - - // Check that we have two records in the 'workspace_association' base table - // and three records in its revision table. - $wa_records = $database->select('workspace_association')->countQuery()->execute()->fetchField(); - $this->assertEquals(2, $wa_records); - $war_records = $database->select('workspace_association_revision')->countQuery()->execute()->fetchField(); - $this->assertEquals(3, $war_records); - - // Check that the node entity type does not have a 'workspace' field. - $this->assertNull(\Drupal::entityDefinitionUpdateManager()->getFieldStorageDefinition('workspace', 'node')); - - $this->runUpdates(); - - $entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager(); - - // Check that the 'workspace' field has been installed for an entity type - // that was workspace-supported before Drupal 8.7.0. - $this->assertTrue($entity_definition_update_manager->getFieldStorageDefinition('workspace', 'node')); - - // Check that the 'workspace' field has been installed for an entity type - // which became workspace-supported as part of an entity schema update. - $this->assertTrue($entity_definition_update_manager->getFieldStorageDefinition('workspace', 'taxonomy_term')); - - // Check that the 'workspace' revision metadata field has been created only - // in the revision table. - $schema = $database->schema(); - $this->assertTrue($schema->fieldExists('node_revision', 'workspace')); - $this->assertFalse($schema->fieldExists('node', 'workspace')); - $this->assertFalse($schema->fieldExists('node_field_data', 'workspace')); - $this->assertFalse($schema->fieldExists('node_field_revision', 'workspace')); - - // Check that the 'workspace_association' records have been migrated - // properly. - $wa_records = $database->select('workspace_association')->fields('workspace_association')->execute()->fetchAll(\PDO::FETCH_ASSOC); - $expected = [ - [ - 'workspace' => 'stage', - 'target_entity_type_id' => 'node', - 'target_entity_id' => '1', - 'target_entity_revision_id' => '2', - ], - [ - 'workspace' => 'dev', - 'target_entity_type_id' => 'node', - 'target_entity_id' => '8', - 'target_entity_revision_id' => '10', - ], - ]; - $this->assertEquals($expected, $wa_records); - - // Check that the 'workspace_association' revisions has been migrated - // properly to the new 'workspace' revision metadata field. - $revisions = \Drupal::entityTypeManager()->getStorage('node')->loadMultipleRevisions([2, 9, 10]); - $this->assertEquals('stage', $revisions[2]->workspace->target_id); - $this->assertEquals('dev', $revisions[9]->workspace->target_id); - $this->assertEquals('dev', $revisions[10]->workspace->target_id); - - // Check that the 'workspace_association' entity type has been uninstalled. - $this->assertNull($entity_definition_update_manager->getEntityType('workspace_association')); - $this->assertNull($entity_definition_update_manager->getFieldStorageDefinition('id', 'workspace_association')); - $this->assertNull(\Drupal::keyValue('entity.storage_schema.sql')->get('workspace_association.entity_schema_data')); - - // Check that the 'workspace_association_revision' table has been removed. - $this->assertFalse($schema->tableExists('workspace_association_revision')); - } - -} diff --git a/core/modules/workspaces/tests/src/Functional/WorkspacesUninstallTest.php b/core/modules/workspaces/tests/src/Functional/WorkspacesUninstallTest.php index e4932eca3282..e652e3fc826b 100644 --- a/core/modules/workspaces/tests/src/Functional/WorkspacesUninstallTest.php +++ b/core/modules/workspaces/tests/src/Functional/WorkspacesUninstallTest.php @@ -36,12 +36,6 @@ public function testUninstallingWorkspace() { $this->drupalPostForm(NULL, [], 'Uninstall'); $session->pageTextContains('The selected modules have been uninstalled.'); $session->pageTextNotContains('Workspaces'); - - $this->assertFalse($this->getDatabaseConnection()->schema()->fieldExists('node_revision', 'workspace')); - - // Verify that the revision metadata key has been removed. - $revision_metadata_keys = \Drupal::entityDefinitionUpdateManager()->getEntityType('node')->get('revision_metadata_keys'); - $this->assertArrayNotHasKey('workspace', $revision_metadata_keys); } } diff --git a/core/modules/workspaces/tests/src/Kernel/WorkspaceAccessTest.php b/core/modules/workspaces/tests/src/Kernel/WorkspaceAccessTest.php index f8a4964eda30..b8065a15c8dd 100644 --- a/core/modules/workspaces/tests/src/Kernel/WorkspaceAccessTest.php +++ b/core/modules/workspaces/tests/src/Kernel/WorkspaceAccessTest.php @@ -33,6 +33,7 @@ protected function setUp() { $this->installSchema('system', ['sequences']); $this->installEntitySchema('workspace'); + $this->installEntitySchema('workspace_association'); $this->installEntitySchema('user'); // User 1. diff --git a/core/modules/workspaces/tests/src/Kernel/WorkspaceCRUDTest.php b/core/modules/workspaces/tests/src/Kernel/WorkspaceCRUDTest.php index c8b1705a7e07..d7d5710c5701 100644 --- a/core/modules/workspaces/tests/src/Kernel/WorkspaceCRUDTest.php +++ b/core/modules/workspaces/tests/src/Kernel/WorkspaceCRUDTest.php @@ -7,6 +7,7 @@ use Drupal\Tests\node\Traits\NodeCreationTrait; use Drupal\Tests\user\Traits\UserCreationTrait; use Drupal\workspaces\Entity\Workspace; +use Drupal\workspaces\Entity\WorkspaceAssociation; /** * Tests CRUD operations for workspaces. @@ -18,7 +19,6 @@ class WorkspaceCRUDTest extends KernelTestBase { use UserCreationTrait; use NodeCreationTrait; use ContentTypeCreationTrait; - use WorkspaceTestTrait; /** * The entity type manager. @@ -66,7 +66,7 @@ protected function setUp() { $this->installSchema('node', ['node_access']); $this->installEntitySchema('workspace'); - $this->installSchema('workspaces', ['workspace_association']); + $this->installEntitySchema('workspace_association'); $this->installEntitySchema('node'); $this->installConfig(['filter', 'node', 'system']); @@ -91,8 +91,10 @@ public function testDeletingWorkspaces() { ]); $this->setCurrentUser($admin); - /** @var \Drupal\workspaces\WorkspaceAssociationInterface $workspace_association */ - $workspace_association = \Drupal::service('workspaces.association'); + /** @var \Drupal\workspaces\WorkspaceAssociationStorageInterface $workspace_association_storage */ + $workspace_association_storage = $this->entityTypeManager->getStorage('workspace_association'); + /** @var \Drupal\node\NodeStorageInterface $node_storage */ + $node_storage = $this->entityTypeManager->getStorage('node'); // Create a workspace with a very small number of associated node revisions. $workspace_1 = Workspace::create([ @@ -104,12 +106,6 @@ public function testDeletingWorkspaces() { $workspace_1_node_1 = $this->createNode(['status' => FALSE]); $workspace_1_node_2 = $this->createNode(['status' => FALSE]); - - // The 'live' workspace should have 2 revisions now. The initial revision - // for each node. - $live_revisions = $this->getUnassociatedRevisions('node'); - $this->assertCount(2, $live_revisions); - for ($i = 0; $i < 4; $i++) { $workspace_1_node_1->setNewRevision(TRUE); $workspace_1_node_1->save(); @@ -118,17 +114,9 @@ public function testDeletingWorkspaces() { $workspace_1_node_2->save(); } - // The workspace should now track 2 nodes. - $tracked_entities = $workspace_association->getTrackedEntities($workspace_1->id()); - $this->assertCount(2, $tracked_entities['node']); - - // There should still be 2 revisions associated with 'live'. - $live_revisions = $this->getUnassociatedRevisions('node'); - $this->assertCount(2, $live_revisions); - - // The other 8 revisions should be associated with 'workspace_1'. - $associated_revisions = $workspace_association->getAssociatedRevisions($workspace_1->id(), 'node'); - $this->assertCount(8, $associated_revisions); + // The workspace should have 10 associated node revisions, 5 for each node. + $associated_revisions = $workspace_association_storage->getTrackedEntities($workspace_1->id(), TRUE); + $this->assertCount(10, $associated_revisions['node']); // Check that we are allowed to delete the workspace. $this->assertTrue($workspace_1->access('delete', $admin)); @@ -137,17 +125,14 @@ public function testDeletingWorkspaces() { // entities and all the node revisions have been deleted as well. $workspace_1->delete(); - // There are no more tracked entities in 'workspace_1'. - $tracked_entities = $workspace_association->getTrackedEntities($workspace_1->id()); - $this->assertEmpty($tracked_entities); - - // There are no more revisions associated with 'workspace_1'. - $associated_revisions = $workspace_association->getAssociatedRevisions($workspace_1->id(), 'node'); + $associated_revisions = $workspace_association_storage->getTrackedEntities($workspace_1->id(), TRUE); $this->assertCount(0, $associated_revisions); - - // There should still be 2 revisions associated with 'live'. - $live_revisions = $this->getUnassociatedRevisions('node'); - $this->assertCount(2, $live_revisions); + $node_revision_count = $node_storage + ->getQuery() + ->allRevisions() + ->count() + ->execute(); + $this->assertEquals(0, $node_revision_count); // Create another workspace, this time with a larger number of associated // node revisions so we can test the batch purge process. @@ -164,27 +149,16 @@ public function testDeletingWorkspaces() { $workspace_2_node_1->save(); } - // Now there is one entity tracked in 'workspace_2'. - $tracked_entities = $workspace_association->getTrackedEntities($workspace_2->id()); - $this->assertCount(1, $tracked_entities['node']); - - // One revision of this entity is in 'live'. - $live_revisions = $this->getUnassociatedRevisions('node', [$workspace_2_node_1->id()]); - $this->assertCount(1, $live_revisions); + // The workspace should have 60 associated node revisions. + $associated_revisions = $workspace_association_storage->getTrackedEntities($workspace_2->id(), TRUE); + $this->assertCount(60, $associated_revisions['node']); - // The other 59 are associated with 'workspace_2'. - $associated_revisions = $workspace_association->getAssociatedRevisions($workspace_2->id(), 'node', [$workspace_2_node_1->id()]); - $this->assertCount(59, $associated_revisions); - - // Delete the workspace and check that we still have 9 revision left to + // Delete the workspace and check that we still have 10 revision left to // delete. $workspace_2->delete(); - $associated_revisions = $workspace_association->getAssociatedRevisions($workspace_2->id(), 'node', [$workspace_2_node_1->id()]); - $this->assertCount(9, $associated_revisions); - // The live revision is also still there. - $live_revisions = $this->getUnassociatedRevisions('node', [$workspace_2_node_1->id()]); - $this->assertCount(1, $live_revisions); + $associated_revisions = $workspace_association_storage->getTrackedEntities($workspace_2->id(), TRUE); + $this->assertCount(10, $associated_revisions['node']); $workspace_deleted = \Drupal::state()->get('workspace.deleted'); $this->assertCount(1, $workspace_deleted); @@ -203,94 +177,41 @@ public function testDeletingWorkspaces() { // from the "workspace.delete" state entry. \Drupal::service('cron')->run(); - $associated_revisions = $workspace_association->getTrackedEntities($workspace_2->id()); - $this->assertCount(0, $associated_revisions); - - // 'workspace_2 'is empty now. - $associated_revisions = $workspace_association->getAssociatedRevisions($workspace_2->id(), 'node', [$workspace_2_node_1->id()]); + $associated_revisions = $workspace_association_storage->getTrackedEntities($workspace_2->id(), TRUE); $this->assertCount(0, $associated_revisions); - $tracked_entities = $workspace_association->getTrackedEntities($workspace_2->id()); - $this->assertEmpty($tracked_entities); - - // The 3 revisions in 'live' remain. - $live_revisions = $this->getUnassociatedRevisions('node'); - $this->assertCount(3, $live_revisions); + $node_revision_count = $node_storage + ->getQuery() + ->allRevisions() + ->count() + ->execute(); + $this->assertEquals(0, $node_revision_count); $workspace_deleted = \Drupal::state()->get('workspace.deleted'); $this->assertCount(0, $workspace_deleted); } /** - * Tests that deleting a workspace keeps its already published content. + * Tests workspace association validation. + * + * @covers \Drupal\workspaces\Entity\WorkspaceAssociation::validate */ - public function testDeletingPublishedWorkspace() { - $admin = $this->createUser([ - 'administer nodes', - 'create workspace', - 'view any workspace', - 'delete any workspace', - ]); - $this->setCurrentUser($admin); - - $live_workspace = Workspace::create([ - 'id' => 'live', - 'label' => 'Live', - ]); - $live_workspace->save(); + public function testWorkspaceAssociationValidation() { $workspace = Workspace::create([ - 'id' => 'stage', - 'label' => 'Stage', + 'id' => 'gibbon', + 'label' => 'Gibbon', ]); $workspace->save(); - $this->workspaceManager->setActiveWorkspace($workspace); - - // Create a new node in the 'stage' workspace - $node = $this->createNode(['status' => TRUE]); - - // Create an additional workspace-specific revision for the node. - $node->setNewRevision(TRUE); - $node->save(); - - // The node should have 3 revisions now: a default and 2 pending ones. - $revisions = $this->entityTypeManager->getStorage('node')->loadMultipleRevisions([1, 2, 3]); - $this->assertCount(3, $revisions); - $this->assertTrue($revisions[1]->isDefaultRevision()); - $this->assertFalse($revisions[2]->isDefaultRevision()); - $this->assertFalse($revisions[3]->isDefaultRevision()); - - // Publish the workspace, which should mark revision 3 as the default one - // and keep revision 2 as a 'source' draft revision. - $workspace->publish(); - $revisions = $this->entityTypeManager->getStorage('node')->loadMultipleRevisions([1, 2, 3]); - $this->assertFalse($revisions[1]->isDefaultRevision()); - $this->assertFalse($revisions[2]->isDefaultRevision()); - $this->assertTrue($revisions[3]->isDefaultRevision()); - - // Create two new workspace-revisions for the node. - $node->setNewRevision(TRUE); - $node->save(); - $node->setNewRevision(TRUE); - $node->save(); - - // The node should now have 5 revisions. - $revisions = $this->entityTypeManager->getStorage('node')->loadMultipleRevisions([1, 2, 3, 4, 5]); - $this->assertFalse($revisions[1]->isDefaultRevision()); - $this->assertFalse($revisions[2]->isDefaultRevision()); - $this->assertTrue($revisions[3]->isDefaultRevision()); - $this->assertFalse($revisions[4]->isDefaultRevision()); - $this->assertFalse($revisions[5]->isDefaultRevision()); - - // Delete the workspace and check that only the two new pending revisions - // were deleted by the workspace purging process. - $workspace->delete(); - - $revisions = $this->entityTypeManager->getStorage('node')->loadMultipleRevisions([1, 2, 3, 4, 5]); - $this->assertCount(3, $revisions); - $this->assertFalse($revisions[1]->isDefaultRevision()); - $this->assertFalse($revisions[2]->isDefaultRevision()); - $this->assertTrue($revisions[3]->isDefaultRevision()); - $this->assertFalse(isset($revisions[4])); - $this->assertFalse(isset($revisions[5])); + $node = $this->createNode(); + + $workspace_association = WorkspaceAssociation::create([ + 'workspace' => $workspace, + 'target_entity_type_id' => $node->getEntityTypeId(), + 'target_entity_id' => $node->id(), + 'target_entity_revision_id' => $node->getRevisionId(), + ]); + + $violations = $workspace_association->validate(); + $this->assertCount(0, $violations); } } diff --git a/core/modules/workspaces/tests/src/Kernel/WorkspaceIntegrationTest.php b/core/modules/workspaces/tests/src/Kernel/WorkspaceIntegrationTest.php index 1530f3865830..20a7efd04e30 100644 --- a/core/modules/workspaces/tests/src/Kernel/WorkspaceIntegrationTest.php +++ b/core/modules/workspaces/tests/src/Kernel/WorkspaceIntegrationTest.php @@ -275,7 +275,7 @@ public function testWorkspaces() { ], ]); $test_scenarios['add_published_node_in_stage'] = $revision_state; - $expected_workspace_association['add_published_node_in_stage'] = ['stage' => [3, 4, 5, 7]]; + $expected_workspace_association['add_published_node_in_stage'] = ['stage' => [3, 4, 5, 6, 7]]; // Deploying 'stage' to 'live' should simply make the latest revisions in // 'stage' the default ones in 'live'. @@ -365,9 +365,8 @@ public function testEntityQueryWithoutConditions() { $this->switchToWorkspace('stage'); // Add a workspace-specific revision to a pre-existing node. - $node = $this->entityTypeManager->getStorage('node')->load(2); - $node->title->value = 'stage - 2 - r3 - published'; - $node->save(); + $this->nodes[1]->title->value = 'stage - 2 - r3 - published'; + $this->nodes[1]->save(); $query = $this->entityTypeManager->getStorage('node')->getQuery(); $query->sort('nid'); @@ -810,7 +809,7 @@ protected function assertEntityQuery(array $expected_values, $entity_type_id) { } /** - * Checks the workspace_association records for a test scenario. + * Checks the workspace_association entries for a test scenario. * * @param array $expected * An array of expected values, as defined in ::testWorkspaces(). @@ -818,10 +817,10 @@ protected function assertEntityQuery(array $expected_values, $entity_type_id) { * The ID of the entity type that is being tested. */ protected function assertWorkspaceAssociation(array $expected, $entity_type_id) { - /** @var \Drupal\workspaces\WorkspaceAssociationInterface $workspace_association */ - $workspace_association = \Drupal::service('workspaces.association'); + /** @var \Drupal\workspaces\WorkspaceAssociationStorageInterface $workspace_association_storage */ + $workspace_association_storage = $this->entityTypeManager->getStorage('workspace_association'); foreach ($expected as $workspace_id => $expected_tracked_revision_ids) { - $tracked_entities = $workspace_association->getTrackedEntities($workspace_id, $entity_type_id); + $tracked_entities = $workspace_association_storage->getTrackedEntities($workspace_id, TRUE); $tracked_revision_ids = isset($tracked_entities[$entity_type_id]) ? $tracked_entities[$entity_type_id] : []; $this->assertEquals($expected_tracked_revision_ids, array_keys($tracked_revision_ids)); } diff --git a/core/modules/workspaces/tests/src/Kernel/WorkspaceInternalResourceTest.php b/core/modules/workspaces/tests/src/Kernel/WorkspaceInternalResourceTest.php new file mode 100644 index 000000000000..06201ce7446d --- /dev/null +++ b/core/modules/workspaces/tests/src/Kernel/WorkspaceInternalResourceTest.php @@ -0,0 +1,43 @@ +<?php + +namespace Drupal\Tests\workspaces\Kernel; + +use Drupal\Component\Plugin\Exception\PluginNotFoundException; +use Drupal\KernelTests\KernelTestBase; +use Drupal\rest\Entity\RestResourceConfig; +use Drupal\rest\RestResourceConfigInterface; + +/** + * Tests REST module with internal workspace entity types. + * + * @group workspaces + */ +class WorkspaceInternalResourceTest extends KernelTestBase { + + /** + * {@inheritdoc} + */ + public static $modules = ['user', 'serialization', 'rest', 'workspaces']; + + /** + * Tests enabling workspace associations for REST throws an exception. + * + * @see \Drupal\workspaces\Entity\WorkspaceAssociation + */ + public function testCreateWorkspaceAssociationResource() { + $this->expectException(PluginNotFoundException::class); + $this->expectExceptionMessage('The "entity:workspace_association" plugin does not exist.'); + RestResourceConfig::create([ + 'id' => 'entity.workspace_association', + 'granularity' => RestResourceConfigInterface::RESOURCE_GRANULARITY, + 'configuration' => [ + 'methods' => ['GET'], + 'formats' => ['json'], + 'authentication' => ['cookie'], + ], + ]) + ->enable() + ->save(); + } + +} diff --git a/core/modules/workspaces/tests/src/Kernel/WorkspaceTestTrait.php b/core/modules/workspaces/tests/src/Kernel/WorkspaceTestTrait.php index 50cf4704c396..13cff6a3dfeb 100644 --- a/core/modules/workspaces/tests/src/Kernel/WorkspaceTestTrait.php +++ b/core/modules/workspaces/tests/src/Kernel/WorkspaceTestTrait.php @@ -35,7 +35,7 @@ protected function initializeWorkspacesModule() { $this->workspaceManager = \Drupal::service('workspaces.manager'); $this->installEntitySchema('workspace'); - $this->installSchema('workspaces', ['workspace_association']); + $this->installEntitySchema('workspace_association'); // Create two workspaces by default, 'live' and 'stage'. $this->workspaces['live'] = Workspace::create(['id' => 'live']); @@ -64,33 +64,4 @@ protected function switchToWorkspace($workspace_id) { \Drupal::service('workspaces.manager')->setActiveWorkspace($workspace); } - /** - * Returns all the revisions which are not associated with any workspace. - * - * @param string $entity_type_id - * An entity type ID to find revisions for. - * @param int[]|string[]|null $entity_ids - * (optional) An array of entity IDs to filter the results by. Defaults to - * NULL. - * - * @return array - * An array of entity IDs, keyed by revision IDs. - */ - protected function getUnassociatedRevisions($entity_type_id, $entity_ids = NULL) { - $entity_type = \Drupal::entityTypeManager()->getDefinition($entity_type_id); - - $query = \Drupal::entityTypeManager() - ->getStorage($entity_type_id) - ->getQuery() - ->allRevisions() - ->accessCheck(FALSE) - ->notExists($entity_type->get('revision_metadata_keys')['workspace']); - - if ($entity_ids) { - $query->condition($entity_type->getKey('id'), $entity_ids, 'IN'); - } - - return $query->execute(); - } - } diff --git a/core/modules/workspaces/workspaces.install b/core/modules/workspaces/workspaces.install index 2bfc003f0e10..c6ac304b6db3 100644 --- a/core/modules/workspaces/workspaces.install +++ b/core/modules/workspaces/workspaces.install @@ -5,8 +5,6 @@ * Contains install, update and uninstall functions for the Workspaces module. */ -use Drupal\Core\Entity\EntityTypeInterface; -use Drupal\Core\Field\BaseFieldDefinition; use Drupal\workspaces\Entity\Workspace; /** @@ -34,27 +32,6 @@ function workspaces_requirements($phase) { return $requirements; } -/** - * Implements hook_module_preinstall(). - */ -function workspaces_module_preinstall($module) { - if ($module !== 'workspaces') { - return; - } - - /** @var \Drupal\workspaces\WorkspaceManagerInterface $workspace_manager */ - $workspace_manager = \Drupal::service('workspaces.manager'); - $entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager(); - foreach ($entity_definition_update_manager->getEntityTypes() as $entity_type) { - $revision_metadata_keys = $entity_type->get('revision_metadata_keys'); - if ($workspace_manager->isEntityTypeSupported($entity_type)) { - $revision_metadata_keys['workspace'] = 'workspace'; - $entity_type->set('revision_metadata_keys', $revision_metadata_keys); - $entity_definition_update_manager->updateEntityType($entity_type); - } - } -} - /** * Implements hook_install(). */ @@ -84,83 +61,3 @@ function workspaces_install() { 'uid' => $owner_id, ])->save(); } - -/** - * Implements hook_schema(). - */ -function workspaces_schema() { - $schema['workspace_association'] = [ - 'description' => 'Stores the association between entity revisions and their workspace.', - 'fields' => [ - 'workspace' => [ - 'type' => 'varchar_ascii', - 'length' => 128, - 'not null' => TRUE, - 'default' => '', - 'description' => 'The workspace ID.', - ], - 'target_entity_type_id' => [ - 'type' => 'varchar_ascii', - 'length' => EntityTypeInterface::ID_MAX_LENGTH, - 'not null' => TRUE, - 'default' => '', - 'description' => 'The ID of the associated entity type.', - ], - 'target_entity_id' => [ - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'description' => 'The ID of the associated entity.', - ], - 'target_entity_revision_id' => [ - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'description' => 'The revision ID of the associated entity.', - ], - ], - 'indexes' => [ - 'target_entity_revision_id' => ['target_entity_revision_id'], - ], - 'primary key' => ['workspace', 'target_entity_type_id', 'target_entity_id'], - ]; - - return $schema; -} - -/** - * Add the 'workspace' revision metadata field on all supported entity types. - */ -function workspaces_update_8801() { - /** @var \Drupal\workspaces\WorkspaceManagerInterface $workspace_manager */ - $workspace_manager = \Drupal::service('workspaces.manager'); - $entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager(); - foreach ($entity_definition_update_manager->getEntityTypes() as $entity_type_id => $entity_type) { - if ($workspace_manager->isEntityTypeSupported($entity_type)) { - $revision_metadata_keys = $entity_type->get('revision_metadata_keys'); - - if (!isset($revision_metadata_keys['workspace'])) { - // Bail out if there's an existing field called 'workspace'. - if ($entity_definition_update_manager->getFieldStorageDefinition('workspace', $entity_type_id)) { - throw new \RuntimeException("An existing 'workspace' field was found for the '$entity_type_id' entity type. Set the 'workspace' revision metadata key to use a different field name and run this update function again."); - } - - $revision_metadata_keys['workspace'] = 'workspace'; - $entity_type->set('revision_metadata_keys', $revision_metadata_keys); - $entity_definition_update_manager->updateEntityType($entity_type); - } - - $field_storage = BaseFieldDefinition::create('entity_reference') - ->setLabel(t('Workspace')) - ->setDescription(t('Indicates the workspace that this revision belongs to.')) - ->setSetting('target_type', 'workspace') - ->setInternal(TRUE) - ->setTranslatable(FALSE) - ->setRevisionable(TRUE); - - $entity_definition_update_manager->installFieldStorageDefinition($revision_metadata_keys['workspace'], $entity_type_id, 'workspaces', $field_storage); - } - } - - return t("The 'workspace' revision metadata field has been installed."); -} diff --git a/core/modules/workspaces/workspaces.module b/core/modules/workspaces/workspaces.module index c3b0d0776274..d47985b223de 100644 --- a/core/modules/workspaces/workspaces.module +++ b/core/modules/workspaces/workspaces.module @@ -8,7 +8,6 @@ use Drupal\Component\Serialization\Json; use Drupal\Core\Entity\EntityFormInterface; use Drupal\Core\Entity\EntityInterface; -use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Session\AccountInterface; @@ -67,15 +66,6 @@ function workspaces_field_info_alter(&$definitions) { ->fieldInfoAlter($definitions); } -/** - * Implements hook_entity_base_field_info(). - */ -function workspaces_entity_base_field_info(EntityTypeInterface $entity_type) { - return \Drupal::service('class_resolver') - ->getInstanceFromDefinition(EntityTypeInfo::class) - ->entityBaseFieldInfo($entity_type); -} - /** * Implements hook_entity_preload(). */ diff --git a/core/modules/workspaces/workspaces.post_update.php b/core/modules/workspaces/workspaces.post_update.php index ac45f3f93285..0df5add2cb8d 100644 --- a/core/modules/workspaces/workspaces.post_update.php +++ b/core/modules/workspaces/workspaces.post_update.php @@ -5,11 +5,6 @@ * Post update functions for the Workspaces module. */ -use Drupal\Core\Entity\ContentEntityNullStorage; -use Drupal\Core\Entity\EntityTypeInterface; -use Drupal\Core\Entity\Sql\SqlContentEntityStorage; -use Drupal\Core\Site\Settings; - /** * Clear caches due to access changes. */ @@ -24,122 +19,3 @@ function workspaces_post_update_remove_default_workspace() { $workspace->delete(); } } - -/** - * Move the workspace association data to an entity field and a custom table. - */ -function workspaces_post_update_move_association_data(&$sandbox) { - $database = \Drupal::database(); - $entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager(); - $entity_type_manager = \Drupal::entityTypeManager(); - $entity_type = $entity_definition_update_manager->getEntityType('workspace_association'); - - // We can't migrate the workspace association data if the entity type is not - // using its default storage. - if ($entity_type->getHandlerClasses()['storage'] !== 'Drupal\workspaces\WorkspaceAssociationStorage') { - return; - } - - // Since the custom storage class doesn't exist anymore, we have to use core's - // default storage. - $entity_type->setStorageClass(SqlContentEntityStorage::class); - - // If 'progress' is not set, this will be the first run of the batch. - if (!isset($sandbox['progress'])) { - $sandbox['progress'] = 0; - $sandbox['current_id'] = -1; - - // Create a temporary table for the new workspace_association index. - $schema = [ - 'description' => 'Stores the association between entity revisions and their workspace.', - 'fields' => [ - 'workspace' => [ - 'type' => 'varchar_ascii', - 'length' => 128, - 'not null' => TRUE, - 'default' => '', - 'description' => 'The workspace ID.', - ], - 'target_entity_type_id' => [ - 'type' => 'varchar_ascii', - 'length' => EntityTypeInterface::ID_MAX_LENGTH, - 'not null' => TRUE, - 'default' => '', - 'description' => 'The ID of the associated entity type.', - ], - 'target_entity_id' => [ - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'description' => 'The ID of the associated entity.', - ], - 'target_entity_revision_id' => [ - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'description' => 'The revision ID of the associated entity.', - ], - ], - 'indexes' => [ - 'target_entity_revision_id' => ['target_entity_revision_id'], - ], - 'primary key' => ['workspace', 'target_entity_type_id', 'target_entity_id'], - ]; - if ($database->schema()->tableExists('tmp_workspace_association')) { - $database->schema()->dropTable('tmp_workspace_association'); - } - $database->schema()->createTable('tmp_workspace_association', $schema); - - // Copy all the data from the base table of the 'workspace_association' - // entity type to the temporary association table. - $select = $database->select($entity_type->getBaseTable()) - ->fields($entity_type->getBaseTable(), ['workspace', 'target_entity_type_id', 'target_entity_id', 'target_entity_revision_id']); - $database->insert('tmp_workspace_association')->from($select)->execute(); - } - - $table_name = $entity_type->getRevisionTable(); - $revision_field_name = 'revision_id'; - - // Get the next entity association revision records to migrate. - $step_size = Settings::get('entity_update_batch_size', 50); - $workspace_association_records = $database->select($table_name, 't') - ->condition("t.$revision_field_name", $sandbox['current_id'], '>') - ->fields('t') - ->orderBy($revision_field_name, 'ASC') - ->range(0, $step_size) - ->execute() - ->fetchAll(); - - foreach ($workspace_association_records as $record) { - // Set the workspace reference on the tracked entity revision. - /** @var \Drupal\Core\Entity\ContentEntityInterface $revision */ - $revision = $entity_type_manager->getStorage($record->target_entity_type_id)->loadRevision($record->target_entity_revision_id); - $revision->set('workspace', $record->workspace); - $revision->setSyncing(TRUE); - $revision->save(); - - $sandbox['progress']++; - $sandbox['current_id'] = $record->{$revision_field_name}; - } - - // Get an updated count of workspace_association revisions that still need to - // be migrated to the new storage. - $missing = $database->select($table_name, 't') - ->condition("t.$revision_field_name", $sandbox['current_id'], '>') - ->orderBy($revision_field_name, 'ASC') - ->countQuery() - ->execute() - ->fetchField(); - $sandbox['#finished'] = $missing ? $sandbox['progress'] / ($sandbox['progress'] + (int) $missing) : 1; - - // Uninstall the 'workspace_association' entity type and rename the temporary - // table. - if ($sandbox['#finished'] == 1) { - $entity_type->setStorageClass(ContentEntityNullStorage::class); - $entity_definition_update_manager->uninstallEntityType($entity_type); - $database->schema()->dropTable('workspace_association'); - $database->schema()->dropTable('workspace_association_revision'); - - $database->schema()->renameTable('tmp_workspace_association', 'workspace_association'); - } -} diff --git a/core/modules/workspaces/workspaces.services.yml b/core/modules/workspaces/workspaces.services.yml index 45f1d5074614..9823d5ce2ec9 100644 --- a/core/modules/workspaces/workspaces.services.yml +++ b/core/modules/workspaces/workspaces.services.yml @@ -1,17 +1,12 @@ services: workspaces.manager: class: Drupal\workspaces\WorkspaceManager - arguments: ['@request_stack', '@entity_type.manager', '@entity.memory_cache', '@current_user', '@state', '@logger.channel.workspaces', '@class_resolver', '@workspaces.association'] + arguments: ['@request_stack', '@entity_type.manager', '@entity.memory_cache', '@current_user', '@state', '@logger.channel.workspaces', '@class_resolver'] tags: - { name: service_id_collector, tag: workspace_negotiator } workspaces.operation_factory: class: Drupal\workspaces\WorkspaceOperationFactory - arguments: ['@entity_type.manager', '@database', '@workspaces.manager', '@workspaces.association'] - workspaces.association: - class: Drupal\workspaces\WorkspaceAssociation - arguments: ['@database', '@entity_type.manager'] - tags: - - { name: backend_overridable } + arguments: ['@entity_type.manager', '@database', '@workspaces.manager'] workspaces.negotiator.session: class: Drupal\workspaces\Negotiator\SessionWorkspaceNegotiator @@ -30,12 +25,6 @@ services: tags: - { name: access_check, applies_to: _has_active_workspace } - workspaces.entity_schema_listener: - class: Drupal\workspaces\EventSubscriber\EntitySchemaSubscriber - arguments: ['@entity.definition_update_manager', '@entity.last_installed_schema.repository', '@workspaces.manager'] - tags: - - { name: 'event_subscriber' } - cache_context.workspace: class: Drupal\workspaces\WorkspaceCacheContext arguments: ['@workspaces.manager'] -- GitLab