Chris@17
|
1 <?php
|
Chris@17
|
2
|
Chris@17
|
3 namespace Drupal\workspaces;
|
Chris@17
|
4
|
Chris@17
|
5 use Drupal\Core\Database\Connection;
|
Chris@17
|
6 use Drupal\Core\Entity\EntityTypeManagerInterface;
|
Chris@17
|
7
|
Chris@17
|
8 /**
|
Chris@17
|
9 * Default implementation of the workspace publisher.
|
Chris@17
|
10 *
|
Chris@17
|
11 * @internal
|
Chris@17
|
12 */
|
Chris@17
|
13 class WorkspacePublisher implements WorkspacePublisherInterface {
|
Chris@17
|
14
|
Chris@17
|
15 /**
|
Chris@17
|
16 * The source workspace entity.
|
Chris@17
|
17 *
|
Chris@17
|
18 * @var \Drupal\workspaces\WorkspaceInterface
|
Chris@17
|
19 */
|
Chris@17
|
20 protected $sourceWorkspace;
|
Chris@17
|
21
|
Chris@17
|
22 /**
|
Chris@17
|
23 * The target workspace entity.
|
Chris@17
|
24 *
|
Chris@17
|
25 * @var \Drupal\workspaces\WorkspaceInterface
|
Chris@17
|
26 */
|
Chris@17
|
27 protected $targetWorkspace;
|
Chris@17
|
28
|
Chris@17
|
29 /**
|
Chris@17
|
30 * The entity type manager.
|
Chris@17
|
31 *
|
Chris@17
|
32 * @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
Chris@17
|
33 */
|
Chris@17
|
34 protected $entityTypeManager;
|
Chris@17
|
35
|
Chris@17
|
36 /**
|
Chris@17
|
37 * The database connection.
|
Chris@17
|
38 *
|
Chris@17
|
39 * @var \Drupal\Core\Database\Connection
|
Chris@17
|
40 */
|
Chris@17
|
41 protected $database;
|
Chris@17
|
42
|
Chris@17
|
43 /**
|
Chris@17
|
44 * The workspace association storage.
|
Chris@17
|
45 *
|
Chris@17
|
46 * @var \Drupal\workspaces\WorkspaceAssociationStorageInterface
|
Chris@17
|
47 */
|
Chris@17
|
48 protected $workspaceAssociationStorage;
|
Chris@17
|
49
|
Chris@17
|
50 /**
|
Chris@18
|
51 * The workspace manager.
|
Chris@18
|
52 *
|
Chris@18
|
53 * @var \Drupal\workspaces\WorkspaceManagerInterface
|
Chris@18
|
54 */
|
Chris@18
|
55 protected $workspaceManager;
|
Chris@18
|
56
|
Chris@18
|
57 /**
|
Chris@17
|
58 * Constructs a new WorkspacePublisher.
|
Chris@17
|
59 *
|
Chris@17
|
60 * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
Chris@17
|
61 * The entity type manager.
|
Chris@17
|
62 * @param \Drupal\Core\Database\Connection $database
|
Chris@17
|
63 * Database connection.
|
Chris@18
|
64 * @param \Drupal\workspaces\WorkspaceManagerInterface $workspace_manager
|
Chris@18
|
65 * The workspace manager.
|
Chris@17
|
66 */
|
Chris@18
|
67 public function __construct(EntityTypeManagerInterface $entity_type_manager, Connection $database, WorkspaceManagerInterface $workspace_manager, WorkspaceInterface $source) {
|
Chris@17
|
68 $this->entityTypeManager = $entity_type_manager;
|
Chris@17
|
69 $this->database = $database;
|
Chris@17
|
70 $this->workspaceAssociationStorage = $entity_type_manager->getStorage('workspace_association');
|
Chris@18
|
71 $this->workspaceManager = $workspace_manager;
|
Chris@17
|
72 $this->sourceWorkspace = $source;
|
Chris@17
|
73 $this->targetWorkspace = $this->entityTypeManager->getStorage('workspace')->load(WorkspaceInterface::DEFAULT_WORKSPACE);
|
Chris@17
|
74 }
|
Chris@17
|
75
|
Chris@17
|
76 /**
|
Chris@17
|
77 * {@inheritdoc}
|
Chris@17
|
78 */
|
Chris@17
|
79 public function publish() {
|
Chris@17
|
80 if ($this->checkConflictsOnTarget()) {
|
Chris@17
|
81 throw new WorkspaceConflictException();
|
Chris@17
|
82 }
|
Chris@17
|
83
|
Chris@17
|
84 $transaction = $this->database->startTransaction();
|
Chris@17
|
85 try {
|
Chris@17
|
86 // @todo Handle the publishing of a workspace with a batch operation in
|
Chris@17
|
87 // https://www.drupal.org/node/2958752.
|
Chris@18
|
88 $this->workspaceManager->executeInWorkspace($this->targetWorkspace->id(), function () {
|
Chris@18
|
89 foreach ($this->getDifferringRevisionIdsOnSource() as $entity_type_id => $revision_difference) {
|
Chris@18
|
90
|
Chris@18
|
91 $entity_revisions = $this->entityTypeManager->getStorage($entity_type_id)
|
Chris@18
|
92 ->loadMultipleRevisions(array_keys($revision_difference));
|
Chris@18
|
93 $default_revisions = $this->entityTypeManager->getStorage($entity_type_id)
|
Chris@18
|
94 ->loadMultiple(array_values($revision_difference));
|
Chris@18
|
95
|
Chris@18
|
96 /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
|
Chris@18
|
97 foreach ($entity_revisions as $entity) {
|
Chris@18
|
98 // When pushing workspace-specific revisions to the default
|
Chris@18
|
99 // workspace (Live), we simply need to mark them as default
|
Chris@18
|
100 // revisions.
|
Chris@18
|
101 $entity->setSyncing(TRUE);
|
Chris@18
|
102 $entity->isDefaultRevision(TRUE);
|
Chris@18
|
103 $entity->original = $default_revisions[$entity->id()];
|
Chris@18
|
104 $entity->save();
|
Chris@18
|
105 }
|
Chris@17
|
106 }
|
Chris@18
|
107 });
|
Chris@17
|
108 }
|
Chris@17
|
109 catch (\Exception $e) {
|
Chris@17
|
110 $transaction->rollBack();
|
Chris@17
|
111 watchdog_exception('workspaces', $e);
|
Chris@17
|
112 throw $e;
|
Chris@17
|
113 }
|
Chris@17
|
114
|
Chris@17
|
115 // Notify the workspace association storage that a workspace has been
|
Chris@17
|
116 // pushed.
|
Chris@17
|
117 $this->workspaceAssociationStorage->postPush($this->sourceWorkspace);
|
Chris@17
|
118 }
|
Chris@17
|
119
|
Chris@17
|
120 /**
|
Chris@17
|
121 * {@inheritdoc}
|
Chris@17
|
122 */
|
Chris@17
|
123 public function getSourceLabel() {
|
Chris@17
|
124 return $this->sourceWorkspace->label();
|
Chris@17
|
125 }
|
Chris@17
|
126
|
Chris@17
|
127 /**
|
Chris@17
|
128 * {@inheritdoc}
|
Chris@17
|
129 */
|
Chris@17
|
130 public function getTargetLabel() {
|
Chris@17
|
131 return $this->targetWorkspace->label();
|
Chris@17
|
132 }
|
Chris@17
|
133
|
Chris@17
|
134 /**
|
Chris@17
|
135 * {@inheritdoc}
|
Chris@17
|
136 */
|
Chris@17
|
137 public function checkConflictsOnTarget() {
|
Chris@17
|
138 // Nothing to do for now, we can not get to a conflicting state because an
|
Chris@17
|
139 // entity which is being edited in a workspace can not be edited in any
|
Chris@17
|
140 // other workspace.
|
Chris@17
|
141 }
|
Chris@17
|
142
|
Chris@17
|
143 /**
|
Chris@17
|
144 * {@inheritdoc}
|
Chris@17
|
145 */
|
Chris@17
|
146 public function getDifferringRevisionIdsOnTarget() {
|
Chris@17
|
147 $target_revision_difference = [];
|
Chris@17
|
148
|
Chris@17
|
149 $tracked_entities = $this->workspaceAssociationStorage->getTrackedEntities($this->sourceWorkspace->id());
|
Chris@17
|
150 foreach ($tracked_entities as $entity_type_id => $tracked_revisions) {
|
Chris@17
|
151 $entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
|
Chris@17
|
152
|
Chris@17
|
153 // Get the latest revision IDs for all the entities that are tracked by
|
Chris@17
|
154 // the source workspace.
|
Chris@17
|
155 $query = $this->entityTypeManager
|
Chris@17
|
156 ->getStorage($entity_type_id)
|
Chris@17
|
157 ->getQuery()
|
Chris@17
|
158 ->condition($entity_type->getKey('id'), $tracked_revisions, 'IN')
|
Chris@17
|
159 ->latestRevision();
|
Chris@17
|
160 $result = $query->execute();
|
Chris@17
|
161
|
Chris@17
|
162 // Now we compare the revision IDs which are tracked by the source
|
Chris@17
|
163 // workspace to the latest revision IDs of those entities and the
|
Chris@17
|
164 // difference between these two arrays gives us all the entities which
|
Chris@17
|
165 // have been modified on the target.
|
Chris@17
|
166 if ($revision_difference = array_diff_key($result, $tracked_revisions)) {
|
Chris@17
|
167 $target_revision_difference[$entity_type_id] = $revision_difference;
|
Chris@17
|
168 }
|
Chris@17
|
169 }
|
Chris@17
|
170
|
Chris@17
|
171 return $target_revision_difference;
|
Chris@17
|
172 }
|
Chris@17
|
173
|
Chris@17
|
174 /**
|
Chris@17
|
175 * {@inheritdoc}
|
Chris@17
|
176 */
|
Chris@17
|
177 public function getDifferringRevisionIdsOnSource() {
|
Chris@17
|
178 // Get the Workspace association revisions which haven't been pushed yet.
|
Chris@17
|
179 return $this->workspaceAssociationStorage->getTrackedEntities($this->sourceWorkspace->id());
|
Chris@17
|
180 }
|
Chris@17
|
181
|
Chris@17
|
182 /**
|
Chris@17
|
183 * {@inheritdoc}
|
Chris@17
|
184 */
|
Chris@17
|
185 public function getNumberOfChangesOnTarget() {
|
Chris@17
|
186 $total_changes = $this->getDifferringRevisionIdsOnTarget();
|
Chris@17
|
187 return count($total_changes, COUNT_RECURSIVE) - count($total_changes);
|
Chris@17
|
188 }
|
Chris@17
|
189
|
Chris@17
|
190 /**
|
Chris@17
|
191 * {@inheritdoc}
|
Chris@17
|
192 */
|
Chris@17
|
193 public function getNumberOfChangesOnSource() {
|
Chris@17
|
194 $total_changes = $this->getDifferringRevisionIdsOnSource();
|
Chris@17
|
195 return count($total_changes, COUNT_RECURSIVE) - count($total_changes);
|
Chris@17
|
196 }
|
Chris@17
|
197
|
Chris@17
|
198 }
|