summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNathaniel Catchpole2018-07-11 15:03:02 (GMT)
committerNathaniel Catchpole2018-07-11 15:03:02 (GMT)
commit0968d287df083daff68ff749874610af4f0d9761 (patch)
treee5f3278bb7ed4d374ba4075534e40524545928b0
parent5a7dc82fe6c440551060b9bc6ec45b28966c098f (diff)
Issue #2958752 by amateescu, timmillwood, catch: Refactor workspace content replication plugin system to three services
-rw-r--r--core/modules/workspace/src/Annotation/RepositoryHandler.php53
-rw-r--r--core/modules/workspace/src/Entity/Workspace.php25
-rw-r--r--core/modules/workspace/src/Form/WorkspaceDeleteForm.php2
-rw-r--r--core/modules/workspace/src/Form/WorkspaceDeployForm.php80
-rw-r--r--core/modules/workspace/src/Negotiator/DefaultWorkspaceNegotiator.php1
-rw-r--r--core/modules/workspace/src/Plugin/RepositoryHandler/NullRepositoryHandler.php82
-rw-r--r--core/modules/workspace/src/RepositoryHandlerBase.php72
-rw-r--r--core/modules/workspace/src/RepositoryHandlerInterface.php127
-rw-r--r--core/modules/workspace/src/RepositoryHandlerManager.php66
-rw-r--r--core/modules/workspace/src/RepositoryHandlerManagerInterface.php23
-rw-r--r--core/modules/workspace/src/WorkspaceInterface.php17
-rw-r--r--core/modules/workspace/src/WorkspaceListBuilder.php3
-rw-r--r--core/modules/workspace/src/WorkspaceOperationFactory.php58
-rw-r--r--core/modules/workspace/src/WorkspaceOperationInterface.php82
-rw-r--r--core/modules/workspace/src/WorkspacePublisher.php (renamed from core/modules/workspace/src/Plugin/RepositoryHandler/LiveRepositoryHandler.php)78
-rw-r--r--core/modules/workspace/src/WorkspacePublisherInterface.php17
-rw-r--r--core/modules/workspace/tests/src/Functional/EntityResource/WorkspaceResourceTestBase.php16
-rw-r--r--core/modules/workspace/tests/src/Functional/WorkspaceConcurrentEditingTest.php2
-rw-r--r--core/modules/workspace/tests/src/Kernel/WorkspaceIntegrationTest.php11
-rw-r--r--core/modules/workspace/workspace.install2
-rw-r--r--core/modules/workspace/workspace.services.yml13
21 files changed, 224 insertions, 606 deletions
diff --git a/core/modules/workspace/src/Annotation/RepositoryHandler.php b/core/modules/workspace/src/Annotation/RepositoryHandler.php
deleted file mode 100644
index 89f7010..0000000
--- a/core/modules/workspace/src/Annotation/RepositoryHandler.php
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-
-namespace Drupal\workspace\Annotation;
-
-use Drupal\Component\Annotation\Plugin;
-
-/**
- * Defines a RepositoryHandler annotation object.
- *
- * @see \Drupal\workspace\RepositoryHandlerInterface
- * @see \Drupal\workspace\RepositoryHandlerBase
- * @see \Drupal\workspace\RepositoryHandlerManager
- * @see plugin_api
- *
- * @Annotation
- */
-class RepositoryHandler extends Plugin {
-
- /**
- * The plugin ID.
- *
- * @var string
- */
- public $id;
-
- /**
- * The human-readable name of the repository handler plugin.
- *
- * @var \Drupal\Core\Annotation\Translation
- *
- * @ingroup plugin_translatable
- */
- public $label;
-
- /**
- * A short description of the repository handler plugin.
- *
- * @var \Drupal\Core\Annotation\Translation
- *
- * @ingroup plugin_translatable
- */
- public $description;
-
- /**
- * The human-readable category.
- *
- * @var \Drupal\Core\Annotation\Translation
- *
- * @ingroup plugin_translatable
- */
- public $category = '';
-
-}
diff --git a/core/modules/workspace/src/Entity/Workspace.php b/core/modules/workspace/src/Entity/Workspace.php
index beba88a..7d5198d 100644
--- a/core/modules/workspace/src/Entity/Workspace.php
+++ b/core/modules/workspace/src/Entity/Workspace.php
@@ -108,35 +108,14 @@ class Workspace extends ContentEntityBase implements WorkspaceInterface {
->setLabel(new TranslatableMarkup('Created'))
->setDescription(new TranslatableMarkup('The time that the workspaces was created.'));
- $fields['target'] = BaseFieldDefinition::create('string')
- ->setLabel(new TranslatableMarkup('Target workspace'))
- ->setDescription(new TranslatableMarkup('The workspace to push to and pull from.'))
- ->setRevisionable(TRUE)
- ->setRequired(TRUE)
- ->setDefaultValue('live');
-
return $fields;
}
/**
* {@inheritdoc}
*/
- public function push() {
- return $this->getRepositoryHandler()->push();
- }
-
- /**
- * {@inheritdoc}
- */
- public function pull() {
- return $this->getRepositoryHandler()->pull();
- }
-
- /**
- * {@inheritdoc}
- */
- public function getRepositoryHandler() {
- return \Drupal::service('plugin.manager.workspace.repository_handler')->createFromWorkspace($this);
+ public function publish() {
+ return \Drupal::service('workspace.operation_factory')->getPublisher($this)->publish();
}
/**
diff --git a/core/modules/workspace/src/Form/WorkspaceDeleteForm.php b/core/modules/workspace/src/Form/WorkspaceDeleteForm.php
index 4a14bc6..9c2113b 100644
--- a/core/modules/workspace/src/Form/WorkspaceDeleteForm.php
+++ b/core/modules/workspace/src/Form/WorkspaceDeleteForm.php
@@ -24,7 +24,7 @@ class WorkspaceDeleteForm extends ContentEntityDeleteForm {
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form = parent::buildForm($form, $form_state);
- $source_rev_diff = $this->entity->getRepositoryHandler()->getDifferringRevisionIdsOnSource();
+ $source_rev_diff = $this->entityTypeManager->getStorage('workspace_association')->getTrackedEntities($this->entity->id());
$items = [];
foreach ($source_rev_diff as $entity_type_id => $revision_ids) {
$label = $this->entityTypeManager->getDefinition($entity_type_id)->getLabel();
diff --git a/core/modules/workspace/src/Form/WorkspaceDeployForm.php b/core/modules/workspace/src/Form/WorkspaceDeployForm.php
index 2abd395..bc24e20 100644
--- a/core/modules/workspace/src/Form/WorkspaceDeployForm.php
+++ b/core/modules/workspace/src/Form/WorkspaceDeployForm.php
@@ -8,6 +8,7 @@ use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\MessengerInterface;
+use Drupal\workspace\WorkspaceOperationFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@@ -30,20 +31,30 @@ class WorkspaceDeployForm extends ContentEntityForm {
protected $messenger;
/**
+ * The workspace operation factory.
+ *
+ * @var \Drupal\workspace\WorkspaceOperationFactory
+ */
+ protected $workspaceOperationFactory;
+
+ /**
* Constructs a new WorkspaceDeployForm.
*
* @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
* The entity repository service.
- * @param \Drupal\Core\Messenger\MessengerInterface $messenger
- * The messenger service.
* @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
* The entity type bundle service.
* @param \Drupal\Component\Datetime\TimeInterface $time
* The time service.
+ * @param \Drupal\Core\Messenger\MessengerInterface $messenger
+ * The messenger service.
+ * @param \Drupal\workspace\WorkspaceOperationFactory $workspace_operation_factory
+ * The workspace operation factory service.
*/
- public function __construct(EntityRepositoryInterface $entity_repository, MessengerInterface $messenger, EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL, TimeInterface $time = NULL) {
+ public function __construct(EntityRepositoryInterface $entity_repository, EntityTypeBundleInfoInterface $entity_type_bundle_info, TimeInterface $time, MessengerInterface $messenger, WorkspaceOperationFactory $workspace_operation_factory) {
parent::__construct($entity_repository, $entity_type_bundle_info, $time);
$this->messenger = $messenger;
+ $this->workspaceOperationFactory = $workspace_operation_factory;
}
/**
@@ -52,9 +63,10 @@ class WorkspaceDeployForm extends ContentEntityForm {
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity.repository'),
- $container->get('messenger'),
$container->get('entity_type.bundle.info'),
- $container->get('datetime.time')
+ $container->get('datetime.time'),
+ $container->get('messenger'),
+ $container->get('workspace.operation_factory')
);
}
@@ -64,17 +76,17 @@ class WorkspaceDeployForm extends ContentEntityForm {
public function form(array $form, FormStateInterface $form_state) {
$form = parent::form($form, $form_state);
- $repository_handler = $this->entity->getRepositoryHandler();
+ $workspace_publisher = $this->workspaceOperationFactory->getPublisher($this->entity);
$args = [
'%source_label' => $this->entity->label(),
- '%target_label' => $repository_handler->getLabel(),
+ '%target_label' => $workspace_publisher->getTargetLabel(),
];
$form['#title'] = $this->t('Deploy %source_label workspace', $args);
// List the changes that can be pushed.
- if ($source_rev_diff = $repository_handler->getDifferringRevisionIdsOnSource()) {
- $total_count = $repository_handler->getNumberOfChangesOnSource();
+ if ($source_rev_diff = $workspace_publisher->getDifferringRevisionIdsOnSource()) {
+ $total_count = $workspace_publisher->getNumberOfChangesOnSource();
$form['deploy'] = [
'#theme' => 'item_list',
'#title' => $this->formatPlural($total_count, 'There is @count item that can be deployed from %source_label to %target_label', 'There are @count items that can be deployed from %source_label to %target_label', $args),
@@ -86,20 +98,6 @@ class WorkspaceDeployForm extends ContentEntityForm {
}
}
- // List the changes that can be pulled.
- if ($target_rev_diff = $repository_handler->getDifferringRevisionIdsOnTarget()) {
- $total_count = $repository_handler->getNumberOfChangesOnTarget();
- $form['refresh'] = [
- '#theme' => 'item_list',
- '#title' => $this->formatPlural($total_count, 'There is @count item that can be refreshed from %target_label to %source_label', 'There are @count items that can be refreshed from %target_label to %source_label', $args),
- '#items' => [],
- '#total_count' => $total_count,
- ];
- foreach ($target_rev_diff as $entity_type_id => $revision_difference) {
- $form['deploy']['#items'][$entity_type_id] = $this->entityTypeManager->getDefinition($entity_type_id)->getCountLabel(count($revision_difference));
- }
- }
-
// If there are no changes to push or pull, show an informational message.
if (!isset($form['deploy']) && !isset($form['refresh'])) {
$form['help'] = [
@@ -117,11 +115,11 @@ class WorkspaceDeployForm extends ContentEntityForm {
$elements = parent::actions($form, $form_state);
unset($elements['delete']);
- $repository_handler = $this->entity->getRepositoryHandler();
+ $workspace_publisher = $this->workspaceOperationFactory->getPublisher($this->entity);
if (isset($form['deploy'])) {
$total_count = $form['deploy']['#total_count'];
- $elements['submit']['#value'] = $this->formatPlural($total_count, 'Deploy @count item to @target', 'Deploy @count items to @target', ['@target' => $repository_handler->getLabel()]);
+ $elements['submit']['#value'] = $this->formatPlural($total_count, 'Deploy @count item to @target', 'Deploy @count items to @target', ['@target' => $workspace_publisher->getTargetLabel()]);
$elements['submit']['#submit'] = ['::submitForm', '::deploy'];
}
else {
@@ -130,16 +128,6 @@ class WorkspaceDeployForm extends ContentEntityForm {
$elements['submit']['#disabled'] = TRUE;
}
- // Only show the 'Refresh' operation if there's something to pull.
- if (isset($form['refresh'])) {
- $total_count = $form['refresh']['#total_count'];
- $elements['refresh'] = [
- '#type' => 'submit',
- '#value' => $this->formatPlural($total_count, 'Refresh @count item from @target', 'Refresh @count items from @target', ['@target' => $repository_handler->getLabel()]),
- '#submit' => ['::submitForm', '::refresh'],
- ];
- }
-
$elements['cancel'] = [
'#type' => 'link',
'#title' => $this->t('Cancel'),
@@ -162,7 +150,7 @@ class WorkspaceDeployForm extends ContentEntityForm {
$workspace = $this->entity;
try {
- $workspace->push();
+ $workspace->publish();
$this->messenger->addMessage($this->t('Successful deployment.'));
}
catch (\Exception $e) {
@@ -170,24 +158,4 @@ class WorkspaceDeployForm extends ContentEntityForm {
}
}
- /**
- * Form submission handler; pulls the target's content into a workspace.
- *
- * @param array $form
- * An associative array containing the structure of the form.
- * @param \Drupal\Core\Form\FormStateInterface $form_state
- * The current state of the form.
- */
- public function refresh(array &$form, FormStateInterface $form_state) {
- $workspace = $this->entity;
-
- try {
- $workspace->pull();
- $this->messenger->addMessage($this->t('Refresh successful.'));
- }
- catch (\Exception $e) {
- $this->messenger->addMessage($this->t('Refresh failed. All errors have been logged.'), 'error');
- }
- }
-
}
diff --git a/core/modules/workspace/src/Negotiator/DefaultWorkspaceNegotiator.php b/core/modules/workspace/src/Negotiator/DefaultWorkspaceNegotiator.php
index d4310db..fe8f2a7 100644
--- a/core/modules/workspace/src/Negotiator/DefaultWorkspaceNegotiator.php
+++ b/core/modules/workspace/src/Negotiator/DefaultWorkspaceNegotiator.php
@@ -51,7 +51,6 @@ class DefaultWorkspaceNegotiator implements WorkspaceNegotiatorInterface {
$default_workspace = $this->workspaceStorage->create([
'id' => WorkspaceInterface::DEFAULT_WORKSPACE,
'label' => Unicode::ucwords(WorkspaceInterface::DEFAULT_WORKSPACE),
- 'target' => '',
]);
$default_workspace->enforceIsNew(FALSE);
diff --git a/core/modules/workspace/src/Plugin/RepositoryHandler/NullRepositoryHandler.php b/core/modules/workspace/src/Plugin/RepositoryHandler/NullRepositoryHandler.php
deleted file mode 100644
index ce2d515..0000000
--- a/core/modules/workspace/src/Plugin/RepositoryHandler/NullRepositoryHandler.php
+++ /dev/null
@@ -1,82 +0,0 @@
-<?php
-
-namespace Drupal\workspace\Plugin\RepositoryHandler;
-
-use Drupal\Core\Plugin\PluginBase;
-use Drupal\workspace\RepositoryHandlerInterface;
-
-/**
- * Defines a fallback repository handler plugin.
- *
- * @RepositoryHandler(
- * id = "null",
- * label = @Translation("Missing repository handler"),
- * description = @Translation("Provides a fallback for missing repository handlers. Do not use."),
- * )
- */
-class NullRepositoryHandler extends PluginBase implements RepositoryHandlerInterface {
-
- /**
- * {@inheritdoc}
- */
- public function push() {
- // Nothing to do here.
- }
-
- /**
- * {@inheritdoc}
- */
- public function pull() {
- // Nothing to do here.
- }
-
- /**
- * {@inheritdoc}
- */
- public function checkConflictsOnTarget() {
- return [];
- }
-
- /**
- * {@inheritdoc}
- */
- public function getDifferringRevisionIdsOnTarget() {
- return [];
- }
-
- /**
- * {@inheritdoc}
- */
- public function getDifferringRevisionIdsOnSource() {
- return [];
- }
-
- /**
- * {@inheritdoc}
- */
- public function getNumberOfChangesOnTarget() {
- return 0;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getNumberOfChangesOnSource() {
- return 0;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getLabel() {
- return $this->getPluginDefinition()['label'];
- }
-
- /**
- * {@inheritdoc}
- */
- public function getDescription() {
- return $this->getPluginDefinition()['description'];
- }
-
-}
diff --git a/core/modules/workspace/src/RepositoryHandlerBase.php b/core/modules/workspace/src/RepositoryHandlerBase.php
deleted file mode 100644
index 636fc28..0000000
--- a/core/modules/workspace/src/RepositoryHandlerBase.php
+++ /dev/null
@@ -1,72 +0,0 @@
-<?php
-
-namespace Drupal\workspace;
-
-use Drupal\Core\Entity\DependencyTrait;
-use Drupal\Core\Plugin\PluginBase;
-
-/**
- * Defines a base RepositoryHandler plugin implementation.
- *
- * @see \Drupal\workspace\RepositoryHandlerInterface
- * @see \Drupal\workspace\RepositoryHandlerManager
- * @see \Drupal\workspace\Annotation\RepositoryHandler
- * @see plugin_api
- */
-abstract class RepositoryHandlerBase extends PluginBase implements RepositoryHandlerInterface {
-
- use DependencyTrait;
-
- /**
- * The source repository identifier.
- *
- * @var string
- */
- protected $source;
-
- /**
- * The target repository identifier.
- *
- * @var string
- */
- protected $target;
-
- /**
- * {@inheritdoc}
- */
- public function __construct(array $configuration, $plugin_id, $plugin_definition) {
- parent::__construct($configuration, $plugin_id, $plugin_definition);
-
- if (!isset($configuration['source'])) {
- throw new \InvalidArgumentException('Missing repository handler source configuration');
- }
- if (!isset($configuration['target'])) {
- throw new \InvalidArgumentException('Missing repository handler target configuration');
- }
-
- $this->source = $configuration['source'];
- $this->target = $configuration['target'];
- }
-
- /**
- * {@inheritdoc}
- */
- public function getLabel() {
- return $this->getPluginDefinition()['label'];
- }
-
- /**
- * {@inheritdoc}
- */
- public function getDescription() {
- return $this->getPluginDefinition()['description'];
- }
-
- /**
- * {@inheritdoc}
- */
- public function calculateDependencies() {
- return [];
- }
-
-}
diff --git a/core/modules/workspace/src/RepositoryHandlerInterface.php b/core/modules/workspace/src/RepositoryHandlerInterface.php
deleted file mode 100644
index 660ab62..0000000
--- a/core/modules/workspace/src/RepositoryHandlerInterface.php
+++ /dev/null
@@ -1,127 +0,0 @@
-<?php
-
-namespace Drupal\workspace;
-
-use Drupal\Component\Plugin\DerivativeInspectionInterface;
-use Drupal\Component\Plugin\PluginInspectionInterface;
-
-/**
- * RepositoryHandler plugins handle content replication.
- *
- * The replication will use data from the target repository handler plugin to
- * merge the content between the source and the target. For example an internal
- * replication might just need the workspace IDs, but a contrib module
- * performing an external replication may need hostname, port, username,
- * password etc.
- */
-interface RepositoryHandlerInterface extends PluginInspectionInterface, DerivativeInspectionInterface {
-
- /**
- * Indicate that an item has been updated both on the source and the target.
- *
- * @var int
- */
- const CONFLICT_UPDATE_ON_CHANGE = 1;
-
- /**
- * Indicate that an item updated on the source has been deleted on the target.
- *
- * @var int
- */
- const CONFLICT_UPDATE_ON_DELETE = 2;
-
- /**
- * Indicate that an item deleted on the source has been changed on the target.
- *
- * @var int
- */
- const CONFLICT_DELETE_ON_CHANGE = 3;
-
- /**
- * Returns the label of the repository handler.
- *
- * This is used as a form label where a user selects the replication target.
- *
- * @return string
- * The label text, which could be a plain string or an object that can be
- * cast to a string.
- */
- public function getLabel();
-
- /**
- * Returns the repository handler plugin description.
- *
- * @return string
- * The description text, which could be a plain string or an object that can
- * be cast to a string.
- */
- public function getDescription();
-
- /**
- * Pushes content from a source repository to a target repository.
- */
- public function push();
-
- /**
- * Pulls content from a target repository to a source repository.
- */
- public function pull();
-
- /**
- * Checks if there are any conflicts between the source and the target.
- *
- * @return array
- * Returns an array consisting of the number of conflicts between the source
- * and the target, keyed by the conflict type constant.
- */
- public function checkConflictsOnTarget();
-
- /**
- * Gets the revision identifiers for items which have changed on the target.
- *
- * @return array
- * A multidimensional array of revision identifiers, either the revision ID
- * or the revision UUID, keyed by entity type IDs.
- *
- * @todo Update the return values to be only UUIDs and revision UUIDs in
- * https://www.drupal.org/node/2958752
- */
- public function getDifferringRevisionIdsOnTarget();
-
- /**
- * Gets the revision identifiers for items which have changed on the source.
- *
- * @return array
- * A multidimensional array of revision identifiers, either the revision ID
- * or the revision UUID, keyed by entity type IDs.
- *
- * @todo Update the return values to be only UUIDs and revision UUIDs in
- * https://www.drupal.org/node/2958752
- */
- public function getDifferringRevisionIdsOnSource();
-
- /**
- * Gets the total number of items which have changed on the target.
- *
- * This returns the aggregated changes count across all entity types.
- * For example, if two nodes and one taxonomy term have changed on the target,
- * the return value is 3.
- *
- * @return int
- * The number of differing revisions.
- */
- public function getNumberOfChangesOnTarget();
-
- /**
- * Gets the total number of items which have changed on the source.
- *
- * This returns the aggregated changes count across all entity types.
- * For example, if two nodes and one taxonomy term have changed on the source,
- * the return value is 3.
- *
- * @return int
- * The number of differing revisions.
- */
- public function getNumberOfChangesOnSource();
-
-}
diff --git a/core/modules/workspace/src/RepositoryHandlerManager.php b/core/modules/workspace/src/RepositoryHandlerManager.php
deleted file mode 100644
index 9d92772..0000000
--- a/core/modules/workspace/src/RepositoryHandlerManager.php
+++ /dev/null
@@ -1,66 +0,0 @@
-<?php
-
-namespace Drupal\workspace;
-
-use Drupal\Component\Plugin\FallbackPluginManagerInterface;
-use Drupal\Core\Plugin\CategorizingPluginManagerTrait;
-use Drupal\Core\Plugin\DefaultPluginManager;
-use Drupal\Core\Cache\CacheBackendInterface;
-use Drupal\Core\Extension\ModuleHandlerInterface;
-
-/**
- * Provides a plugin manager for Repository Handlers.
- *
- * @see \Drupal\workspace\Annotation\RepositoryHandler
- * @see \Drupal\workspace\RepositoryHandlerInterface
- * @see plugin_api
- */
-class RepositoryHandlerManager extends DefaultPluginManager implements RepositoryHandlerManagerInterface, FallbackPluginManagerInterface {
-
- use CategorizingPluginManagerTrait;
-
- /**
- * Constructs a new RepositoryHandlerManager.
- *
- * @param \Traversable $namespaces
- * An object that implements \Traversable which contains the root paths
- * keyed by the corresponding namespace to look for plugin implementations.
- * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
- * Cache backend instance to use.
- * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
- * The module handler to invoke the alter hook with.
- */
- public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
- parent::__construct('Plugin/RepositoryHandler', $namespaces, $module_handler, 'Drupal\workspace\RepositoryHandlerInterface', 'Drupal\workspace\Annotation\RepositoryHandler');
- $this->alterInfo('workspace_repository_handler_info');
- $this->setCacheBackend($cache_backend, 'workspace_repository_handler');
- }
-
- /**
- * {@inheritdoc}
- */
- public function processDefinition(&$definition, $plugin_id) {
- parent::processDefinition($definition, $plugin_id);
- $this->processDefinitionCategory($definition);
- }
-
- /**
- * {@inheritdoc}
- */
- public function createFromWorkspace(WorkspaceInterface $workspace) {
- $target = $workspace->target->value;
- $configuration = [
- 'source' => $workspace->id(),
- 'target' => $target,
- ];
- return $this->createInstance($target, $configuration);
- }
-
- /**
- * {@inheritdoc}
- */
- public function getFallbackPluginId($plugin_id, array $configuration = []) {
- return 'null';
- }
-
-}
diff --git a/core/modules/workspace/src/RepositoryHandlerManagerInterface.php b/core/modules/workspace/src/RepositoryHandlerManagerInterface.php
deleted file mode 100644
index ae04e90..0000000
--- a/core/modules/workspace/src/RepositoryHandlerManagerInterface.php
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-
-namespace Drupal\workspace;
-
-use Drupal\Component\Plugin\CategorizingPluginManagerInterface;
-
-/**
- * Provides the interface for a plugin manager of repository handlers.
- */
-interface RepositoryHandlerManagerInterface extends CategorizingPluginManagerInterface {
-
- /**
- * Creates a repository handler instance from a given workspace entity.
- *
- * @param \Drupal\workspace\WorkspaceInterface $workspace
- * A workspace entity.
- *
- * @return \Drupal\workspace\RepositoryHandlerInterface
- * A repository handler plugin.
- */
- public function createFromWorkspace(WorkspaceInterface $workspace);
-
-}
diff --git a/core/modules/workspace/src/WorkspaceInterface.php b/core/modules/workspace/src/WorkspaceInterface.php
index 411437c..191518e 100644
--- a/core/modules/workspace/src/WorkspaceInterface.php
+++ b/core/modules/workspace/src/WorkspaceInterface.php
@@ -17,22 +17,9 @@ interface WorkspaceInterface extends ContentEntityInterface, EntityChangedInterf
const DEFAULT_WORKSPACE = 'live';
/**
- * Pushes content from this workspace to the target repository.
+ * Publishes the contents of this workspace to the default (Live) workspace.
*/
- public function push();
-
- /**
- * Pulls content from the target repository into this workspace.
- */
- public function pull();
-
- /**
- * Gets an instance of the repository handler configured for the workspace.
- *
- * @return \Drupal\workspace\RepositoryHandlerInterface
- * A repository handler plugin object.
- */
- public function getRepositoryHandler();
+ public function publish();
/**
* Determines whether the workspace is the default one or not.
diff --git a/core/modules/workspace/src/WorkspaceListBuilder.php b/core/modules/workspace/src/WorkspaceListBuilder.php
index e819454..f6bb934 100644
--- a/core/modules/workspace/src/WorkspaceListBuilder.php
+++ b/core/modules/workspace/src/WorkspaceListBuilder.php
@@ -6,7 +6,6 @@ use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityListBuilder;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
-use Drupal\workspace\Plugin\RepositoryHandler\NullRepositoryHandler;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@@ -94,7 +93,7 @@ class WorkspaceListBuilder extends EntityListBuilder {
];
}
- if (!$entity->getRepositoryHandler() instanceof NullRepositoryHandler) {
+ if (!$entity->isDefaultWorkspace()) {
$operations['deploy'] = [
'title' => $this->t('Deploy content'),
// The 'Deploy' operation should be the default one for the currently
diff --git a/core/modules/workspace/src/WorkspaceOperationFactory.php b/core/modules/workspace/src/WorkspaceOperationFactory.php
new file mode 100644
index 0000000..f4cd119
--- /dev/null
+++ b/core/modules/workspace/src/WorkspaceOperationFactory.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace Drupal\workspace;
+
+use Drupal\Core\Database\Connection;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+
+/**
+ * Defines a factory class for workspace operations.
+ *
+ * @see \Drupal\workspace\WorkspaceOperationInterface
+ * @see \Drupal\workspace\WorkspacePublisherInterface
+ *
+ * @internal
+ */
+class WorkspaceOperationFactory {
+
+ /**
+ * The entity type manager.
+ *
+ * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+ */
+ protected $entityTypeManager;
+
+ /**
+ * The database connection.
+ *
+ * @var \Drupal\Core\Database\Connection
+ */
+ protected $database;
+
+ /**
+ * Constructs a new WorkspacePublisher.
+ *
+ * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+ * The entity type manager.
+ * @param \Drupal\Core\Database\Connection $database
+ * Database connection.
+ */
+ public function __construct(EntityTypeManagerInterface $entity_type_manager, Connection $database) {
+ $this->entityTypeManager = $entity_type_manager;
+ $this->database = $database;
+ }
+
+ /**
+ * Gets the workspace publisher.
+ *
+ * @param \Drupal\workspace\WorkspaceInterface $source
+ * A workspace entity.
+ *
+ * @return \Drupal\workspace\WorkspacePublisherInterface
+ * A workspace publisher object.
+ */
+ public function getPublisher(WorkspaceInterface $source) {
+ return new WorkspacePublisher($this->entityTypeManager, $this->database, $source);
+ }
+
+}
diff --git a/core/modules/workspace/src/WorkspaceOperationInterface.php b/core/modules/workspace/src/WorkspaceOperationInterface.php
new file mode 100644
index 0000000..76bb2b8
--- /dev/null
+++ b/core/modules/workspace/src/WorkspaceOperationInterface.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace Drupal\workspace;
+
+/**
+ * Defines an interface for workspace operations.
+ *
+ * Example operations are publishing, merging and syncing with a remote
+ * workspace.
+ *
+ * @internal
+ */
+interface WorkspaceOperationInterface {
+
+ /**
+ * Returns the human-readable label of the source.
+ *
+ * @return string
+ * The source label.
+ */
+ public function getSourceLabel();
+
+ /**
+ * Returns the human-readable label of the target.
+ *
+ * @return string
+ * The target label.
+ */
+ public function getTargetLabel();
+
+ /**
+ * Checks if there are any conflicts between the source and the target.
+ *
+ * @return array
+ * Returns an array consisting of the number of conflicts between the source
+ * and the target, keyed by the conflict type constant.
+ */
+ public function checkConflictsOnTarget();
+
+ /**
+ * Gets the revision identifiers for items which have changed on the target.
+ *
+ * @return array
+ * A multidimensional array of revision identifiers, keyed by entity type
+ * IDs.
+ */
+ public function getDifferringRevisionIdsOnTarget();
+
+ /**
+ * Gets the revision identifiers for items which have changed on the source.
+ *
+ * @return array
+ * A multidimensional array of revision identifiers, keyed by entity type
+ * IDs.
+ */
+ public function getDifferringRevisionIdsOnSource();
+
+ /**
+ * Gets the total number of items which have changed on the target.
+ *
+ * This returns the aggregated changes count across all entity types.
+ * For example, if two nodes and one taxonomy term have changed on the target,
+ * the return value is 3.
+ *
+ * @return int
+ * The number of differing revisions.
+ */
+ public function getNumberOfChangesOnTarget();
+
+ /**
+ * Gets the total number of items which have changed on the source.
+ *
+ * This returns the aggregated changes count across all entity types.
+ * For example, if two nodes and one taxonomy term have changed on the source,
+ * the return value is 3.
+ *
+ * @return int
+ * The number of differing revisions.
+ */
+ public function getNumberOfChangesOnSource();
+
+}
diff --git a/core/modules/workspace/src/Plugin/RepositoryHandler/LiveRepositoryHandler.php b/core/modules/workspace/src/WorkspacePublisher.php
index fef5942..00e313e 100644
--- a/core/modules/workspace/src/Plugin/RepositoryHandler/LiveRepositoryHandler.php
+++ b/core/modules/workspace/src/WorkspacePublisher.php
@@ -1,35 +1,26 @@
<?php
-namespace Drupal\workspace\Plugin\RepositoryHandler;
+namespace Drupal\workspace;
use Drupal\Core\Database\Connection;
-use Drupal\workspace\RepositoryHandlerBase;
use Drupal\Core\Entity\EntityTypeManagerInterface;
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
-use Drupal\workspace\RepositoryHandlerInterface;
-use Drupal\workspace\WorkspaceConflictException;
-use Symfony\Component\DependencyInjection\ContainerInterface;
/**
- * Defines a plugin which replicates content to the default (Live) workspace.
+ * Default implementation of the workspace publisher.
*
- * @RepositoryHandler(
- * id = "live",
- * label = @Translation("Live"),
- * description = @Translation("The default (Live) workspace."),
- * )
+ * @internal
*/
-class LiveRepositoryHandler extends RepositoryHandlerBase implements RepositoryHandlerInterface, ContainerFactoryPluginInterface {
+class WorkspacePublisher implements WorkspacePublisherInterface {
/**
- * The source workspace entity for the repository handler.
+ * The source workspace entity.
*
* @var \Drupal\workspace\WorkspaceInterface
*/
protected $sourceWorkspace;
/**
- * The target workspace entity for the repository handler.
+ * The target workspace entity.
*
* @var \Drupal\workspace\WorkspaceInterface
*/
@@ -57,56 +48,25 @@ class LiveRepositoryHandler extends RepositoryHandlerBase implements RepositoryH
protected $workspaceAssociationStorage;
/**
- * Constructs a new LiveRepositoryHandler.
+ * Constructs a new WorkspacePublisher.
*
- * @param array $configuration
- * A configuration array containing information about the plugin instance.
- * @param string $plugin_id
- * The plugin_id for the plugin instance.
- * @param mixed $plugin_definition
- * The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\Database\Connection $database
* Database connection.
*/
- public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, Connection $database) {
- parent::__construct($configuration, $plugin_id, $plugin_definition);
-
+ public function __construct(EntityTypeManagerInterface $entity_type_manager, Connection $database, WorkspaceInterface $source) {
$this->entityTypeManager = $entity_type_manager;
$this->database = $database;
$this->workspaceAssociationStorage = $entity_type_manager->getStorage('workspace_association');
- $this->sourceWorkspace = $this->entityTypeManager->getStorage('workspace')->load($this->source);
- $this->targetWorkspace = $this->entityTypeManager->getStorage('workspace')->load($this->target);
+ $this->sourceWorkspace = $source;
+ $this->targetWorkspace = $this->entityTypeManager->getStorage('workspace')->load(WorkspaceInterface::DEFAULT_WORKSPACE);
}
/**
* {@inheritdoc}
*/
- public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
- return new static(
- $configuration,
- $plugin_id,
- $plugin_definition,
- $container->get('entity_type.manager'),
- $container->get('database')
- );
- }
-
- /**
- * {@inheritdoc}
- */
- public function calculateDependencies() {
- $this->dependencies = parent::calculateDependencies();
- $this->addDependency($this->sourceWorkspace->getConfigDependencyKey(), $this->sourceWorkspace->getConfigDependencyName());
-
- return $this->dependencies;
- }
-
- /**
- * {@inheritdoc}
- */
- public function push() {
+ public function publish() {
if ($this->checkConflictsOnTarget()) {
throw new WorkspaceConflictException();
}
@@ -145,9 +105,15 @@ class LiveRepositoryHandler extends RepositoryHandlerBase implements RepositoryH
/**
* {@inheritdoc}
*/
- public function pull() {
- // Nothing to do for now, pulling in changes can only be implemented when we
- // are able to resolve conflicts.
+ public function getSourceLabel() {
+ return $this->sourceWorkspace->label();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getTargetLabel() {
+ return $this->targetWorkspace->label();
}
/**
@@ -165,7 +131,7 @@ class LiveRepositoryHandler extends RepositoryHandlerBase implements RepositoryH
public function getDifferringRevisionIdsOnTarget() {
$target_revision_difference = [];
- $tracked_entities = $this->workspaceAssociationStorage->getTrackedEntities($this->source);
+ $tracked_entities = $this->workspaceAssociationStorage->getTrackedEntities($this->sourceWorkspace->id());
foreach ($tracked_entities as $entity_type_id => $tracked_revisions) {
$entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
@@ -195,7 +161,7 @@ class LiveRepositoryHandler extends RepositoryHandlerBase implements RepositoryH
*/
public function getDifferringRevisionIdsOnSource() {
// Get the Workspace association revisions which haven't been pushed yet.
- return $this->workspaceAssociationStorage->getTrackedEntities($this->source);
+ return $this->workspaceAssociationStorage->getTrackedEntities($this->sourceWorkspace->id());
}
/**
diff --git a/core/modules/workspace/src/WorkspacePublisherInterface.php b/core/modules/workspace/src/WorkspacePublisherInterface.php
new file mode 100644
index 0000000..c5a4366
--- /dev/null
+++ b/core/modules/workspace/src/WorkspacePublisherInterface.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace Drupal\workspace;
+
+/**
+ * Defines an interface for the workspace publisher.
+ *
+ * @internal
+ */
+interface WorkspacePublisherInterface extends WorkspaceOperationInterface {
+
+ /**
+ * Publishes the contents of a workspace to the default (Live) workspace.
+ */
+ public function publish();
+
+}
diff --git a/core/modules/workspace/tests/src/Functional/EntityResource/WorkspaceResourceTestBase.php b/core/modules/workspace/tests/src/Functional/EntityResource/WorkspaceResourceTestBase.php
index 19943ab..08ff02a 100644
--- a/core/modules/workspace/tests/src/Functional/EntityResource/WorkspaceResourceTestBase.php
+++ b/core/modules/workspace/tests/src/Functional/EntityResource/WorkspaceResourceTestBase.php
@@ -68,7 +68,6 @@ abstract class WorkspaceResourceTestBase extends EntityResourceTestBase {
$workspace = Workspace::create([
'id' => 'layla',
'label' => 'Layla',
- 'target' => 'live',
]);
$workspace->save();
return $workspace;
@@ -120,11 +119,6 @@ abstract class WorkspaceResourceTestBase extends EntityResourceTestBase {
'url' => base_path() . 'user/' . $author->id(),
],
],
- 'target' => [
- [
- 'value' => 'live',
- ],
- ],
'uuid' => [
[
'value' => $this->entity->uuid(),
@@ -148,11 +142,6 @@ abstract class WorkspaceResourceTestBase extends EntityResourceTestBase {
'value' => 'Running on faith',
],
],
- 'target' => [
- [
- 'value' => 'local_workspace:stage',
- ],
- ],
];
}
@@ -176,11 +165,6 @@ abstract class WorkspaceResourceTestBase extends EntityResourceTestBase {
'value' => 'Running on faith',
],
],
- 'target' => [
- [
- 'value' => 'local_workspace:stage',
- ],
- ],
];
}
diff --git a/core/modules/workspace/tests/src/Functional/WorkspaceConcurrentEditingTest.php b/core/modules/workspace/tests/src/Functional/WorkspaceConcurrentEditingTest.php
index 1662eb6..bced173 100644
--- a/core/modules/workspace/tests/src/Functional/WorkspaceConcurrentEditingTest.php
+++ b/core/modules/workspace/tests/src/Functional/WorkspaceConcurrentEditingTest.php
@@ -87,7 +87,7 @@ class WorkspaceConcurrentEditingTest extends BrowserTestBase {
// Deploy the changes from the 'Vultures' workspace and check that the node
// can be edited again in other workspaces.
- $vultures->getRepositoryHandler()->push();
+ $vultures->publish();
$this->switchToWorkspace($gravity);
$this->drupalGet('/node/' . $test_node->id() . '/edit');
$page = $this->getSession()->getPage();
diff --git a/core/modules/workspace/tests/src/Kernel/WorkspaceIntegrationTest.php b/core/modules/workspace/tests/src/Kernel/WorkspaceIntegrationTest.php
index dd34274..cbf2b74 100644
--- a/core/modules/workspace/tests/src/Kernel/WorkspaceIntegrationTest.php
+++ b/core/modules/workspace/tests/src/Kernel/WorkspaceIntegrationTest.php
@@ -104,7 +104,7 @@ class WorkspaceIntegrationTest extends KernelTestBase {
// Create two workspaces by default, 'live' and 'stage'.
$this->workspaces['live'] = Workspace::create(['id' => 'live']);
$this->workspaces['live']->save();
- $this->workspaces['stage'] = Workspace::create(['id' => 'stage', 'target' => 'live']);
+ $this->workspaces['stage'] = Workspace::create(['id' => 'stage']);
$this->workspaces['stage']->save();
$permissions = [
@@ -345,7 +345,8 @@ class WorkspaceIntegrationTest extends KernelTestBase {
$this->assertWorkspaceAssociation($expected_workspace_association['add_published_node_in_stage'], 'node');
// Deploy 'stage' to 'live'.
- $stage_repository_handler = $this->workspaces['stage']->getRepositoryHandler();
+ /** @var \Drupal\workspace\WorkspacePublisher $workspace_publisher */
+ $workspace_publisher = \Drupal::service('workspace.operation_factory')->getPublisher($this->workspaces['stage']);
// Check which revisions need to be pushed.
$expected = [
@@ -356,14 +357,14 @@ class WorkspaceIntegrationTest extends KernelTestBase {
7 => 4,
],
];
- $this->assertEquals($expected, $stage_repository_handler->getDifferringRevisionIdsOnSource());
+ $this->assertEquals($expected, $workspace_publisher->getDifferringRevisionIdsOnSource());
- $stage_repository_handler->push();
+ $this->workspaces['stage']->publish();
$this->assertWorkspaceStatus($test_scenarios['push_stage_to_live'], 'node');
$this->assertWorkspaceAssociation($expected_workspace_association['push_stage_to_live'], 'node');
// Check that there are no more revisions to push.
- $this->assertEmpty($stage_repository_handler->getDifferringRevisionIdsOnSource());
+ $this->assertEmpty($workspace_publisher->getDifferringRevisionIdsOnSource());
}
/**
diff --git a/core/modules/workspace/workspace.install b/core/modules/workspace/workspace.install
index e9af7df..e748fc6 100644
--- a/core/modules/workspace/workspace.install
+++ b/core/modules/workspace/workspace.install
@@ -48,14 +48,12 @@ function workspace_install() {
Workspace::create([
'id' => 'live',
'label' => 'Live',
- 'target' => '',
'uid' => $owner_id,
])->save();
Workspace::create([
'id' => 'stage',
'label' => 'Stage',
- 'target' => 'live',
'uid' => $owner_id,
])->save();
}
diff --git a/core/modules/workspace/workspace.services.yml b/core/modules/workspace/workspace.services.yml
index cd2b9ea..e147017 100644
--- a/core/modules/workspace/workspace.services.yml
+++ b/core/modules/workspace/workspace.services.yml
@@ -4,9 +4,10 @@ services:
arguments: ['@request_stack', '@entity_type.manager', '@current_user', '@state', '@logger.channel.workspace', '@class_resolver']
tags:
- { name: service_id_collector, tag: workspace_negotiator }
- plugin.manager.workspace.repository_handler:
- class: Drupal\workspace\RepositoryHandlerManager
- parent: default_plugin_manager
+ workspace.operation_factory:
+ class: Drupal\workspace\WorkspaceOperationFactory
+ arguments: ['@entity_type.manager', '@database']
+
workspace.negotiator.default:
class: Drupal\workspace\Negotiator\DefaultWorkspaceNegotiator
arguments: ['@entity_type.manager']
@@ -22,6 +23,7 @@ services:
parent: workspace.negotiator.session
tags:
- { name: workspace_negotiator, priority: 100 }
+
cache_context.workspace:
class: Drupal\workspace\WorkspaceCacheContext
arguments: ['@workspace.manager']
@@ -30,8 +32,9 @@ services:
logger.channel.workspace:
parent: logger.channel_base
arguments: ['workspace']
+
workspace.entity.query.sql:
- decorates: 'entity.query.sql'
+ decorates: entity.query.sql
class: Drupal\workspace\EntityQuery\QueryFactory
arguments: ['@database', '@workspace.manager']
public: false
@@ -39,7 +42,7 @@ services:
tags:
- { name: backend_overridable }
pgsql.workspace.entity.query.sql:
- decorates: 'pgsql.entity.query.sql'
+ decorates: pgsql.entity.query.sql
class: Drupal\workspace\EntityQuery\PgsqlQueryFactory
arguments: ['@database', '@workspace.manager']
public: false