diff --git a/core/modules/workspace/src/EntityQuery/SqlQueryFactory.php b/core/modules/workspace/src/EntityQuery/PgsqlQueryFactory.php similarity index 88% copy from core/modules/workspace/src/EntityQuery/SqlQueryFactory.php copy to core/modules/workspace/src/EntityQuery/PgsqlQueryFactory.php index 39bef05..c7b7429 100644 --- a/core/modules/workspace/src/EntityQuery/SqlQueryFactory.php +++ b/core/modules/workspace/src/EntityQuery/PgsqlQueryFactory.php @@ -5,13 +5,13 @@ use Drupal\Core\Database\Connection; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\Query\QueryBase; -use Drupal\Core\Entity\Query\Sql\QueryFactory as BaseQueryFactory; +use Drupal\Core\Entity\Query\Sql\pgsql\QueryFactory as BaseQueryFactory; use Drupal\workspace\WorkspaceManagerInterface; /** - * Workspace specific entity query implementation. + * Workspace PostgreSQL specific entity query implementation. */ -class SqlQueryFactory extends BaseQueryFactory { +class PgsqlQueryFactory extends BaseQueryFactory { /** * The workspace manager. diff --git a/core/modules/workspace/src/EntityQuery/SqlQueryFactory.php b/core/modules/workspace/src/EntityQuery/QueryFactory.php similarity index 97% rename from core/modules/workspace/src/EntityQuery/SqlQueryFactory.php rename to core/modules/workspace/src/EntityQuery/QueryFactory.php index 39bef05..9feae9b 100644 --- a/core/modules/workspace/src/EntityQuery/SqlQueryFactory.php +++ b/core/modules/workspace/src/EntityQuery/QueryFactory.php @@ -11,7 +11,7 @@ /** * Workspace specific entity query implementation. */ -class SqlQueryFactory extends BaseQueryFactory { +class QueryFactory extends BaseQueryFactory { /** * The workspace manager. diff --git a/core/modules/workspace/src/Plugin/RepositoryHandler/LiveRepositoryHandler.php b/core/modules/workspace/src/Plugin/RepositoryHandler/LiveRepositoryHandler.php index 900d26e..daaee46 100644 --- a/core/modules/workspace/src/Plugin/RepositoryHandler/LiveRepositoryHandler.php +++ b/core/modules/workspace/src/Plugin/RepositoryHandler/LiveRepositoryHandler.php @@ -2,6 +2,7 @@ namespace Drupal\workspace\Plugin\RepositoryHandler; +use Drupal\Core\Database\Connection; use Drupal\workspace\RepositoryHandlerBase; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; @@ -42,6 +43,13 @@ class LiveRepositoryHandler extends RepositoryHandlerBase implements RepositoryH protected $entityTypeManager; /** + * The database connection. + * + * @var \Drupal\Core\Database\Connection + */ + protected $database; + + /** * The workspace association storage. * * @var \Drupal\workspace\WorkspaceAssociationStorageInterface @@ -59,11 +67,14 @@ class LiveRepositoryHandler extends RepositoryHandlerBase implements RepositoryH * The plugin implementation definition. * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager * The entity type manager. + * @param \Drupal\Core\Database\Connection $database + * Database connection. */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager) { + public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, Connection $database) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->entityTypeManager = $entity_type_manager; + $this->database = $database; $this->workspaceAssociationStorage = $entity_type_manager->getStorage('workspace_association'); $this->sourceWorkspace = $this->entityTypeManager->getStorage('workspace')->load($this->source); $this->targetWorkspace = $this->entityTypeManager->getStorage('workspace')->load($this->target); @@ -77,7 +88,8 @@ public static function create(ContainerInterface $container, array $configuratio $configuration, $plugin_id, $plugin_definition, - $container->get('entity_type.manager') + $container->get('entity_type.manager'), + $container->get('database') ); } @@ -99,17 +111,26 @@ public function push() { throw new WorkspaceConflictException(); } - foreach ($this->getSourceRevisionDifference() as $entity_type_id => $revision_difference) { - $entity_revisions = $this->entityTypeManager->getStorage($entity_type_id)->loadMultipleRevisions(array_keys($revision_difference)); - /** @var \Drupal\Core\Entity\ContentEntityInterface|\Drupal\Core\Entity\RevisionableInterface $entity */ - foreach ($entity_revisions as $entity) { - // When pushing workspace-specific revisions to the default workspace - // (Live), we simply need to mark them as default revisions. - $entity->_isReplicating = TRUE; - $entity->isDefaultRevision(TRUE); - $entity->save(); + $transaction = $this->database->startTransaction(); + try { + foreach ($this->getSourceRevisionDifference() as $entity_type_id => $revision_difference) { + $entity_revisions = $this->entityTypeManager->getStorage($entity_type_id) + ->loadMultipleRevisions(array_keys($revision_difference)); + /** @var \Drupal\Core\Entity\ContentEntityInterface|\Drupal\Core\Entity\RevisionableInterface $entity */ + foreach ($entity_revisions as $entity) { + // When pushing workspace-specific revisions to the default workspace + // (Live), we simply need to mark them as default revisions. + $entity->_isReplicating = TRUE; + $entity->isDefaultRevision(TRUE); + $entity->save(); + } } } + catch (\Exception $e) { + $transaction->rollBack(); + watchdog_exception('workspace', $e); + throw $e; + } // Notify the workspace association storage that a workspace has been // pushed. diff --git a/core/modules/workspace/src/WorkspaceAssociationStorage.php b/core/modules/workspace/src/WorkspaceAssociationStorage.php index ff68859..0f6f8d9 100644 --- a/core/modules/workspace/src/WorkspaceAssociationStorage.php +++ b/core/modules/workspace/src/WorkspaceAssociationStorage.php @@ -32,6 +32,7 @@ public function getTrackedEntities($workspace_id, $all_revisions = FALSE, $group $query = $this->database->select($table, 'base_table'); $query ->fields('base_table', ['content_entity_type_id', 'content_entity_id', 'content_entity_revision_id']) + ->orderBy('content_entity_revision_id', 'ASC') ->condition('workspace', $workspace_id); $tracked_revisions = []; diff --git a/core/modules/workspace/src/WorkspaceManager.php b/core/modules/workspace/src/WorkspaceManager.php index 2d72e73..fca0f95 100644 --- a/core/modules/workspace/src/WorkspaceManager.php +++ b/core/modules/workspace/src/WorkspaceManager.php @@ -67,20 +67,6 @@ class WorkspaceManager implements WorkspaceManagerInterface { protected $state; /** - * A list of workspace negotiators. - * - * @var \Drupal\workspace\Negotiator\WorkspaceNegotiatorInterface[] - */ - protected $negotiators = []; - - /** - * A list of workspace negotiators sorted by their priority. - * - * @var \Drupal\workspace\Negotiator\WorkspaceNegotiatorInterface[] - */ - protected $sortedNegotiators; - - /** * A logger instance. * * @var \Psr\Log\LoggerInterface @@ -102,6 +88,13 @@ class WorkspaceManager implements WorkspaceManagerInterface { protected $negotiatorIds; /** + * The current active workspace. + * + * @var \Drupal\workspace\WorkspaceInterface + */ + protected $activeWorkspace; + + /** * Constructs a new WorkspaceManager. * * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack @@ -133,12 +126,12 @@ public function __construct(RequestStack $request_stack, EntityTypeManagerInterf * {@inheritdoc} */ public function entityTypeCanBelongToWorkspaces(EntityTypeInterface $entity_type) { - if (!in_array($entity_type->id(), $this->blacklist, TRUE) + if (!isset($this->blacklist[$entity_type->id()]) && $entity_type->entityClassImplements(EntityPublishedInterface::class) && $entity_type->isRevisionable()) { return TRUE; } - $this->blacklist[] = $entity_type->id(); + $this->blacklist[$entity_type->id()] = $entity_type->id(); return FALSE; } @@ -159,18 +152,20 @@ public function getSupportedEntityTypes() { * {@inheritdoc} */ public function getActiveWorkspace() { - $request = $this->requestStack->getCurrentRequest(); - foreach ($this->negotiatorIds as $negotiator_id) { - $negotiator = $this->classResolver->getInstanceFromDefinition($negotiator_id); - if ($negotiator->applies($request)) { - if ($active_workspace = $negotiator->getActiveWorkspace($request)) { - break; + if (!isset($this->activeWorkspace)) { + $request = $this->requestStack->getCurrentRequest(); + foreach ($this->negotiatorIds as $negotiator_id) { + $negotiator = $this->classResolver->getInstanceFromDefinition($negotiator_id); + if ($negotiator->applies($request)) { + if ($this->activeWorkspace = $negotiator->getActiveWorkspace($request)) { + break; + } } } } // The default workspace negotiator always returns a valid workspace. - return $active_workspace; + return $this->activeWorkspace; } /** @@ -184,6 +179,8 @@ public function setActiveWorkspace(WorkspaceInterface $workspace) { throw new WorkspaceAccessException('The user does not have permission to view that workspace.'); } + $this->activeWorkspace = $workspace; + // Set the workspace on the proper negotiator. $request = $this->requestStack->getCurrentRequest(); foreach ($this->negotiatorIds as $negotiator_id) { diff --git a/core/modules/workspace/src/WorkspaceServiceProvider.php b/core/modules/workspace/src/WorkspaceServiceProvider.php index d30f520..5836493 100644 --- a/core/modules/workspace/src/WorkspaceServiceProvider.php +++ b/core/modules/workspace/src/WorkspaceServiceProvider.php @@ -18,13 +18,6 @@ public function alter(ContainerBuilder $container) { $renderer_config = $container->getParameter('renderer.config'); $renderer_config['required_cache_contexts'][] = 'workspace'; $container->setParameter('renderer.config', $renderer_config); - - // Switch core's SQL entity query factory to our own so we can reliably - // alter entity queries. - // @todo Do the same for the pgsql entity query backend override. - $definition = $container->getDefinition('entity.query.sql'); - $definition->setClass('Drupal\workspace\EntityQuery\SqlQueryFactory'); - $definition->addArgument($container->getDefinition('workspace.manager')); } } diff --git a/core/modules/workspace/tests/src/Functional/EntityResource/WorkspaceResourceTestBase.php b/core/modules/workspace/tests/src/Functional/EntityResource/WorkspaceResourceTestBase.php index 61d2755..c0a4e83 100644 --- a/core/modules/workspace/tests/src/Functional/EntityResource/WorkspaceResourceTestBase.php +++ b/core/modules/workspace/tests/src/Functional/EntityResource/WorkspaceResourceTestBase.php @@ -140,16 +140,10 @@ protected function getExpectedNormalizedEntity() { * {@inheritdoc} */ protected function getNormalizedPostEntity() { - // Work around the fact that core's REST test suite assumes that all content - // entity types have serial IDs. - // @todo Removed this when - // https://www.drupal.org/project/drupal/issues/2935076 gets fixed. - static $calls = 0; - - $normalized_post_entity = [ + return [ 'id' => [ [ - 'value' => $calls !== 1 ? static::$firstCreatedEntityId : static::$secondCreatedEntityId, + 'value' => static::$firstCreatedEntityId, ], ], 'label' => [ @@ -163,9 +157,6 @@ protected function getNormalizedPostEntity() { ], ], ]; - - $calls++; - return $normalized_post_entity; } /** diff --git a/core/modules/workspace/tests/src/Kernel/WorkspaceIntegrationTest.php b/core/modules/workspace/tests/src/Kernel/WorkspaceIntegrationTest.php index b1a4d76..f0d9d64 100644 --- a/core/modules/workspace/tests/src/Kernel/WorkspaceIntegrationTest.php +++ b/core/modules/workspace/tests/src/Kernel/WorkspaceIntegrationTest.php @@ -604,7 +604,7 @@ protected function assertEntityQuery(array $expected_values, $entity_type_id) { $query ->condition($entity_keys['id'], $expected_value[$id_key]) ->condition($entity_keys['label'], $expected_value[$label_key]) - ->condition($entity_keys['published'], $expected_value[$published_key]); + ->condition($entity_keys['published'], (int) $expected_value[$published_key]); // If the entity is not expected to be the default revision, we need to // query all revisions if we want to find it. diff --git a/core/modules/workspace/workspace.services.yml b/core/modules/workspace/workspace.services.yml index c036412..13d67ae 100644 --- a/core/modules/workspace/workspace.services.yml +++ b/core/modules/workspace/workspace.services.yml @@ -25,3 +25,17 @@ services: logger.channel.workspace: parent: logger.channel_base arguments: ['workspace'] + workspace.entity.query.sql: + decorates: 'entity.query.sql' + class: Drupal\workspace\EntityQuery\QueryFactory + arguments: ['@database', '@workspace.manager'] + public: false + decoration_priority: 50 + tags: + - { name: backend_overridable } + pgsql.workspace.entity.query.sql: + decorates: 'pgsql.entity.query.sql' + class: Drupal\workspace\EntityQuery\PgsqlQueryFactory + arguments: ['@database', '@workspace.manager'] + public: false + decoration_priority: 50