[TASK] Refactor element dependency resolving
[Packages/TYPO3.CMS.git] / typo3 / sysext / version / Classes / Dependency / ElementEntityProcessor.php
1 <?php
2 namespace TYPO3\CMS\Version\Dependency;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2014 Oliver Hader <oliver@typo3.org>
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 * A copy is found in the text file GPL.txt and important notices to the license
19 * from the author is found in LICENSE.txt distributed with these scripts.
20 *
21 *
22 * This script is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * This copyright notice MUST APPEAR in all copies of the script!
28 ***************************************************************/
29
30 use TYPO3\CMS\Core\Utility\GeneralUtility;
31 use TYPO3\CMS\Backend\Utility\BackendUtility;
32 use TYPO3\CMS\Core\Versioning\VersionState;
33
34 /**
35 * Processor having generic callback methods for element entities
36 */
37 class ElementEntityProcessor {
38
39 /**
40 * @var int
41 */
42 protected $workspace;
43
44 /**
45 * @var \TYPO3\CMS\Core\DataHandling\DataHandler
46 */
47 protected $dataHandler;
48
49 /**
50 * Sets the current workspace.
51 *
52 * @param int $workspace
53 */
54 public function setWorkspace($workspace) {
55 $this->workspace = (int)$workspace;
56 }
57
58 /**
59 * Gets the current workspace.
60 *
61 * @return int
62 */
63 public function getWorkspace() {
64 return $this->workspace;
65 }
66
67 /**
68 * @return \TYPO3\CMS\Core\DataHandling\DataHandler
69 */
70 public function getDataHandler() {
71 if (!isset($this->dataHandler)) {
72 $this->dataHandler = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\DataHandling\\DataHandler');
73 }
74 return $this->dataHandler;
75 }
76
77 /**
78 * Transforms dependent elements to use the liveId as array key.
79 *
80 * @param array|ElementEntity[] $elements
81 * @return array
82 */
83 public function transformDependentElementsToUseLiveId(array $elements) {
84 $transformedElements = array();
85 /** @var $element ElementEntity */
86 foreach ($elements as $element) {
87 $elementName = ElementEntity::getIdentifier($element->getTable(), $element->getDataValue('liveId'));
88 $transformedElements[$elementName] = $element;
89 }
90 return $transformedElements;
91 }
92
93 /**
94 * Callback to determine whether a new child reference shall be considered in the dependency resolver utility.
95 *
96 * @param array $callerArguments
97 * @param array $targetArgument
98 * @param ElementEntity $caller
99 * @param string $eventName
100 * @return NULL|string Skip response (if required)
101 */
102 public function createNewDependentElementChildReferenceCallback(array $callerArguments, array $targetArgument, ElementEntity $caller, $eventName) {
103 $fieldConfiguration = BackendUtility::getTcaFieldConfiguration($caller->getTable(), $callerArguments['field']);
104 if (!$fieldConfiguration || !GeneralUtility::inList('field,list', $this->getDataHandler()->getInlineFieldType($fieldConfiguration))) {
105 return ElementEntity::RESPONSE_Skip;
106 }
107 return NULL;
108 }
109
110 /**
111 * Callback to determine whether a new parent reference shall be considered in the dependency resolver utility.
112 *
113 * @param array $callerArguments
114 * @param array $targetArgument
115 * @param \TYPO3\CMS\Version\Dependency\ElementEntity $caller
116 * @param string $eventName
117 * @return NULL|string Skip response (if required)
118 */
119 public function createNewDependentElementParentReferenceCallback(array $callerArguments, array $targetArgument, ElementEntity $caller, $eventName) {
120 $fieldConfiguration = BackendUtility::getTcaFieldConfiguration($callerArguments['table'], $callerArguments['field']);
121 if (!$fieldConfiguration || !GeneralUtility::inList('field,list', $this->getDataHandler()->getInlineFieldType($fieldConfiguration))) {
122 return ElementEntity::RESPONSE_Skip;
123 }
124 return NULL;
125 }
126
127 /**
128 * Callback to determine whether a new child reference shall be considered in the dependency resolver utility.
129 * Only elements that are a delete placeholder are considered.
130 *
131 * @param array $callerArguments
132 * @param array $targetArgument
133 * @param ElementEntity $caller
134 * @param string $eventName
135 * @return NULL|string Skip response (if required)
136 */
137 public function createClearDependentElementChildReferenceCallback(array $callerArguments, array $targetArgument, ElementEntity $caller, $eventName) {
138 $response = $this->createNewDependentElementChildReferenceCallback($callerArguments, $targetArgument, $caller, $eventName);
139 if (empty($response)) {
140 $record = BackendUtility::getRecord($callerArguments['table'], $callerArguments['id']);
141 if (!VersionState::cast($record['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
142 $response = ElementEntity::RESPONSE_Skip;
143 }
144 }
145 return $response;
146 }
147
148 /**
149 * Callback to determine whether a new parent reference shall be considered in the dependency resolver utility.
150 * Only elements that are a delete placeholder are considered.
151 *
152 * @param array $callerArguments
153 * @param array $targetArgument
154 * @param ElementEntity $caller
155 * @param string $eventName
156 * @return NULL|string Skip response (if required)
157 */
158 public function createClearDependentElementParentReferenceCallback(array $callerArguments, array $targetArgument, ElementEntity $caller, $eventName) {
159 $response = $this->createNewDependentElementParentReferenceCallback($callerArguments, $targetArgument, $caller, $eventName);
160 if (empty($response)) {
161 $record = BackendUtility::getRecord($callerArguments['table'], $callerArguments['id']);
162 if (!VersionState::cast($record['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
163 $response = ElementEntity::RESPONSE_Skip;
164 }
165 }
166 return $response;
167 }
168
169 /**
170 * Callback to add additional data to new elements created in the dependency resolver utility.
171 *
172 * @throws \RuntimeException
173 * @param ElementEntity $caller
174 * @param array $callerArguments
175 * @param array $targetArgument
176 * @param string $eventName
177 * @return void
178 */
179 public function createNewDependentElementCallback(array $callerArguments, array $targetArgument, ElementEntity $caller, $eventName) {
180 $versionRecord = $caller->getRecord();
181 // If version record does not exist, it probably has been deleted (cleared from workspace), this means,
182 // that the reference index still has an old reference pointer, which is "fine" for deleted parents
183 if (empty($versionRecord)) {
184 throw new \RuntimeException(
185 'Element "' . $caller::getIdentifier($caller->getTable(), $caller->getId()) . '" does not exist',
186 1393960943
187 );
188 }
189 // If version is on live workspace, but the pid is negative, mark the record as invalid.
190 // This happens if a change has been discarded (clearWSID) - it will be removed from the command map.
191 if ((int)$versionRecord['t3ver_wsid'] === 0 && (int)$versionRecord['pid'] === -1) {
192 $caller->setDataValue('liveId', $caller->getId());
193 $caller->setInvalid(TRUE);
194 return;
195 }
196 if ($caller->hasDataValue('liveId') === FALSE) {
197 // Set the original uid from the version record
198 if (!empty($versionRecord['t3ver_oid']) && (int)$versionRecord['pid'] === -1 && (int)$versionRecord['t3ver_wsid'] === $this->getWorkspace()) {
199 $caller->setDataValue('liveId', $versionRecord['t3ver_oid']);
200 // The current version record is actually a live record or an accordant placeholder for live
201 } elseif ((int)$versionRecord['t3ver_wsid'] === 0 || (int)$versionRecord['pid'] !== -1) {
202 $caller->setDataValue('liveId', $caller->getId());
203 $versionRecord = BackendUtility::getWorkspaceVersionOfRecord(
204 $this->getWorkspace(),
205 $caller->getTable(),
206 $caller->getId(),
207 'uid,t3ver_state'
208 );
209 // Set version uid to caller, most likely it's a delete placeholder
210 // for a child record that is not recognized in the reference index
211 if (!empty($versionRecord['uid'])) {
212 $caller->setId($versionRecord['uid']);
213 // If no version could be determined, mark record as invalid
214 // (thus, it will be removed from the command map)
215 } else {
216 $caller->setInvalid(TRUE);
217 }
218 // In case of an unexpected record state, mark the record as invalid
219 } else {
220 $caller->setInvalid(TRUE);
221 }
222 }
223 }
224
225 }