[TASK] Re-work/simplify copyright header in PHP files - Part 2
[Packages/TYPO3.CMS.git] / typo3 / sysext / version / Classes / DataHandler / CommandMap.php
1 <?php
2 namespace TYPO3\CMS\Version\DataHandler;
3
4 /**
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Utility\GeneralUtility;
18 use TYPO3\CMS\Backend\Utility\BackendUtility;
19 use TYPO3\CMS\Version\Dependency\ElementEntity;
20
21 /**
22 * Handles the \TYPO3\CMS\Core\DataHandling\DataHandler command map and is
23 * only used in combination with \TYPO3\CMS\Core\DataHandling\DataHandler
24 */
25 class CommandMap {
26
27 const SCOPE_WorkspacesSwap = 'SCOPE_WorkspacesSwap';
28 const SCOPE_WorkspacesSetStage = 'SCOPE_WorkspacesSetStage';
29 const SCOPE_WorkspacesClear = 'SCOPE_WorkspacesClear';
30 const KEY_ScopeErrorMessage = 'KEY_ScopeErrorMessage';
31 const KEY_ScopeErrorCode = 'KEY_ScopeErrorCode';
32 const KEY_GetElementPropertiesCallback = 'KEY_GetElementPropertiesCallback';
33 const KEY_GetCommonPropertiesCallback = 'KEY_GetCommonPropertiesCallback';
34 const KEY_ElementConstructCallback = 'KEY_EventConstructCallback';
35 const KEY_ElementCreateChildReferenceCallback = 'KEY_ElementCreateChildReferenceCallback';
36 const KEY_ElementCreateParentReferenceCallback = 'KEY_ElementCreateParentReferenceCallback';
37 const KEY_PurgeWithErrorMessageGetIdCallback = 'KEY_PurgeWithErrorMessageGetIdCallback';
38 const KEY_UpdateGetIdCallback = 'KEY_UpdateGetIdCallback';
39 const KEY_TransformDependentElementsToUseLiveId = 'KEY_TransformDependentElementsToUseLiveId';
40 /**
41 * @var \TYPO3\CMS\Version\Hook\DataHandlerHook
42 */
43 protected $parent;
44
45 /**
46 * @var \TYPO3\CMS\Core\DataHandling\DataHandler
47 */
48 protected $tceMain;
49
50 /**
51 * @var array
52 */
53 protected $commandMap = array();
54
55 /**
56 * @var int
57 */
58 protected $workspace;
59
60 /**
61 * @var string
62 */
63 protected $workspacesSwapMode;
64
65 /**
66 * @var string
67 */
68 protected $workspacesChangeStageMode;
69
70 /**
71 * @var boolean
72 */
73 protected $workspacesConsiderReferences;
74
75 /**
76 * @var array
77 */
78 protected $scopes;
79
80 /**
81 * @var \TYPO3\CMS\Version\Dependency\ElementEntityProcessor
82 */
83 protected $elementEntityProcessor;
84
85 /**
86 * Creates this object.
87 *
88 * @param \TYPO3\CMS\Version\Hook\DataHandlerHook $parent
89 * @param \TYPO3\CMS\Core\DataHandling\DataHandler $tceMain
90 * @param array $commandMap
91 * @param int $workspace
92 */
93 public function __construct(\TYPO3\CMS\Version\Hook\DataHandlerHook $parent, \TYPO3\CMS\Core\DataHandling\DataHandler $tceMain, array $commandMap, $workspace) {
94 $this->setParent($parent);
95 $this->setTceMain($tceMain);
96 $this->set($commandMap);
97 $this->setWorkspace($workspace);
98 $this->setWorkspacesSwapMode($this->getTceMain()->BE_USER->getTSConfigVal('options.workspaces.swapMode'));
99 $this->setWorkspacesChangeStageMode($this->getTceMain()->BE_USER->getTSConfigVal('options.workspaces.changeStageMode'));
100 $this->setWorkspacesConsiderReferences($this->getTceMain()->BE_USER->getTSConfigVal('options.workspaces.considerReferences'));
101 $this->constructScopes();
102 }
103
104 /**
105 * Gets the command map.
106 *
107 * @return array
108 */
109 public function get() {
110 return $this->commandMap;
111 }
112
113 /**
114 * Sets the command map.
115 *
116 * @param array $commandMap
117 * @return \TYPO3\CMS\Version\DataHandler\CommandMap
118 */
119 public function set(array $commandMap) {
120 $this->commandMap = $commandMap;
121 return $this;
122 }
123
124 /**
125 * Gets the parent object.
126 *
127 * @return \TYPO3\CMS\Version\Hook\DataHandlerHook
128 */
129 public function getParent() {
130 return $this->parent;
131 }
132
133 /**
134 * Sets the parent object.
135 *
136 * @param \TYPO3\CMS\Version\Hook\DataHandlerHook $parent
137 * @return \TYPO3\CMS\Version\DataHandler\CommandMap
138 */
139 public function setParent(\TYPO3\CMS\Version\Hook\DataHandlerHook $parent) {
140 $this->parent = $parent;
141 return $this;
142 }
143
144 /**
145 * Gets the parent object.
146 *
147 * @return \TYPO3\CMS\Core\DataHandling\DataHandler
148 */
149 public function getTceMain() {
150 return $this->tceMain;
151 }
152
153 /**
154 * Sets the parent object.
155 *
156 * @param \TYPO3\CMS\Core\DataHandling\DataHandler $tceMain
157 * @return \TYPO3\CMS\Version\DataHandler\CommandMap
158 */
159 public function setTceMain(\TYPO3\CMS\Core\DataHandling\DataHandler $tceMain) {
160 $this->tceMain = $tceMain;
161 return $this;
162 }
163
164 /**
165 * Sets the current workspace.
166 *
167 * @param int $workspace
168 */
169 public function setWorkspace($workspace) {
170 $this->workspace = (int)$workspace;
171 }
172
173 /**
174 * Gets the current workspace.
175 *
176 * @return int
177 */
178 public function getWorkspace() {
179 return $this->workspace;
180 }
181
182 /**
183 * Sets the workspaces swap mode
184 * (see options.workspaces.swapMode).
185 *
186 * @param string $workspacesSwapMode
187 * @return \TYPO3\CMS\Version\DataHandler\CommandMap
188 */
189 public function setWorkspacesSwapMode($workspacesSwapMode) {
190 $this->workspacesSwapMode = (string) $workspacesSwapMode;
191 return $this;
192 }
193
194 /**
195 * Sets the workspaces change stage mode
196 * see options.workspaces.changeStageMode)
197 *
198 * @param string $workspacesChangeStageMode
199 * @return \TYPO3\CMS\Version\DataHandler\CommandMap
200 */
201 public function setWorkspacesChangeStageMode($workspacesChangeStageMode) {
202 $this->workspacesChangeStageMode = (string) $workspacesChangeStageMode;
203 return $this;
204 }
205
206 /**
207 * Sets the workspace behaviour to automatically consider references
208 * (see options.workspaces.considerReferences)
209 *
210 * @param boolean $workspacesConsiderReferences
211 * @return \TYPO3\CMS\Version\DataHandler\CommandMap
212 */
213 public function setWorkspacesConsiderReferences($workspacesConsiderReferences) {
214 $this->workspacesConsiderReferences = (bool) $workspacesConsiderReferences;
215 return $this;
216 }
217
218 /**
219 * Gets the element entity processor.
220 *
221 * @return \TYPO3\CMS\Version\Dependency\ElementEntityProcessor
222 */
223 protected function getElementEntityProcessor() {
224 if (!isset($this->elementEntityProcessor)) {
225 $this->elementEntityProcessor = GeneralUtility::makeInstance(
226 'TYPO3\\CMS\\Version\\Dependency\\ElementEntityProcessor'
227 );
228 $this->elementEntityProcessor->setWorkspace($this->getWorkspace());
229 }
230 return $this->elementEntityProcessor;
231 }
232
233 /**
234 * Processes the command map.
235 *
236 * @return \TYPO3\CMS\Version\DataHandler\CommandMap
237 */
238 public function process() {
239 $this->resolveWorkspacesSwapDependencies();
240 $this->resolveWorkspacesSetStageDependencies();
241 $this->resolveWorkspacesClearDependencies();
242 return $this;
243 }
244
245 /**
246 * Invokes all items for swapping/publishing with a callback method.
247 *
248 * @param string $callbackMethod
249 * @param array $arguments Optional leading arguments for the callback method
250 * @return void
251 */
252 protected function invokeWorkspacesSwapItems($callbackMethod, array $arguments = array()) {
253 // Traverses the cmd[] array and fetches the accordant actions:
254 foreach ($this->commandMap as $table => $liveIdCollection) {
255 foreach ($liveIdCollection as $liveId => $commandCollection) {
256 foreach ($commandCollection as $command => $properties) {
257 if ($command === 'version' && isset($properties['action']) && $properties['action'] === 'swap') {
258 if (isset($properties['swapWith']) && \TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($properties['swapWith'])) {
259 call_user_func_array(array($this, $callbackMethod), array_merge($arguments, array($table, $liveId, $properties)));
260 }
261 }
262 }
263 }
264 }
265 }
266
267 /**
268 * Resolves workspaces related dependencies for swapping/publishing of the command map.
269 * Workspaces records that have children or (relative) parents which are versionized
270 * but not published with this request, are removed from the command map. Otherwise
271 * this would produce hanging record sets and lost references.
272 *
273 * @return void
274 */
275 protected function resolveWorkspacesSwapDependencies() {
276 $scope = self::SCOPE_WorkspacesSwap;
277 $dependency = $this->getDependencyUtility($scope);
278 if (GeneralUtility::inList('any,pages', $this->workspacesSwapMode)) {
279 $this->invokeWorkspacesSwapItems('applyWorkspacesSwapBehaviour');
280 }
281 $this->invokeWorkspacesSwapItems('addWorkspacesSwapElements', array($dependency));
282 $this->applyWorkspacesDependencies($dependency, $scope);
283 }
284
285 /**
286 * Applies workspaces behaviour for swapping/publishing and takes care of the swapMode.
287 *
288 * @param string $table
289 * @param integer $liveId
290 * @param array $properties
291 * @return void
292 */
293 protected function applyWorkspacesSwapBehaviour($table, $liveId, array $properties) {
294 $extendedCommandMap = array();
295 $elementList = array();
296 // Fetch accordant elements if the swapMode is 'any' or 'pages':
297 if ($this->workspacesSwapMode === 'any' || $this->workspacesSwapMode === 'pages' && $table === 'pages') {
298 $elementList = $this->getParent()->findPageElementsForVersionSwap($table, $liveId, $properties['swapWith']);
299 }
300 foreach ($elementList as $elementTable => $elementIdArray) {
301 foreach ($elementIdArray as $elementIds) {
302 $extendedCommandMap[$elementTable][$elementIds[0]]['version'] = array_merge($properties, array('swapWith' => $elementIds[1]));
303 }
304 }
305 if (count($elementList) > 0) {
306 $this->remove($table, $liveId, 'version');
307 $this->mergeToBottom($extendedCommandMap);
308 }
309 }
310
311 /**
312 * Adds workspaces elements for swapping/publishing.
313 *
314 * @param \TYPO3\CMS\Version\Dependency\DependencyResolver $dependency
315 * @param string $table
316 * @param integer $liveId
317 * @param array $properties
318 * @return void
319 */
320 protected function addWorkspacesSwapElements(\TYPO3\CMS\Version\Dependency\DependencyResolver $dependency, $table, $liveId, array $properties) {
321 $elementList = array();
322 // Fetch accordant elements if the swapMode is 'any' or 'pages':
323 if ($this->workspacesSwapMode === 'any' || $this->workspacesSwapMode === 'pages' && $table === 'pages') {
324 $elementList = $this->getParent()->findPageElementsForVersionSwap($table, $liveId, $properties['swapWith']);
325 }
326 foreach ($elementList as $elementTable => $elementIdArray) {
327 foreach ($elementIdArray as $elementIds) {
328 $dependency->addElement($elementTable, $elementIds[1], array('liveId' => $elementIds[0], 'properties' => array_merge($properties, array('swapWith' => $elementIds[1]))));
329 }
330 }
331 if (count($elementList) === 0) {
332 $dependency->addElement($table, $properties['swapWith'], array('liveId' => $liveId, 'properties' => $properties));
333 }
334 }
335
336 /**
337 * Invokes all items for staging with a callback method.
338 *
339 * @param string $callbackMethod
340 * @param array $arguments Optional leading arguments for the callback method
341 * @return void
342 */
343 protected function invokeWorkspacesSetStageItems($callbackMethod, array $arguments = array()) {
344 // Traverses the cmd[] array and fetches the accordant actions:
345 foreach ($this->commandMap as $table => $versionIdCollection) {
346 foreach ($versionIdCollection as $versionIdList => $commandCollection) {
347 foreach ($commandCollection as $command => $properties) {
348 if ($command === 'version' && isset($properties['action']) && $properties['action'] === 'setStage') {
349 if (isset($properties['stageId']) && \TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($properties['stageId'])) {
350 call_user_func_array(array($this, $callbackMethod), array_merge($arguments, array($table, $versionIdList, $properties)));
351 }
352 }
353 }
354 }
355 }
356 }
357
358 /**
359 * Resolves workspaces related dependencies for staging of the command map.
360 * Workspaces records that have children or (relative) parents which are versionized
361 * but not staged with this request, are removed from the command map.
362 *
363 * @return void
364 */
365 protected function resolveWorkspacesSetStageDependencies() {
366 $scope = self::SCOPE_WorkspacesSetStage;
367 $dependency = $this->getDependencyUtility($scope);
368 if (GeneralUtility::inList('any,pages', $this->workspacesChangeStageMode)) {
369 $this->invokeWorkspacesSetStageItems('applyWorkspacesSetStageBehaviour');
370 }
371 $this->invokeWorkspacesSetStageItems('explodeSetStage');
372 $this->invokeWorkspacesSetStageItems('addWorkspacesSetStageElements', array($dependency));
373 $this->applyWorkspacesDependencies($dependency, $scope);
374 }
375
376 /**
377 * Applies workspaces behaviour for staging and takes care of the changeStageMode.
378 *
379 * @param string $table
380 * @param string $versionIdList
381 * @param array $properties
382 * @return void
383 */
384 protected function applyWorkspacesSetStageBehaviour($table, $versionIdList, array $properties) {
385 $extendedCommandMap = array();
386 $versionIds = GeneralUtility::trimExplode(',', $versionIdList, TRUE);
387 $elementList = array($table => $versionIds);
388 if (GeneralUtility::inList('any,pages', $this->workspacesChangeStageMode)) {
389 if (count($versionIds) === 1) {
390 $workspaceRecord = BackendUtility::getRecord($table, $versionIds[0], 't3ver_wsid');
391 $workspaceId = $workspaceRecord['t3ver_wsid'];
392 } else {
393 $workspaceId = $this->getWorkspace();
394 }
395 if ($table === 'pages') {
396 // Find all elements from the same ws to change stage
397 $livePageIds = $versionIds;
398 $this->getParent()->findRealPageIds($livePageIds);
399 $this->getParent()->findPageElementsForVersionStageChange($livePageIds, $workspaceId, $elementList);
400 } elseif ($this->workspacesChangeStageMode === 'any') {
401 // Find page to change stage:
402 $pageIdList = array();
403 $this->getParent()->findPageIdsForVersionStateChange($table, $versionIds, $workspaceId, $pageIdList, $elementList);
404 // Find other elements from the same ws to change stage:
405 $this->getParent()->findPageElementsForVersionStageChange($pageIdList, $workspaceId, $elementList);
406 }
407 }
408 foreach ($elementList as $elementTable => $elementIds) {
409 foreach ($elementIds as $elementId) {
410 $extendedCommandMap[$elementTable][$elementId]['version'] = $properties;
411 }
412 }
413 $this->remove($table, $versionIds, 'version');
414 $this->mergeToBottom($extendedCommandMap);
415 }
416
417 /**
418 * Adds workspaces elements for staging.
419 *
420 * @param \TYPO3\CMS\Version\Dependency\DependencyResolver $dependency
421 * @param string $table
422 * @param string $versionId
423 * @param array $properties
424 * @return void
425 */
426 protected function addWorkspacesSetStageElements(\TYPO3\CMS\Version\Dependency\DependencyResolver $dependency, $table, $versionId, array $properties) {
427 $dependency->addElement($table, $versionId, array('versionId' => $versionId, 'properties' => $properties));
428 }
429
430 /**
431 * Resolves workspaces related dependencies for clearing/flushing of the command map.
432 * Workspaces records that have children or (relative) parents which are versionized
433 * but not cleared/flushed with this request, are removed from the command map.
434 *
435 * @return void
436 */
437 protected function resolveWorkspacesClearDependencies() {
438 $scope = self::SCOPE_WorkspacesClear;
439 $dependency = $this->getDependencyUtility($scope);
440 // Traverses the cmd[] array and fetches the accordant actions:
441 foreach ($this->commandMap as $table => $versionIdCollection) {
442 foreach ($versionIdCollection as $versionId => $commandCollection) {
443 foreach ($commandCollection as $command => $properties) {
444 if ($command === 'version' && isset($properties['action']) && ($properties['action'] === 'clearWSID' || $properties['action'] === 'flush')) {
445 $dependency->addElement($table, $versionId, array('versionId' => $versionId, 'properties' => $properties));
446 }
447 }
448 }
449 }
450 $this->applyWorkspacesDependencies($dependency, $scope);
451 }
452
453 /**
454 * Explodes id-lists in the command map for staging actions.
455 *
456 * @throws \RuntimeException
457 * @param string $table
458 * @param string $versionIdList
459 * @param array $properties
460 * @return void
461 */
462 protected function explodeSetStage($table, $versionIdList, array $properties) {
463 $extractedCommandMap = array();
464 $versionIds = GeneralUtility::trimExplode(',', $versionIdList, TRUE);
465 if (count($versionIds) > 1) {
466 foreach ($versionIds as $versionId) {
467 if (isset($this->commandMap[$table][$versionId]['version'])) {
468 throw new \RuntimeException('Command map for [' . $table . '][' . $versionId . '][version] was already set.', 1289391048);
469 }
470 $extractedCommandMap[$table][$versionId]['version'] = $properties;
471 }
472 $this->remove($table, $versionIdList, 'version');
473 $this->mergeToBottom($extractedCommandMap);
474 }
475 }
476
477 /**
478 * Applies the workspaces dependencies and removes incomplete structures or automatically
479 * completes them, depending on the options.workspaces.considerReferences setting
480 *
481 * @param \TYPO3\CMS\Version\Dependency\DependencyResolver $dependency
482 * @param string $scope
483 * @return void
484 */
485 protected function applyWorkspacesDependencies(\TYPO3\CMS\Version\Dependency\DependencyResolver $dependency, $scope) {
486 $transformDependentElementsToUseLiveId = $this->getScopeData($scope, self::KEY_TransformDependentElementsToUseLiveId);
487 $elementsToBeVersioned = $dependency->getElements();
488 // Use the uid of the live record instead of the workspace record:
489 if ($transformDependentElementsToUseLiveId) {
490 $elementsToBeVersioned = $this->getElementEntityProcessor()->transformDependentElementsToUseLiveId($elementsToBeVersioned);
491 }
492 $outerMostParents = $dependency->getOuterMostParents();
493 /** @var $outerMostParent ElementEntity */
494 foreach ($outerMostParents as $outerMostParent) {
495 $dependentElements = $dependency->getNestedElements($outerMostParent);
496 if ($transformDependentElementsToUseLiveId) {
497 $dependentElements = $this->getElementEntityProcessor()->transformDependentElementsToUseLiveId($dependentElements);
498 }
499 // Gets the difference (intersection) between elements that were submitted by the user
500 // and the evaluation of all dependent records that should be used for this action instead:
501 $intersectingElements = array_intersect_key($dependentElements, $elementsToBeVersioned);
502 if (count($intersectingElements) > 0) {
503 // If at least one element intersects but not all, throw away all elements of the depdendent structure:
504 if (count($intersectingElements) !== count($dependentElements) && $this->workspacesConsiderReferences === FALSE) {
505 $this->purgeWithErrorMessage($intersectingElements, $scope);
506 } else {
507 $this->update(current($intersectingElements), $dependentElements, $scope);
508 }
509 }
510 }
511 }
512
513 /**
514 * Purges incomplete structures from the command map and triggers an error message.
515 *
516 * @param array $elements
517 * @param string $scope
518 * @return void
519 */
520 protected function purgeWithErrorMessage(array $elements, $scope) {
521 /** @var $element ElementEntity */
522 foreach ($elements as $element) {
523 $table = $element->getTable();
524 $id = $this->processCallback($this->getScopeData($scope, self::KEY_PurgeWithErrorMessageGetIdCallback), array($element));
525 $this->remove($table, $id, 'version');
526 $this->getTceMain()->log($table, $id, 5, 0, 1, $this->getScopeData($scope, self::KEY_ScopeErrorMessage), $this->getScopeData($scope, self::KEY_ScopeErrorCode), array(
527 BackendUtility::getRecordTitle($table, BackendUtility::getRecord($table, $id)),
528 $table,
529 $id
530 ));
531 }
532 }
533
534 /**
535 * Updates the command map accordant to valid structures and takes care of the correct order.
536 *
537 * @param ElementEntity $intersectingElement
538 * @param array $elements
539 * @param string $scope
540 * @return void
541 */
542 protected function update(ElementEntity $intersectingElement, array $elements, $scope) {
543 $orderedCommandMap = array();
544 $commonProperties = array();
545 if ($this->getScopeData($scope, self::KEY_GetCommonPropertiesCallback)) {
546 $commonProperties = $this->processCallback($this->getScopeData($scope, self::KEY_GetCommonPropertiesCallback), array($intersectingElement));
547 }
548 /** @var $element ElementEntity */
549 foreach ($elements as $element) {
550 $table = $element->getTable();
551 $id = $this->processCallback($this->getScopeData($scope, self::KEY_UpdateGetIdCallback), array($element));
552 $this->remove($table, $id, 'version');
553 if ($element->isInvalid()) {
554 continue;
555 }
556 $orderedCommandMap[$table][$id]['version'] = $commonProperties;
557 if ($this->getScopeData($scope, self::KEY_GetElementPropertiesCallback)) {
558 $orderedCommandMap[$table][$id]['version'] = array_merge($commonProperties, $this->processCallback($this->getScopeData($scope, self::KEY_GetElementPropertiesCallback), array($element)));
559 }
560 }
561 // Ensure that ordered command map is on top of the command map:
562 $this->mergeToTop($orderedCommandMap);
563 }
564
565 /**
566 * Merges command map elements to the top of the current command map..
567 *
568 * @param array $commandMap
569 * @return void
570 */
571 protected function mergeToTop(array $commandMap) {
572 \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($commandMap, $this->commandMap);
573 $this->commandMap = $commandMap;
574 }
575
576 /**
577 * Merges command map elements to the bottom of the current command map.
578 *
579 * @param array $commandMap
580 * @return void
581 */
582 protected function mergeToBottom(array $commandMap) {
583 \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($this->commandMap, $commandMap);
584 }
585
586 /**
587 * Removes an element from the command map.
588 *
589 * @param string $table
590 * @param string $id
591 * @param string $command (optional)
592 * @return void
593 */
594 protected function remove($table, $id, $command = NULL) {
595 if (is_string($command)) {
596 unset($this->commandMap[$table][$id][$command]);
597 } else {
598 unset($this->commandMap[$table][$id]);
599 }
600 }
601
602 /**
603 * Callback to get the liveId of an dependent element.
604 *
605 * @param ElementEntity $element
606 * @return integer
607 */
608 protected function getElementLiveIdCallback(ElementEntity $element) {
609 return $element->getDataValue('liveId');
610 }
611
612 /**
613 * Callback to get the real id of an dependent element.
614 *
615 * @param ElementEntity $element
616 * @return integer
617 */
618 protected function getElementIdCallback(ElementEntity $element) {
619 return $element->getId();
620 }
621
622 /**
623 * Callback to get the specific properties of a dependent element for swapping/publishing.
624 *
625 * @param ElementEntity $element
626 * @return array
627 */
628 protected function getElementSwapPropertiesCallback(ElementEntity $element) {
629 return array(
630 'swapWith' => $element->getId()
631 );
632 }
633
634 /**
635 * Callback to get common properties of dependent elements for clearing.
636 *
637 * @param ElementEntity $element
638 * @return array
639 */
640 protected function getCommonClearPropertiesCallback(ElementEntity $element) {
641 $commonSwapProperties = array();
642 $elementProperties = $element->getDataValue('properties');
643 if (isset($elementProperties['action'])) {
644 $commonSwapProperties['action'] = $elementProperties['action'];
645 }
646 return $commonSwapProperties;
647 }
648
649 /**
650 * Callback to get common properties of dependent elements for swapping/publishing.
651 *
652 * @param ElementEntity $element
653 * @return array
654 */
655 protected function getCommonSwapPropertiesCallback(ElementEntity $element) {
656 $commonSwapProperties = array();
657 $elementProperties = $element->getDataValue('properties');
658 if (isset($elementProperties['action'])) {
659 $commonSwapProperties['action'] = $elementProperties['action'];
660 }
661 if (isset($elementProperties['swapIntoWS'])) {
662 $commonSwapProperties['swapIntoWS'] = $elementProperties['swapIntoWS'];
663 }
664 if (isset($elementProperties['comment'])) {
665 $commonSwapProperties['comment'] = $elementProperties['comment'];
666 }
667 if (isset($elementProperties['notificationAlternativeRecipients'])) {
668 $commonSwapProperties['notificationAlternativeRecipients'] = $elementProperties['notificationAlternativeRecipients'];
669 }
670
671 return $commonSwapProperties;
672 }
673
674 /**
675 * Callback to get the specific properties of a dependent element for staging.
676 *
677 * @param ElementEntity $element
678 * @return array
679 */
680 protected function getElementSetStagePropertiesCallback(ElementEntity $element) {
681 return $this->getCommonSetStagePropertiesCallback($element);
682 }
683
684 /**
685 * Callback to get common properties of dependent elements for staging.
686 *
687 * @param ElementEntity $element
688 * @return array
689 */
690 protected function getCommonSetStagePropertiesCallback(ElementEntity $element) {
691 $commonSetStageProperties = array();
692 $elementProperties = $element->getDataValue('properties');
693 if (isset($elementProperties['stageId'])) {
694 $commonSetStageProperties['stageId'] = $elementProperties['stageId'];
695 }
696 if (isset($elementProperties['comment'])) {
697 $commonSetStageProperties['comment'] = $elementProperties['comment'];
698 }
699 if (isset($elementProperties['action'])) {
700 $commonSetStageProperties['action'] = $elementProperties['action'];
701 }
702 if (isset($elementProperties['notificationAlternativeRecipients'])) {
703 $commonSetStageProperties['notificationAlternativeRecipients'] = $elementProperties['notificationAlternativeRecipients'];
704 }
705 return $commonSetStageProperties;
706 }
707
708 /**
709 * Gets an instance of the depency resolver utility.
710 *
711 * @param string $scope Scope identifier
712 * @return \TYPO3\CMS\Version\Dependency\DependencyResolver
713 */
714 protected function getDependencyUtility($scope) {
715 /** @var $dependency \TYPO3\CMS\Version\Dependency\DependencyResolver */
716 $dependency = GeneralUtility::makeInstance('TYPO3\\CMS\\Version\\Dependency\\DependencyResolver');
717 $dependency->setWorkspace($this->getWorkspace());
718 $dependency->setOuterMostParentsRequireReferences(TRUE);
719 if ($this->getScopeData($scope, self::KEY_ElementConstructCallback)) {
720 $dependency->setEventCallback(ElementEntity::EVENT_Construct, $this->getDependencyCallback($this->getScopeData($scope, self::KEY_ElementConstructCallback)));
721 }
722 if ($this->getScopeData($scope, self::KEY_ElementCreateChildReferenceCallback)) {
723 $dependency->setEventCallback(ElementEntity::EVENT_CreateChildReference, $this->getDependencyCallback($this->getScopeData($scope, self::KEY_ElementCreateChildReferenceCallback)));
724 }
725 if ($this->getScopeData($scope, self::KEY_ElementCreateParentReferenceCallback)) {
726 $dependency->setEventCallback(ElementEntity::EVENT_CreateParentReference, $this->getDependencyCallback($this->getScopeData($scope, self::KEY_ElementCreateParentReferenceCallback)));
727 }
728 return $dependency;
729 }
730
731 /**
732 * Constructs the scope settings.
733 * Currently the scopes for swapping/publishing and staging are available.
734 *
735 * @return void
736 */
737 protected function constructScopes() {
738 $this->scopes = array(
739 // settings for publishing and swapping:
740 self::SCOPE_WorkspacesSwap => array(
741 // error message and error code
742 self::KEY_ScopeErrorMessage => 'Record "%s" (%s:%s) cannot be swapped or published independently, because it is related to other new or modified records.',
743 self::KEY_ScopeErrorCode => 1288283630,
744 // callback functons used to modify the commandMap
745 // + element properties are specific for each element
746 // + common properties are the same for all elements
747 self::KEY_GetElementPropertiesCallback => 'getElementSwapPropertiesCallback',
748 self::KEY_GetCommonPropertiesCallback => 'getCommonSwapPropertiesCallback',
749 // callback function used, when a new element to be checked is added
750 self::KEY_ElementConstructCallback => 'createNewDependentElementCallback',
751 // callback function used to determine whether an element is a valid child or parent reference (e.g. IRRE)
752 self::KEY_ElementCreateChildReferenceCallback => 'createNewDependentElementChildReferenceCallback',
753 self::KEY_ElementCreateParentReferenceCallback => 'createNewDependentElementParentReferenceCallback',
754 // callback function used to get the correct record uid to be used in the error message
755 self::KEY_PurgeWithErrorMessageGetIdCallback => 'getElementLiveIdCallback',
756 // callback function used to fetch the correct record uid on modifying the commandMap
757 self::KEY_UpdateGetIdCallback => 'getElementLiveIdCallback',
758 // setting whether to use the uid of the live record instead of the workspace record
759 self::KEY_TransformDependentElementsToUseLiveId => TRUE
760 ),
761 // settings for modifying the stage:
762 self::SCOPE_WorkspacesSetStage => array(
763 // error message and error code
764 self::KEY_ScopeErrorMessage => 'Record "%s" (%s:%s) cannot be sent to another stage independently, because it is related to other new or modified records.',
765 self::KEY_ScopeErrorCode => 1289342524,
766 // callback functons used to modify the commandMap
767 // + element properties are specific for each element
768 // + common properties are the same for all elements
769 self::KEY_GetElementPropertiesCallback => 'getElementSetStagePropertiesCallback',
770 self::KEY_GetCommonPropertiesCallback => 'getCommonSetStagePropertiesCallback',
771 // callback function used, when a new element to be checked is added
772 self::KEY_ElementConstructCallback => NULL,
773 // callback function used to determine whether an element is a valid child or parent reference (e.g. IRRE)
774 self::KEY_ElementCreateChildReferenceCallback => 'createNewDependentElementChildReferenceCallback',
775 self::KEY_ElementCreateParentReferenceCallback => 'createNewDependentElementParentReferenceCallback',
776 // callback function used to get the correct record uid to be used in the error message
777 self::KEY_PurgeWithErrorMessageGetIdCallback => 'getElementIdCallback',
778 // callback function used to fetch the correct record uid on modifying the commandMap
779 self::KEY_UpdateGetIdCallback => 'getElementIdCallback',
780 // setting whether to use the uid of the live record instead of the workspace record
781 self::KEY_TransformDependentElementsToUseLiveId => FALSE
782 ),
783 // settings for clearing and flushing:
784 self::SCOPE_WorkspacesClear => array(
785 // error message and error code
786 self::KEY_ScopeErrorMessage => 'Record "%s" (%s:%s) cannot be flushed independently, because it is related to other new or modified records.',
787 self::KEY_ScopeErrorCode => 1300467990,
788 // callback functons used to modify the commandMap
789 // + element properties are specific for each element
790 // + common properties are the same for all elements
791 self::KEY_GetElementPropertiesCallback => NULL,
792 self::KEY_GetCommonPropertiesCallback => 'getCommonClearPropertiesCallback',
793 // callback function used, when a new element to be checked is added
794 self::KEY_ElementConstructCallback => NULL,
795 // callback function used to determine whether an element is a valid child or parent reference (e.g. IRRE)
796 self::KEY_ElementCreateChildReferenceCallback => 'createClearDependentElementChildReferenceCallback',
797 self::KEY_ElementCreateParentReferenceCallback => 'createClearDependentElementParentReferenceCallback',
798 // callback function used to get the correct record uid to be used in the error message
799 self::KEY_PurgeWithErrorMessageGetIdCallback => 'getElementIdCallback',
800 // callback function used to fetch the correct record uid on modifying the commandMap
801 self::KEY_UpdateGetIdCallback => 'getElementIdCallback',
802 // setting whether to use the uid of the live record instead of the workspace record
803 self::KEY_TransformDependentElementsToUseLiveId => FALSE
804 )
805 );
806 }
807
808 /**
809 * Gets data for a particular scope.
810 *
811 * @throws \RuntimeException
812 * @param string $scope Scope identifier
813 * @param string $key
814 * @return string
815 */
816 protected function getScopeData($scope, $key) {
817 if (!isset($this->scopes[$scope])) {
818 throw new \RuntimeException('Scope "' . $scope . '" is not defined.', 1289342187);
819 }
820 return $this->scopes[$scope][$key];
821 }
822
823 /**
824 * Gets a new callback to be used in the dependency resolver utility.
825 *
826 * @param string $method
827 * @param array $targetArguments
828 * @return \TYPO3\CMS\Version\Dependency\EventCallback
829 */
830 protected function getDependencyCallback($method, array $targetArguments = array()) {
831 return GeneralUtility::makeInstance(
832 'TYPO3\\CMS\\Version\\Dependency\\EventCallback',
833 $this->getElementEntityProcessor(), $method, $targetArguments
834 );
835 }
836
837 /**
838 * Processes a local callback inside this object.
839 *
840 * @param string $method
841 * @param array $callbackArguments
842 * @return mixed
843 */
844 protected function processCallback($method, array $callbackArguments) {
845 return call_user_func_array(array($this, $method), $callbackArguments);
846 }
847
848 }