419f4969f2ee16dc7cc43d586a551a065e3b16c2
[Packages/TYPO3.CMS.git] / typo3 / sysext / version / Classes / Dependency / ElementEntityProcessor.php
1 <?php
2 namespace TYPO3\CMS\Version\Dependency;
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\Backend\Utility\BackendUtility;
18 use TYPO3\CMS\Core\Utility\GeneralUtility;
19 use TYPO3\CMS\Core\Versioning\VersionState;
20
21 /**
22 * Processor having generic callback methods for element entities
23 */
24 class ElementEntityProcessor
25 {
26 /**
27 * @var int
28 */
29 protected $workspace;
30
31 /**
32 * @var \TYPO3\CMS\Core\DataHandling\DataHandler
33 */
34 protected $dataHandler;
35
36 /**
37 * Sets the current workspace.
38 *
39 * @param int $workspace
40 */
41 public function setWorkspace($workspace)
42 {
43 $this->workspace = (int)$workspace;
44 }
45
46 /**
47 * Gets the current workspace.
48 *
49 * @return int
50 */
51 public function getWorkspace()
52 {
53 return $this->workspace;
54 }
55
56 /**
57 * @return \TYPO3\CMS\Core\DataHandling\DataHandler
58 */
59 public function getDataHandler()
60 {
61 if (!isset($this->dataHandler)) {
62 $this->dataHandler = GeneralUtility::makeInstance(\TYPO3\CMS\Core\DataHandling\DataHandler::class);
63 }
64 return $this->dataHandler;
65 }
66
67 /**
68 * Transforms dependent elements to use the liveId as array key.
69 *
70 * @param array|ElementEntity[] $elements
71 * @return array
72 */
73 public function transformDependentElementsToUseLiveId(array $elements)
74 {
75 $transformedElements = [];
76 /** @var $element ElementEntity */
77 foreach ($elements as $element) {
78 $elementName = ElementEntity::getIdentifier($element->getTable(), $element->getDataValue('liveId'));
79 $transformedElements[$elementName] = $element;
80 }
81 return $transformedElements;
82 }
83
84 /**
85 * Callback to determine whether a new child reference shall be considered in the dependency resolver utility.
86 *
87 * @param array $callerArguments
88 * @param array $targetArgument
89 * @param ElementEntity $caller
90 * @param string $eventName
91 * @return null|string Skip response (if required)
92 */
93 public function createNewDependentElementChildReferenceCallback(array $callerArguments, array $targetArgument, ElementEntity $caller, $eventName)
94 {
95 $fieldConfiguration = BackendUtility::getTcaFieldConfiguration($caller->getTable(), $callerArguments['field']);
96 $inlineFieldType = $this->getDataHandler()->getInlineFieldType($fieldConfiguration);
97 if (!$fieldConfiguration || ($fieldConfiguration['type'] !== 'flex' && $inlineFieldType !== 'field' && $inlineFieldType !== 'list')) {
98 return ElementEntity::RESPONSE_Skip;
99 }
100 return null;
101 }
102
103 /**
104 * Callback to determine whether a new parent reference shall be considered in the dependency resolver utility.
105 *
106 * @param array $callerArguments
107 * @param array $targetArgument
108 * @param \TYPO3\CMS\Version\Dependency\ElementEntity $caller
109 * @param string $eventName
110 * @return null|string Skip response (if required)
111 */
112 public function createNewDependentElementParentReferenceCallback(array $callerArguments, array $targetArgument, ElementEntity $caller, $eventName)
113 {
114 $fieldConfiguration = BackendUtility::getTcaFieldConfiguration($callerArguments['table'], $callerArguments['field']);
115 $inlineFieldType = $this->getDataHandler()->getInlineFieldType($fieldConfiguration);
116 if (!$fieldConfiguration || ($fieldConfiguration['type'] !== 'flex' && $inlineFieldType !== 'field' && $inlineFieldType !== 'list')) {
117 return ElementEntity::RESPONSE_Skip;
118 }
119 return null;
120 }
121
122 /**
123 * Callback to determine whether a new child reference shall be considered in the dependency resolver utility.
124 * Only elements that are a delete placeholder are considered.
125 *
126 * @param array $callerArguments
127 * @param array $targetArgument
128 * @param ElementEntity $caller
129 * @param string $eventName
130 * @return null|string Skip response (if required)
131 */
132 public function createClearDependentElementChildReferenceCallback(array $callerArguments, array $targetArgument, ElementEntity $caller, $eventName)
133 {
134 $response = $this->createNewDependentElementChildReferenceCallback($callerArguments, $targetArgument, $caller, $eventName);
135 if (empty($response)) {
136 $record = BackendUtility::getRecord($callerArguments['table'], $callerArguments['id']);
137 if (!VersionState::cast($record['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
138 $response = ElementEntity::RESPONSE_Skip;
139 }
140 }
141 return $response;
142 }
143
144 /**
145 * Callback to determine whether a new parent reference shall be considered in the dependency resolver utility.
146 * Only elements that are a delete placeholder are considered.
147 *
148 * @param array $callerArguments
149 * @param array $targetArgument
150 * @param ElementEntity $caller
151 * @param string $eventName
152 * @return null|string Skip response (if required)
153 */
154 public function createClearDependentElementParentReferenceCallback(array $callerArguments, array $targetArgument, ElementEntity $caller, $eventName)
155 {
156 $response = $this->createNewDependentElementParentReferenceCallback($callerArguments, $targetArgument, $caller, $eventName);
157 if (empty($response)) {
158 $record = BackendUtility::getRecord($callerArguments['table'], $callerArguments['id']);
159 if (!VersionState::cast($record['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
160 $response = ElementEntity::RESPONSE_Skip;
161 }
162 }
163 return $response;
164 }
165
166 /**
167 * Callback to add additional data to new elements created in the dependency resolver utility.
168 *
169 * @throws \RuntimeException
170 * @param ElementEntity $caller
171 * @param array $callerArguments
172 * @param array $targetArgument
173 * @param string $eventName
174 */
175 public function createNewDependentElementCallback(array $callerArguments, array $targetArgument, ElementEntity $caller, $eventName)
176 {
177 if (!BackendUtility::isTableWorkspaceEnabled($caller->getTable())) {
178 $caller->setInvalid(true);
179 return;
180 }
181
182 $versionRecord = $caller->getRecord();
183 // If version record does not exist, it probably has been deleted (cleared from workspace), this means,
184 // that the reference index still has an old reference pointer, which is "fine" for deleted parents
185 if (empty($versionRecord)) {
186 throw new \RuntimeException(
187 'Element "' . $caller::getIdentifier($caller->getTable(), $caller->getId()) . '" does not exist',
188 1393960943
189 );
190 }
191 // If version is on live workspace, but the pid is negative, mark the record as invalid.
192 // This happens if a change has been discarded (clearWSID) - it will be removed from the command map.
193 if ((int)$versionRecord['t3ver_wsid'] === 0 && (int)$versionRecord['pid'] === -1) {
194 $caller->setDataValue('liveId', $caller->getId());
195 $caller->setInvalid(true);
196 return;
197 }
198 if ($caller->hasDataValue('liveId') === false) {
199 // Set the original uid from the version record
200 if (!empty($versionRecord['t3ver_oid']) && (int)$versionRecord['pid'] === -1 && (int)$versionRecord['t3ver_wsid'] === $this->getWorkspace()) {
201 $caller->setDataValue('liveId', $versionRecord['t3ver_oid']);
202 } elseif ((int)$versionRecord['t3ver_wsid'] === 0 || (int)$versionRecord['pid'] !== -1) {
203 // The current version record is actually a live record or an accordant placeholder for live
204 $caller->setDataValue('liveId', $caller->getId());
205 $versionRecord = BackendUtility::getWorkspaceVersionOfRecord(
206 $this->getWorkspace(),
207 $caller->getTable(),
208 $caller->getId(),
209 'uid,t3ver_state'
210 );
211 // Set version uid to caller, most likely it's a delete placeholder
212 // for a child record that is not recognized in the reference index
213 if (!empty($versionRecord['uid'])) {
214 $caller->setId($versionRecord['uid']);
215 } else {
216 // If no version could be determined, mark record as invalid
217 // (thus, it will be removed from the command map)
218 $caller->setInvalid(true);
219 }
220 } else {
221 // In case of an unexpected record state, mark the record as invalid
222 $caller->setInvalid(true);
223 }
224 }
225 }
226 }