3 namespace Drupal\workspaces;
5 use Drupal\Core\Database\Connection;
6 use Drupal\Core\Entity\EntityTypeManagerInterface;
9 * Default implementation of the workspace publisher.
13 class WorkspacePublisher implements WorkspacePublisherInterface {
16 * The source workspace entity.
18 * @var \Drupal\workspaces\WorkspaceInterface
20 protected $sourceWorkspace;
23 * The target workspace entity.
25 * @var \Drupal\workspaces\WorkspaceInterface
27 protected $targetWorkspace;
30 * The entity type manager.
32 * @var \Drupal\Core\Entity\EntityTypeManagerInterface
34 protected $entityTypeManager;
37 * The database connection.
39 * @var \Drupal\Core\Database\Connection
44 * The workspace association storage.
46 * @var \Drupal\workspaces\WorkspaceAssociationStorageInterface
48 protected $workspaceAssociationStorage;
51 * Constructs a new WorkspacePublisher.
53 * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
54 * The entity type manager.
55 * @param \Drupal\Core\Database\Connection $database
56 * Database connection.
58 public function __construct(EntityTypeManagerInterface $entity_type_manager, Connection $database, WorkspaceInterface $source) {
59 $this->entityTypeManager = $entity_type_manager;
60 $this->database = $database;
61 $this->workspaceAssociationStorage = $entity_type_manager->getStorage('workspace_association');
62 $this->sourceWorkspace = $source;
63 $this->targetWorkspace = $this->entityTypeManager->getStorage('workspace')->load(WorkspaceInterface::DEFAULT_WORKSPACE);
69 public function publish() {
70 if ($this->checkConflictsOnTarget()) {
71 throw new WorkspaceConflictException();
74 $transaction = $this->database->startTransaction();
76 // @todo Handle the publishing of a workspace with a batch operation in
77 // https://www.drupal.org/node/2958752.
78 foreach ($this->getDifferringRevisionIdsOnSource() as $entity_type_id => $revision_difference) {
79 $entity_revisions = $this->entityTypeManager->getStorage($entity_type_id)
80 ->loadMultipleRevisions(array_keys($revision_difference));
81 /** @var \Drupal\Core\Entity\EntityInterface|\Drupal\Core\Entity\RevisionableInterface $entity */
82 foreach ($entity_revisions as $entity) {
83 // When pushing workspace-specific revisions to the default workspace
84 // (Live), we simply need to mark them as default revisions.
85 // @todo Remove this dynamic property once we have an API for
86 // associating temporary data with an entity:
87 // https://www.drupal.org/node/2896474.
88 $entity->_isReplicating = TRUE;
89 $entity->isDefaultRevision(TRUE);
94 catch (\Exception $e) {
95 $transaction->rollBack();
96 watchdog_exception('workspaces', $e);
100 // Notify the workspace association storage that a workspace has been
102 $this->workspaceAssociationStorage->postPush($this->sourceWorkspace);
108 public function getSourceLabel() {
109 return $this->sourceWorkspace->label();
115 public function getTargetLabel() {
116 return $this->targetWorkspace->label();
122 public function checkConflictsOnTarget() {
123 // Nothing to do for now, we can not get to a conflicting state because an
124 // entity which is being edited in a workspace can not be edited in any
131 public function getDifferringRevisionIdsOnTarget() {
132 $target_revision_difference = [];
134 $tracked_entities = $this->workspaceAssociationStorage->getTrackedEntities($this->sourceWorkspace->id());
135 foreach ($tracked_entities as $entity_type_id => $tracked_revisions) {
136 $entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
138 // Get the latest revision IDs for all the entities that are tracked by
139 // the source workspace.
140 $query = $this->entityTypeManager
141 ->getStorage($entity_type_id)
143 ->condition($entity_type->getKey('id'), $tracked_revisions, 'IN')
145 $result = $query->execute();
147 // Now we compare the revision IDs which are tracked by the source
148 // workspace to the latest revision IDs of those entities and the
149 // difference between these two arrays gives us all the entities which
150 // have been modified on the target.
151 if ($revision_difference = array_diff_key($result, $tracked_revisions)) {
152 $target_revision_difference[$entity_type_id] = $revision_difference;
156 return $target_revision_difference;
162 public function getDifferringRevisionIdsOnSource() {
163 // Get the Workspace association revisions which haven't been pushed yet.
164 return $this->workspaceAssociationStorage->getTrackedEntities($this->sourceWorkspace->id());
170 public function getNumberOfChangesOnTarget() {
171 $total_changes = $this->getDifferringRevisionIdsOnTarget();
172 return count($total_changes, COUNT_RECURSIVE) - count($total_changes);
178 public function getNumberOfChangesOnSource() {
179 $total_changes = $this->getDifferringRevisionIdsOnSource();
180 return count($total_changes, COUNT_RECURSIVE) - count($total_changes);