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 17bd9ce08f.
merge-requests/55/head
parent
a68ff42323
commit
ade7b950a1
|
|
@ -1454,25 +1454,3 @@ function system_element_info_alter(&$type) {
|
||||||
$type['page']['#theme_wrappers']['off_canvas_page_wrapper'] = ['#weight' => -1000];
|
$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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -34,13 +34,6 @@ class EntityOperations implements ContainerInjectionInterface {
|
||||||
*/
|
*/
|
||||||
protected $workspaceManager;
|
protected $workspaceManager;
|
||||||
|
|
||||||
/**
|
|
||||||
* The workspace association service.
|
|
||||||
*
|
|
||||||
* @var \Drupal\workspaces\WorkspaceAssociationInterface
|
|
||||||
*/
|
|
||||||
protected $workspaceAssociation;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new EntityOperations instance.
|
* Constructs a new EntityOperations instance.
|
||||||
*
|
*
|
||||||
|
|
@ -48,13 +41,10 @@ class EntityOperations implements ContainerInjectionInterface {
|
||||||
* The entity type manager service.
|
* The entity type manager service.
|
||||||
* @param \Drupal\workspaces\WorkspaceManagerInterface $workspace_manager
|
* @param \Drupal\workspaces\WorkspaceManagerInterface $workspace_manager
|
||||||
* The workspace manager service.
|
* 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->entityTypeManager = $entity_type_manager;
|
||||||
$this->workspaceManager = $workspace_manager;
|
$this->workspaceManager = $workspace_manager;
|
||||||
$this->workspaceAssociation = $workspace_association;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -63,8 +53,7 @@ class EntityOperations implements ContainerInjectionInterface {
|
||||||
public static function create(ContainerInterface $container) {
|
public static function create(ContainerInterface $container) {
|
||||||
return new static(
|
return new static(
|
||||||
$container->get('entity_type.manager'),
|
$container->get('entity_type.manager'),
|
||||||
$container->get('workspaces.manager'),
|
$container->get('workspaces.manager')
|
||||||
$container->get('workspaces.association')
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -85,13 +74,31 @@ class EntityOperations implements ContainerInjectionInterface {
|
||||||
// Get a list of revision IDs for entities that have a revision set for the
|
// 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
|
// current active workspace. If an entity has multiple revisions set for a
|
||||||
// workspace, only the one with the highest ID is returned.
|
// 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 */
|
/** @var \Drupal\Core\Entity\RevisionableStorageInterface $storage */
|
||||||
$storage = $this->entityTypeManager->getStorage($entity_type_id);
|
$storage = $this->entityTypeManager->getStorage($entity_type_id);
|
||||||
|
|
||||||
// Swap out every entity which has a revision set for the current active
|
// Swap out every entity which has a revision set for the current active
|
||||||
// workspace.
|
// 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;
|
$entities[$revision->id()] = $revision;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -135,10 +142,6 @@ class EntityOperations implements ContainerInjectionInterface {
|
||||||
// become the default revision only when it is replicated to the default
|
// become the default revision only when it is replicated to the default
|
||||||
// workspace.
|
// workspace.
|
||||||
$entity->isDefaultRevision(FALSE);
|
$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
|
// When a new published entity is inserted in a non-default workspace, we
|
||||||
|
|
@ -171,7 +174,7 @@ class EntityOperations implements ContainerInjectionInterface {
|
||||||
return;
|
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
|
// 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
|
// that workspace, but not yet published on the live workspace. It is first
|
||||||
|
|
@ -208,7 +211,7 @@ class EntityOperations implements ContainerInjectionInterface {
|
||||||
// Only track new revisions.
|
// Only track new revisions.
|
||||||
/** @var \Drupal\Core\Entity\RevisionableInterface $entity */
|
/** @var \Drupal\Core\Entity\RevisionableInterface $entity */
|
||||||
if ($entity->getLoadedRevisionId() != $entity->getRevisionId()) {
|
if ($entity->getLoadedRevisionId() != $entity->getRevisionId()) {
|
||||||
$this->workspaceAssociation->trackEntity($entity, $this->workspaceManager->getActiveWorkspace());
|
$this->trackEntity($entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -237,6 +240,51 @@ class EntityOperations implements ContainerInjectionInterface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
* Alters entity forms to disallow concurrent editing in multiple workspaces.
|
||||||
*
|
*
|
||||||
|
|
@ -250,7 +298,7 @@ class EntityOperations implements ContainerInjectionInterface {
|
||||||
* @see hook_form_alter()
|
* @see hook_form_alter()
|
||||||
*/
|
*/
|
||||||
public function entityFormAlter(array &$form, FormStateInterface $form_state, $form_id) {
|
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();
|
$entity = $form_state->getFormObject()->getEntity();
|
||||||
if (!$this->workspaceManager->isEntityTypeSupported($entity->getEntityType())) {
|
if (!$this->workspaceManager->isEntityTypeSupported($entity->getEntityType())) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -270,7 +318,9 @@ class EntityOperations implements ContainerInjectionInterface {
|
||||||
$form['#entity_builders'][] = [get_called_class(), 'entityFormEntityBuild'];
|
$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.
|
// An entity can only be edited in one workspace.
|
||||||
$workspace_id = reset($workspace_ids);
|
$workspace_id = reset($workspace_ids);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,7 @@
|
||||||
namespace Drupal\workspaces;
|
namespace Drupal\workspaces;
|
||||||
|
|
||||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||||
use Drupal\Core\Entity\EntityTypeInterface;
|
|
||||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||||
use Drupal\Core\Field\BaseFieldDefinition;
|
|
||||||
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
|
||||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -69,10 +66,6 @@ class EntityTypeInfo implements ContainerInjectionInterface {
|
||||||
foreach ($entity_types as $entity_type) {
|
foreach ($entity_types as $entity_type) {
|
||||||
if ($this->workspaceManager->isEntityTypeSupported($entity_type)) {
|
if ($this->workspaceManager->isEntityTypeSupported($entity_type)) {
|
||||||
$entity_type->addConstraint('EntityWorkspaceConflict');
|
$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 @@ class EntityTypeInfo implements ContainerInjectionInterface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -2,13 +2,8 @@
|
||||||
|
|
||||||
namespace Drupal\workspaces\Form;
|
namespace Drupal\workspaces\Form;
|
||||||
|
|
||||||
use Drupal\Component\Datetime\TimeInterface;
|
|
||||||
use Drupal\Core\Entity\ContentEntityDeleteForm;
|
use Drupal\Core\Entity\ContentEntityDeleteForm;
|
||||||
use Drupal\Core\Entity\EntityRepositoryInterface;
|
|
||||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
|
||||||
use Drupal\Core\Form\FormStateInterface;
|
use Drupal\Core\Form\FormStateInterface;
|
||||||
use Drupal\workspaces\WorkspaceAssociationInterface;
|
|
||||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a form for deleting a workspace.
|
* Provides a form for deleting a workspace.
|
||||||
|
|
@ -24,52 +19,14 @@ class WorkspaceDeleteForm extends ContentEntityDeleteForm implements WorkspaceFo
|
||||||
*/
|
*/
|
||||||
protected $entity;
|
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}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||||
$form = parent::buildForm($form, $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 = [];
|
$items = [];
|
||||||
foreach (array_keys($tracked_entities) as $entity_type_id => $entity_ids) {
|
foreach ($source_rev_diff as $entity_type_id => $revision_ids) {
|
||||||
$revision_ids = $this->workspaceAssociation->getAssociatedRevisions($this->entity->id(), $entity_type_id, $entity_ids);
|
|
||||||
$label = $this->entityTypeManager->getDefinition($entity_type_id)->getLabel();
|
$label = $this->entityTypeManager->getDefinition($entity_type_id)->getLabel();
|
||||||
$items[] = $this->formatPlural(count($revision_ids), '1 @label revision.', '@count @label revisions.', ['@label' => $label]);
|
$items[] = $this->formatPlural(count($revision_ids), '1 @label revision.', '@count @label revisions.', ['@label' => $label]);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
namespace Drupal\workspaces\Plugin\Validation\Constraint;
|
namespace Drupal\workspaces\Plugin\Validation\Constraint;
|
||||||
|
|
||||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||||
use Drupal\workspaces\WorkspaceAssociationInterface;
|
use Drupal\workspaces\WorkspaceAssociationStorageInterface;
|
||||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||||
use Symfony\Component\Validator\Constraint;
|
use Symfony\Component\Validator\Constraint;
|
||||||
use Symfony\Component\Validator\ConstraintValidator;
|
use Symfony\Component\Validator\ConstraintValidator;
|
||||||
|
|
@ -14,20 +14,20 @@ use Symfony\Component\Validator\ConstraintValidator;
|
||||||
class DeletedWorkspaceConstraintValidator extends ConstraintValidator implements ContainerInjectionInterface {
|
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.
|
* Creates a new DeletedWorkspaceConstraintValidator instance.
|
||||||
*
|
*
|
||||||
* @param \Drupal\workspaces\WorkspaceAssociationInterface $workspace_association
|
* @param \Drupal\workspaces\WorkspaceAssociationStorageInterface $workspace_association_storage
|
||||||
* The workspace association service.
|
* The workspace association storage.
|
||||||
*/
|
*/
|
||||||
public function __construct(WorkspaceAssociationInterface $workspace_association) {
|
public function __construct(WorkspaceAssociationStorageInterface $workspace_association_storage) {
|
||||||
$this->workspaceAssociation = $workspace_association;
|
$this->workspaceAssociationStorage = $workspace_association_storage;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -35,7 +35,7 @@ class DeletedWorkspaceConstraintValidator extends ConstraintValidator implements
|
||||||
*/
|
*/
|
||||||
public static function create(ContainerInterface $container) {
|
public static function create(ContainerInterface $container) {
|
||||||
return new static(
|
return new static(
|
||||||
$container->get('workspaces.association')
|
$container->get('entity_type.manager')->getStorage('workspace_association')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,7 +49,14 @@ class DeletedWorkspaceConstraintValidator extends ConstraintValidator implements
|
||||||
return;
|
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);
|
$this->context->addViolation($constraint->message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ namespace Drupal\workspaces\Plugin\Validation\Constraint;
|
||||||
|
|
||||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||||
use Drupal\workspaces\WorkspaceAssociationInterface;
|
|
||||||
use Drupal\workspaces\WorkspaceManagerInterface;
|
use Drupal\workspaces\WorkspaceManagerInterface;
|
||||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||||
use Symfony\Component\Validator\Constraint;
|
use Symfony\Component\Validator\Constraint;
|
||||||
|
|
@ -29,13 +28,6 @@ class EntityWorkspaceConflictConstraintValidator extends ConstraintValidator imp
|
||||||
*/
|
*/
|
||||||
protected $workspaceManager;
|
protected $workspaceManager;
|
||||||
|
|
||||||
/**
|
|
||||||
* The workspace association service.
|
|
||||||
*
|
|
||||||
* @var \Drupal\workspaces\WorkspaceAssociationInterface
|
|
||||||
*/
|
|
||||||
protected $workspaceAssociation;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an EntityUntranslatableFieldsConstraintValidator object.
|
* Constructs an EntityUntranslatableFieldsConstraintValidator object.
|
||||||
*
|
*
|
||||||
|
|
@ -43,13 +35,10 @@ class EntityWorkspaceConflictConstraintValidator extends ConstraintValidator imp
|
||||||
* The entity type manager service.
|
* The entity type manager service.
|
||||||
* @param \Drupal\workspaces\WorkspaceManagerInterface $workspace_manager
|
* @param \Drupal\workspaces\WorkspaceManagerInterface $workspace_manager
|
||||||
* The workspace manager service.
|
* 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->entityTypeManager = $entity_type_manager;
|
||||||
$this->workspaceManager = $workspace_manager;
|
$this->workspaceManager = $workspace_manager;
|
||||||
$this->workspaceAssociation = $workspace_association;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -58,8 +47,7 @@ class EntityWorkspaceConflictConstraintValidator extends ConstraintValidator imp
|
||||||
public static function create(ContainerInterface $container) {
|
public static function create(ContainerInterface $container) {
|
||||||
return new static(
|
return new static(
|
||||||
$container->get('entity_type.manager'),
|
$container->get('entity_type.manager'),
|
||||||
$container->get('workspaces.manager'),
|
$container->get('workspaces.manager')
|
||||||
$container->get('workspaces.association')
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -69,7 +57,9 @@ class EntityWorkspaceConflictConstraintValidator extends ConstraintValidator imp
|
||||||
public function validate($entity, Constraint $constraint) {
|
public function validate($entity, Constraint $constraint) {
|
||||||
/** @var \Drupal\Core\Entity\EntityInterface $entity */
|
/** @var \Drupal\Core\Entity\EntityInterface $entity */
|
||||||
if (isset($entity) && !$entity->isNew()) {
|
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();
|
$active_workspace = $this->workspaceManager->getActiveWorkspace();
|
||||||
|
|
||||||
if ($workspace_ids && (!$active_workspace || !in_array($active_workspace->id(), $workspace_ids, TRUE))) {
|
if ($workspace_ids && (!$active_workspace || !in_array($active_workspace->id(), $workspace_ids, TRUE))) {
|
||||||
|
|
|
||||||
|
|
@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -83,13 +83,6 @@ class WorkspaceManager implements WorkspaceManagerInterface {
|
||||||
*/
|
*/
|
||||||
protected $classResolver;
|
protected $classResolver;
|
||||||
|
|
||||||
/**
|
|
||||||
* The workspace association service.
|
|
||||||
*
|
|
||||||
* @var \Drupal\workspaces\WorkspaceAssociationInterface
|
|
||||||
*/
|
|
||||||
protected $workspaceAssociation;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The workspace negotiator service IDs.
|
* The workspace negotiator service IDs.
|
||||||
*
|
*
|
||||||
|
|
@ -121,12 +114,10 @@ class WorkspaceManager implements WorkspaceManagerInterface {
|
||||||
* A logger instance.
|
* A logger instance.
|
||||||
* @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver
|
* @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver
|
||||||
* The class resolver.
|
* The class resolver.
|
||||||
* @param \Drupal\workspaces\WorkspaceAssociationInterface $workspace_association
|
|
||||||
* The workspace association service.
|
|
||||||
* @param array $negotiator_ids
|
* @param array $negotiator_ids
|
||||||
* The workspace negotiator service 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->requestStack = $request_stack;
|
||||||
$this->entityTypeManager = $entity_type_manager;
|
$this->entityTypeManager = $entity_type_manager;
|
||||||
$this->entityMemoryCache = $entity_memory_cache;
|
$this->entityMemoryCache = $entity_memory_cache;
|
||||||
|
|
@ -134,7 +125,6 @@ class WorkspaceManager implements WorkspaceManagerInterface {
|
||||||
$this->state = $state;
|
$this->state = $state;
|
||||||
$this->logger = $logger;
|
$this->logger = $logger;
|
||||||
$this->classResolver = $class_resolver;
|
$this->classResolver = $class_resolver;
|
||||||
$this->workspaceAssociation = $workspace_association;
|
|
||||||
$this->negotiatorIds = $negotiator_ids;
|
$this->negotiatorIds = $negotiator_ids;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -315,35 +305,67 @@ class WorkspaceManager implements WorkspaceManagerInterface {
|
||||||
|
|
||||||
$batch_size = Settings::get('entity_update_batch_size', 50);
|
$batch_size = Settings::get('entity_update_batch_size', 50);
|
||||||
|
|
||||||
// Get the first deleted workspace from the list and delete the revisions
|
/** @var \Drupal\workspaces\WorkspaceAssociationStorageInterface $workspace_association_storage */
|
||||||
// associated with it, along with the workspace association records.
|
$workspace_association_storage = $this->entityTypeManager->getStorage('workspace_association');
|
||||||
$workspace_id = reset($deleted_workspace_ids);
|
|
||||||
$tracked_entities = $this->workspaceAssociation->getTrackedEntities($workspace_id);
|
|
||||||
|
|
||||||
$count = 1;
|
// Get the first deleted workspace from the list and delete the revisions
|
||||||
foreach ($tracked_entities as $entity_type_id => $entities) {
|
// associated with it, along with the workspace_association entries.
|
||||||
$associated_entity_storage = $this->entityTypeManager->getStorage($entity_type_id);
|
$workspace_id = reset($deleted_workspace_ids);
|
||||||
$associated_revisions = $this->workspaceAssociation->getAssociatedRevisions($workspace_id, $entity_type_id);
|
$workspace_association_ids = $this->getWorkspaceAssociationRevisionsToPurge($workspace_id, $batch_size);
|
||||||
foreach (array_keys($associated_revisions) as $revision_id) {
|
|
||||||
if ($count > $batch_size) {
|
if ($workspace_association_ids) {
|
||||||
continue 2;
|
$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.
|
||||||
|
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 associated entity revision.
|
// Delete the workspace_association revision.
|
||||||
$associated_entity_storage->deleteRevision($revision_id);
|
if ($workspace_association->isDefaultRevision()) {
|
||||||
$count++;
|
$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
|
// 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
|
// request a fresh list of workspace association IDs. If it is empty, we can
|
||||||
// and remove the deleted workspace ID entry from state.
|
// go ahead and remove the deleted workspace ID entry from state.
|
||||||
if (!$this->workspaceAssociation->getTrackedEntities($workspace_id)) {
|
if (!$this->getWorkspaceAssociationRevisionsToPurge($workspace_id, $batch_size)) {
|
||||||
unset($deleted_workspace_ids[$workspace_id]);
|
unset($deleted_workspace_ids[$workspace_id]);
|
||||||
$this->state->set('workspace.deleted', $deleted_workspace_ids);
|
$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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,13 +36,6 @@ class WorkspaceOperationFactory {
|
||||||
*/
|
*/
|
||||||
protected $workspaceManager;
|
protected $workspaceManager;
|
||||||
|
|
||||||
/**
|
|
||||||
* The workspace association service.
|
|
||||||
*
|
|
||||||
* @var \Drupal\workspaces\WorkspaceAssociationInterface
|
|
||||||
*/
|
|
||||||
protected $workspaceAssociation;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new WorkspacePublisher.
|
* Constructs a new WorkspacePublisher.
|
||||||
*
|
*
|
||||||
|
|
@ -50,16 +43,11 @@ class WorkspaceOperationFactory {
|
||||||
* The entity type manager.
|
* The entity type manager.
|
||||||
* @param \Drupal\Core\Database\Connection $database
|
* @param \Drupal\Core\Database\Connection $database
|
||||||
* Database connection.
|
* 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->entityTypeManager = $entity_type_manager;
|
||||||
$this->database = $database;
|
$this->database = $database;
|
||||||
$this->workspaceManager = $workspace_manager;
|
$this->workspaceManager = $workspace_manager;
|
||||||
$this->workspaceAssociation = $workspace_association;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -72,7 +60,7 @@ class WorkspaceOperationFactory {
|
||||||
* A workspace publisher object.
|
* A workspace publisher object.
|
||||||
*/
|
*/
|
||||||
public function getPublisher(WorkspaceInterface $source) {
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,13 @@ class WorkspacePublisher implements WorkspacePublisherInterface {
|
||||||
*/
|
*/
|
||||||
protected $database;
|
protected $database;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The workspace association storage.
|
||||||
|
*
|
||||||
|
* @var \Drupal\workspaces\WorkspaceAssociationStorageInterface
|
||||||
|
*/
|
||||||
|
protected $workspaceAssociationStorage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The workspace manager.
|
* The workspace manager.
|
||||||
*
|
*
|
||||||
|
|
@ -43,13 +50,6 @@ class WorkspacePublisher implements WorkspacePublisherInterface {
|
||||||
*/
|
*/
|
||||||
protected $workspaceManager;
|
protected $workspaceManager;
|
||||||
|
|
||||||
/**
|
|
||||||
* The workspace association service.
|
|
||||||
*
|
|
||||||
* @var \Drupal\workspaces\WorkspaceAssociationInterface
|
|
||||||
*/
|
|
||||||
protected $workspaceAssociation;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new WorkspacePublisher.
|
* Constructs a new WorkspacePublisher.
|
||||||
*
|
*
|
||||||
|
|
@ -59,14 +59,12 @@ class WorkspacePublisher implements WorkspacePublisherInterface {
|
||||||
* Database connection.
|
* Database connection.
|
||||||
* @param \Drupal\workspaces\WorkspaceManagerInterface $workspace_manager
|
* @param \Drupal\workspaces\WorkspaceManagerInterface $workspace_manager
|
||||||
* The 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->entityTypeManager = $entity_type_manager;
|
||||||
$this->database = $database;
|
$this->database = $database;
|
||||||
|
$this->workspaceAssociationStorage = $entity_type_manager->getStorage('workspace_association');
|
||||||
$this->workspaceManager = $workspace_manager;
|
$this->workspaceManager = $workspace_manager;
|
||||||
$this->workspaceAssociation = $workspace_association;
|
|
||||||
$this->sourceWorkspace = $source;
|
$this->sourceWorkspace = $source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,11 +95,6 @@ class WorkspacePublisher implements WorkspacePublisherInterface {
|
||||||
// revisions.
|
// revisions.
|
||||||
$entity->setSyncing(TRUE);
|
$entity->setSyncing(TRUE);
|
||||||
$entity->isDefaultRevision(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->original = $default_revisions[$entity->id()];
|
||||||
$entity->save();
|
$entity->save();
|
||||||
}
|
}
|
||||||
|
|
@ -114,8 +107,9 @@ class WorkspacePublisher implements WorkspacePublisherInterface {
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify the workspace association that a workspace has been published.
|
// Notify the workspace association storage that a workspace has been
|
||||||
$this->workspaceAssociation->postPublish($this->sourceWorkspace);
|
// pushed.
|
||||||
|
$this->workspaceAssociationStorage->postPush($this->sourceWorkspace);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -147,7 +141,7 @@ class WorkspacePublisher implements WorkspacePublisherInterface {
|
||||||
public function getDifferringRevisionIdsOnTarget() {
|
public function getDifferringRevisionIdsOnTarget() {
|
||||||
$target_revision_difference = [];
|
$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) {
|
foreach ($tracked_entities as $entity_type_id => $tracked_revisions) {
|
||||||
$entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
|
$entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
|
||||||
|
|
||||||
|
|
@ -177,7 +171,7 @@ class WorkspacePublisher implements WorkspacePublisherInterface {
|
||||||
*/
|
*/
|
||||||
public function getDifferringRevisionIdsOnSource() {
|
public function getDifferringRevisionIdsOnSource() {
|
||||||
// Get the Workspace association revisions which haven't been pushed yet.
|
// 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());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -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'));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -36,12 +36,6 @@ class WorkspacesUninstallTest extends BrowserTestBase {
|
||||||
$this->drupalPostForm(NULL, [], 'Uninstall');
|
$this->drupalPostForm(NULL, [], 'Uninstall');
|
||||||
$session->pageTextContains('The selected modules have been uninstalled.');
|
$session->pageTextContains('The selected modules have been uninstalled.');
|
||||||
$session->pageTextNotContains('Workspaces');
|
$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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ class WorkspaceAccessTest extends KernelTestBase {
|
||||||
$this->installSchema('system', ['sequences']);
|
$this->installSchema('system', ['sequences']);
|
||||||
|
|
||||||
$this->installEntitySchema('workspace');
|
$this->installEntitySchema('workspace');
|
||||||
|
$this->installEntitySchema('workspace_association');
|
||||||
$this->installEntitySchema('user');
|
$this->installEntitySchema('user');
|
||||||
|
|
||||||
// User 1.
|
// User 1.
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
|
||||||
use Drupal\Tests\node\Traits\NodeCreationTrait;
|
use Drupal\Tests\node\Traits\NodeCreationTrait;
|
||||||
use Drupal\Tests\user\Traits\UserCreationTrait;
|
use Drupal\Tests\user\Traits\UserCreationTrait;
|
||||||
use Drupal\workspaces\Entity\Workspace;
|
use Drupal\workspaces\Entity\Workspace;
|
||||||
|
use Drupal\workspaces\Entity\WorkspaceAssociation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests CRUD operations for workspaces.
|
* Tests CRUD operations for workspaces.
|
||||||
|
|
@ -18,7 +19,6 @@ class WorkspaceCRUDTest extends KernelTestBase {
|
||||||
use UserCreationTrait;
|
use UserCreationTrait;
|
||||||
use NodeCreationTrait;
|
use NodeCreationTrait;
|
||||||
use ContentTypeCreationTrait;
|
use ContentTypeCreationTrait;
|
||||||
use WorkspaceTestTrait;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The entity type manager.
|
* The entity type manager.
|
||||||
|
|
@ -66,7 +66,7 @@ class WorkspaceCRUDTest extends KernelTestBase {
|
||||||
$this->installSchema('node', ['node_access']);
|
$this->installSchema('node', ['node_access']);
|
||||||
|
|
||||||
$this->installEntitySchema('workspace');
|
$this->installEntitySchema('workspace');
|
||||||
$this->installSchema('workspaces', ['workspace_association']);
|
$this->installEntitySchema('workspace_association');
|
||||||
$this->installEntitySchema('node');
|
$this->installEntitySchema('node');
|
||||||
|
|
||||||
$this->installConfig(['filter', 'node', 'system']);
|
$this->installConfig(['filter', 'node', 'system']);
|
||||||
|
|
@ -91,8 +91,10 @@ class WorkspaceCRUDTest extends KernelTestBase {
|
||||||
]);
|
]);
|
||||||
$this->setCurrentUser($admin);
|
$this->setCurrentUser($admin);
|
||||||
|
|
||||||
/** @var \Drupal\workspaces\WorkspaceAssociationInterface $workspace_association */
|
/** @var \Drupal\workspaces\WorkspaceAssociationStorageInterface $workspace_association_storage */
|
||||||
$workspace_association = \Drupal::service('workspaces.association');
|
$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.
|
// Create a workspace with a very small number of associated node revisions.
|
||||||
$workspace_1 = Workspace::create([
|
$workspace_1 = Workspace::create([
|
||||||
|
|
@ -104,12 +106,6 @@ class WorkspaceCRUDTest extends KernelTestBase {
|
||||||
|
|
||||||
$workspace_1_node_1 = $this->createNode(['status' => FALSE]);
|
$workspace_1_node_1 = $this->createNode(['status' => FALSE]);
|
||||||
$workspace_1_node_2 = $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++) {
|
for ($i = 0; $i < 4; $i++) {
|
||||||
$workspace_1_node_1->setNewRevision(TRUE);
|
$workspace_1_node_1->setNewRevision(TRUE);
|
||||||
$workspace_1_node_1->save();
|
$workspace_1_node_1->save();
|
||||||
|
|
@ -118,17 +114,9 @@ class WorkspaceCRUDTest extends KernelTestBase {
|
||||||
$workspace_1_node_2->save();
|
$workspace_1_node_2->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
// The workspace should now track 2 nodes.
|
// The workspace should have 10 associated node revisions, 5 for each node.
|
||||||
$tracked_entities = $workspace_association->getTrackedEntities($workspace_1->id());
|
$associated_revisions = $workspace_association_storage->getTrackedEntities($workspace_1->id(), TRUE);
|
||||||
$this->assertCount(2, $tracked_entities['node']);
|
$this->assertCount(10, $associated_revisions['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);
|
|
||||||
|
|
||||||
// Check that we are allowed to delete the workspace.
|
// Check that we are allowed to delete the workspace.
|
||||||
$this->assertTrue($workspace_1->access('delete', $admin));
|
$this->assertTrue($workspace_1->access('delete', $admin));
|
||||||
|
|
@ -137,17 +125,14 @@ class WorkspaceCRUDTest extends KernelTestBase {
|
||||||
// entities and all the node revisions have been deleted as well.
|
// entities and all the node revisions have been deleted as well.
|
||||||
$workspace_1->delete();
|
$workspace_1->delete();
|
||||||
|
|
||||||
// There are no more tracked entities in 'workspace_1'.
|
$associated_revisions = $workspace_association_storage->getTrackedEntities($workspace_1->id(), TRUE);
|
||||||
$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');
|
|
||||||
$this->assertCount(0, $associated_revisions);
|
$this->assertCount(0, $associated_revisions);
|
||||||
|
$node_revision_count = $node_storage
|
||||||
// There should still be 2 revisions associated with 'live'.
|
->getQuery()
|
||||||
$live_revisions = $this->getUnassociatedRevisions('node');
|
->allRevisions()
|
||||||
$this->assertCount(2, $live_revisions);
|
->count()
|
||||||
|
->execute();
|
||||||
|
$this->assertEquals(0, $node_revision_count);
|
||||||
|
|
||||||
// Create another workspace, this time with a larger number of associated
|
// Create another workspace, this time with a larger number of associated
|
||||||
// node revisions so we can test the batch purge process.
|
// node revisions so we can test the batch purge process.
|
||||||
|
|
@ -164,27 +149,16 @@ class WorkspaceCRUDTest extends KernelTestBase {
|
||||||
$workspace_2_node_1->save();
|
$workspace_2_node_1->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now there is one entity tracked in 'workspace_2'.
|
// The workspace should have 60 associated node revisions.
|
||||||
$tracked_entities = $workspace_association->getTrackedEntities($workspace_2->id());
|
$associated_revisions = $workspace_association_storage->getTrackedEntities($workspace_2->id(), TRUE);
|
||||||
$this->assertCount(1, $tracked_entities['node']);
|
$this->assertCount(60, $associated_revisions['node']);
|
||||||
|
|
||||||
// One revision of this entity is in 'live'.
|
// Delete the workspace and check that we still have 10 revision left to
|
||||||
$live_revisions = $this->getUnassociatedRevisions('node', [$workspace_2_node_1->id()]);
|
|
||||||
$this->assertCount(1, $live_revisions);
|
|
||||||
|
|
||||||
// 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.
|
// delete.
|
||||||
$workspace_2->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.
|
$associated_revisions = $workspace_association_storage->getTrackedEntities($workspace_2->id(), TRUE);
|
||||||
$live_revisions = $this->getUnassociatedRevisions('node', [$workspace_2_node_1->id()]);
|
$this->assertCount(10, $associated_revisions['node']);
|
||||||
$this->assertCount(1, $live_revisions);
|
|
||||||
|
|
||||||
$workspace_deleted = \Drupal::state()->get('workspace.deleted');
|
$workspace_deleted = \Drupal::state()->get('workspace.deleted');
|
||||||
$this->assertCount(1, $workspace_deleted);
|
$this->assertCount(1, $workspace_deleted);
|
||||||
|
|
@ -203,94 +177,41 @@ class WorkspaceCRUDTest extends KernelTestBase {
|
||||||
// from the "workspace.delete" state entry.
|
// from the "workspace.delete" state entry.
|
||||||
\Drupal::service('cron')->run();
|
\Drupal::service('cron')->run();
|
||||||
|
|
||||||
$associated_revisions = $workspace_association->getTrackedEntities($workspace_2->id());
|
$associated_revisions = $workspace_association_storage->getTrackedEntities($workspace_2->id(), TRUE);
|
||||||
$this->assertCount(0, $associated_revisions);
|
$this->assertCount(0, $associated_revisions);
|
||||||
|
$node_revision_count = $node_storage
|
||||||
// 'workspace_2 'is empty now.
|
->getQuery()
|
||||||
$associated_revisions = $workspace_association->getAssociatedRevisions($workspace_2->id(), 'node', [$workspace_2_node_1->id()]);
|
->allRevisions()
|
||||||
$this->assertCount(0, $associated_revisions);
|
->count()
|
||||||
$tracked_entities = $workspace_association->getTrackedEntities($workspace_2->id());
|
->execute();
|
||||||
$this->assertEmpty($tracked_entities);
|
$this->assertEquals(0, $node_revision_count);
|
||||||
|
|
||||||
// The 3 revisions in 'live' remain.
|
|
||||||
$live_revisions = $this->getUnassociatedRevisions('node');
|
|
||||||
$this->assertCount(3, $live_revisions);
|
|
||||||
|
|
||||||
$workspace_deleted = \Drupal::state()->get('workspace.deleted');
|
$workspace_deleted = \Drupal::state()->get('workspace.deleted');
|
||||||
$this->assertCount(0, $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() {
|
public function testWorkspaceAssociationValidation() {
|
||||||
$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();
|
|
||||||
$workspace = Workspace::create([
|
$workspace = Workspace::create([
|
||||||
'id' => 'stage',
|
'id' => 'gibbon',
|
||||||
'label' => 'Stage',
|
'label' => 'Gibbon',
|
||||||
]);
|
]);
|
||||||
$workspace->save();
|
$workspace->save();
|
||||||
$this->workspaceManager->setActiveWorkspace($workspace);
|
$node = $this->createNode();
|
||||||
|
|
||||||
// Create a new node in the 'stage' workspace
|
$workspace_association = WorkspaceAssociation::create([
|
||||||
$node = $this->createNode(['status' => TRUE]);
|
'workspace' => $workspace,
|
||||||
|
'target_entity_type_id' => $node->getEntityTypeId(),
|
||||||
|
'target_entity_id' => $node->id(),
|
||||||
|
'target_entity_revision_id' => $node->getRevisionId(),
|
||||||
|
]);
|
||||||
|
|
||||||
// Create an additional workspace-specific revision for the node.
|
$violations = $workspace_association->validate();
|
||||||
$node->setNewRevision(TRUE);
|
$this->assertCount(0, $violations);
|
||||||
$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]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -275,7 +275,7 @@ class WorkspaceIntegrationTest extends KernelTestBase {
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
$test_scenarios['add_published_node_in_stage'] = $revision_state;
|
$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
|
// Deploying 'stage' to 'live' should simply make the latest revisions in
|
||||||
// 'stage' the default ones in 'live'.
|
// 'stage' the default ones in 'live'.
|
||||||
|
|
@ -365,9 +365,8 @@ class WorkspaceIntegrationTest extends KernelTestBase {
|
||||||
$this->switchToWorkspace('stage');
|
$this->switchToWorkspace('stage');
|
||||||
|
|
||||||
// Add a workspace-specific revision to a pre-existing node.
|
// Add a workspace-specific revision to a pre-existing node.
|
||||||
$node = $this->entityTypeManager->getStorage('node')->load(2);
|
$this->nodes[1]->title->value = 'stage - 2 - r3 - published';
|
||||||
$node->title->value = 'stage - 2 - r3 - published';
|
$this->nodes[1]->save();
|
||||||
$node->save();
|
|
||||||
|
|
||||||
$query = $this->entityTypeManager->getStorage('node')->getQuery();
|
$query = $this->entityTypeManager->getStorage('node')->getQuery();
|
||||||
$query->sort('nid');
|
$query->sort('nid');
|
||||||
|
|
@ -810,7 +809,7 @@ class WorkspaceIntegrationTest extends KernelTestBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks the workspace_association records for a test scenario.
|
* Checks the workspace_association entries for a test scenario.
|
||||||
*
|
*
|
||||||
* @param array $expected
|
* @param array $expected
|
||||||
* An array of expected values, as defined in ::testWorkspaces().
|
* An array of expected values, as defined in ::testWorkspaces().
|
||||||
|
|
@ -818,10 +817,10 @@ class WorkspaceIntegrationTest extends KernelTestBase {
|
||||||
* The ID of the entity type that is being tested.
|
* The ID of the entity type that is being tested.
|
||||||
*/
|
*/
|
||||||
protected function assertWorkspaceAssociation(array $expected, $entity_type_id) {
|
protected function assertWorkspaceAssociation(array $expected, $entity_type_id) {
|
||||||
/** @var \Drupal\workspaces\WorkspaceAssociationInterface $workspace_association */
|
/** @var \Drupal\workspaces\WorkspaceAssociationStorageInterface $workspace_association_storage */
|
||||||
$workspace_association = \Drupal::service('workspaces.association');
|
$workspace_association_storage = $this->entityTypeManager->getStorage('workspace_association');
|
||||||
foreach ($expected as $workspace_id => $expected_tracked_revision_ids) {
|
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] : [];
|
$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));
|
$this->assertEquals($expected_tracked_revision_ids, array_keys($tracked_revision_ids));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -35,7 +35,7 @@ trait WorkspaceTestTrait {
|
||||||
$this->workspaceManager = \Drupal::service('workspaces.manager');
|
$this->workspaceManager = \Drupal::service('workspaces.manager');
|
||||||
|
|
||||||
$this->installEntitySchema('workspace');
|
$this->installEntitySchema('workspace');
|
||||||
$this->installSchema('workspaces', ['workspace_association']);
|
$this->installEntitySchema('workspace_association');
|
||||||
|
|
||||||
// Create two workspaces by default, 'live' and 'stage'.
|
// Create two workspaces by default, 'live' and 'stage'.
|
||||||
$this->workspaces['live'] = Workspace::create(['id' => 'live']);
|
$this->workspaces['live'] = Workspace::create(['id' => 'live']);
|
||||||
|
|
@ -64,33 +64,4 @@ trait WorkspaceTestTrait {
|
||||||
\Drupal::service('workspaces.manager')->setActiveWorkspace($workspace);
|
\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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,6 @@
|
||||||
* Contains install, update and uninstall functions for the Workspaces module.
|
* 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;
|
use Drupal\workspaces\Entity\Workspace;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -34,27 +32,6 @@ function workspaces_requirements($phase) {
|
||||||
return $requirements;
|
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().
|
* Implements hook_install().
|
||||||
*/
|
*/
|
||||||
|
|
@ -84,83 +61,3 @@ function workspaces_install() {
|
||||||
'uid' => $owner_id,
|
'uid' => $owner_id,
|
||||||
])->save();
|
])->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.");
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@
|
||||||
use Drupal\Component\Serialization\Json;
|
use Drupal\Component\Serialization\Json;
|
||||||
use Drupal\Core\Entity\EntityFormInterface;
|
use Drupal\Core\Entity\EntityFormInterface;
|
||||||
use Drupal\Core\Entity\EntityInterface;
|
use Drupal\Core\Entity\EntityInterface;
|
||||||
use Drupal\Core\Entity\EntityTypeInterface;
|
|
||||||
use Drupal\Core\Form\FormStateInterface;
|
use Drupal\Core\Form\FormStateInterface;
|
||||||
use Drupal\Core\Routing\RouteMatchInterface;
|
use Drupal\Core\Routing\RouteMatchInterface;
|
||||||
use Drupal\Core\Session\AccountInterface;
|
use Drupal\Core\Session\AccountInterface;
|
||||||
|
|
@ -67,15 +66,6 @@ function workspaces_field_info_alter(&$definitions) {
|
||||||
->fieldInfoAlter($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().
|
* Implements hook_entity_preload().
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,6 @@
|
||||||
* Post update functions for the Workspaces module.
|
* 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.
|
* Clear caches due to access changes.
|
||||||
*/
|
*/
|
||||||
|
|
@ -24,122 +19,3 @@ function workspaces_post_update_remove_default_workspace() {
|
||||||
$workspace->delete();
|
$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');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,12 @@
|
||||||
services:
|
services:
|
||||||
workspaces.manager:
|
workspaces.manager:
|
||||||
class: Drupal\workspaces\WorkspaceManager
|
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:
|
tags:
|
||||||
- { name: service_id_collector, tag: workspace_negotiator }
|
- { name: service_id_collector, tag: workspace_negotiator }
|
||||||
workspaces.operation_factory:
|
workspaces.operation_factory:
|
||||||
class: Drupal\workspaces\WorkspaceOperationFactory
|
class: Drupal\workspaces\WorkspaceOperationFactory
|
||||||
arguments: ['@entity_type.manager', '@database', '@workspaces.manager', '@workspaces.association']
|
arguments: ['@entity_type.manager', '@database', '@workspaces.manager']
|
||||||
workspaces.association:
|
|
||||||
class: Drupal\workspaces\WorkspaceAssociation
|
|
||||||
arguments: ['@database', '@entity_type.manager']
|
|
||||||
tags:
|
|
||||||
- { name: backend_overridable }
|
|
||||||
|
|
||||||
workspaces.negotiator.session:
|
workspaces.negotiator.session:
|
||||||
class: Drupal\workspaces\Negotiator\SessionWorkspaceNegotiator
|
class: Drupal\workspaces\Negotiator\SessionWorkspaceNegotiator
|
||||||
|
|
@ -30,12 +25,6 @@ services:
|
||||||
tags:
|
tags:
|
||||||
- { name: access_check, applies_to: _has_active_workspace }
|
- { 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:
|
cache_context.workspace:
|
||||||
class: Drupal\workspaces\WorkspaceCacheContext
|
class: Drupal\workspaces\WorkspaceCacheContext
|
||||||
arguments: ['@workspaces.manager']
|
arguments: ['@workspaces.manager']
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue