[TASK] Integrate tests for DataHandler hook invocations
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Functional / DataHandling / Framework / ActionService.php
1 <?php
2 namespace TYPO3\CMS\Core\Tests\Functional\DataHandling\Framework;
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\Database\ConnectionPool;
18 use TYPO3\CMS\Core\DataHandling\DataHandler;
19 use TYPO3\CMS\Core\Utility\GeneralUtility;
20 use TYPO3\CMS\Core\Utility\StringUtility;
21
22 /**
23 * DataHandler Actions
24 */
25 class ActionService
26 {
27 /**
28 * @var DataHandler
29 */
30 protected $dataHandler;
31
32 /**
33 * @return DataHandler
34 */
35 public function getDataHandler()
36 {
37 return $this->dataHandler;
38 }
39
40 /**
41 * @param string $tableName
42 * @param int $pageId
43 * @param array $recordData
44 * @return array
45 */
46 public function createNewRecord($tableName, $pageId, array $recordData)
47 {
48 return $this->createNewRecords($pageId, [$tableName => $recordData]);
49 }
50
51 /**
52 * @param int $pageId
53 * @param array $tableRecordData
54 * @return array
55 */
56 public function createNewRecords($pageId, array $tableRecordData)
57 {
58 $dataMap = [];
59 $newTableIds = [];
60 $currentUid = null;
61 $previousTableName = null;
62 $previousUid = null;
63 foreach ($tableRecordData as $tableName => $recordData) {
64 $recordData = $this->resolvePreviousUid($recordData, $currentUid);
65 if (!isset($recordData['pid'])) {
66 $recordData['pid'] = $pageId;
67 }
68 $currentUid = StringUtility::getUniqueId('NEW');
69 $newTableIds[$tableName][] = $currentUid;
70 $dataMap[$tableName][$currentUid] = $recordData;
71 if ($previousTableName !== null && $previousUid !== null) {
72 $dataMap[$previousTableName][$previousUid] = $this->resolveNextUid(
73 $dataMap[$previousTableName][$previousUid],
74 $currentUid
75 );
76 }
77 $previousTableName = $tableName;
78 $previousUid = $currentUid;
79 }
80 $this->createDataHandler();
81 $this->dataHandler->start($dataMap, []);
82 $this->dataHandler->process_datamap();
83
84 foreach ($newTableIds as $tableName => &$ids) {
85 foreach ($ids as &$id) {
86 if (!empty($this->dataHandler->substNEWwithIDs[$id])) {
87 $id = $this->dataHandler->substNEWwithIDs[$id];
88 }
89 }
90 }
91
92 return $newTableIds;
93 }
94
95 /**
96 * @param string $tableName
97 * @param int $uid
98 * @param array $recordData
99 * @param NULL|array $deleteTableRecordIds
100 */
101 public function modifyRecord($tableName, $uid, array $recordData, array $deleteTableRecordIds = null)
102 {
103 $dataMap = [
104 $tableName => [
105 $uid => $recordData,
106 ],
107 ];
108 $commandMap = [];
109 if (!empty($deleteTableRecordIds)) {
110 foreach ($deleteTableRecordIds as $tableName => $recordIds) {
111 foreach ($recordIds as $recordId) {
112 $commandMap[$tableName][$recordId]['delete'] = true;
113 }
114 }
115 }
116 $this->createDataHandler();
117 $this->dataHandler->start($dataMap, $commandMap);
118 $this->dataHandler->process_datamap();
119 if (!empty($commandMap)) {
120 $this->dataHandler->process_cmdmap();
121 }
122 }
123
124 /**
125 * @param int $pageId
126 * @param array $tableRecordData
127 */
128 public function modifyRecords($pageId, array $tableRecordData)
129 {
130 $dataMap = [];
131 $currentUid = null;
132 $previousTableName = null;
133 $previousUid = null;
134 foreach ($tableRecordData as $tableName => $recordData) {
135 if (empty($recordData['uid'])) {
136 continue;
137 }
138 $recordData = $this->resolvePreviousUid($recordData, $currentUid);
139 $currentUid = $recordData['uid'];
140 if ($recordData['uid'] === '__NEW') {
141 $currentUid = StringUtility::getUniqueId('NEW');
142 }
143 if (strpos($currentUid, 'NEW') === 0) {
144 $recordData['pid'] = $pageId;
145 }
146 unset($recordData['uid']);
147 $dataMap[$tableName][$currentUid] = $recordData;
148 if ($previousTableName !== null && $previousUid !== null) {
149 $dataMap[$previousTableName][$previousUid] = $this->resolveNextUid(
150 $dataMap[$previousTableName][$previousUid],
151 $currentUid
152 );
153 }
154 $previousTableName = $tableName;
155 $previousUid = $currentUid;
156 }
157 $this->createDataHandler();
158 $this->dataHandler->start($dataMap, []);
159 $this->dataHandler->process_datamap();
160 }
161
162 /**
163 * @param string $tableName
164 * @param int $uid
165 * @return array
166 */
167 public function deleteRecord($tableName, $uid)
168 {
169 return $this->deleteRecords(
170 [
171 $tableName => [$uid],
172 ]
173 );
174 }
175
176 /**
177 * @param array $tableRecordIds
178 * @return array
179 */
180 public function deleteRecords(array $tableRecordIds)
181 {
182 $commandMap = [];
183 foreach ($tableRecordIds as $tableName => $ids) {
184 foreach ($ids as $uid) {
185 $commandMap[$tableName][$uid] = [
186 'delete' => true,
187 ];
188 }
189 }
190 $this->createDataHandler();
191 $this->dataHandler->start([], $commandMap);
192 $this->dataHandler->process_cmdmap();
193 // Deleting workspace records is actually a copy(!)
194 return $this->dataHandler->copyMappingArray;
195 }
196
197 /**
198 * @param string $tableName
199 * @param int $uid
200 */
201 public function clearWorkspaceRecord($tableName, $uid)
202 {
203 $this->clearWorkspaceRecords(
204 [
205 $tableName => [$uid],
206 ]
207 );
208 }
209
210 /**
211 * @param array $tableRecordIds
212 */
213 public function clearWorkspaceRecords(array $tableRecordIds)
214 {
215 $commandMap = [];
216 foreach ($tableRecordIds as $tableName => $ids) {
217 foreach ($ids as $uid) {
218 $commandMap[$tableName][$uid] = [
219 'version' => [
220 'action' => 'clearWSID',
221 ]
222 ];
223 }
224 }
225 $this->createDataHandler();
226 $this->dataHandler->start([], $commandMap);
227 $this->dataHandler->process_cmdmap();
228 }
229
230 /**
231 * @param string $tableName
232 * @param int $uid
233 * @param int $pageId
234 * @param NULL|array $recordData
235 * @return array
236 */
237 public function copyRecord($tableName, $uid, $pageId, array $recordData = null)
238 {
239 $commandMap = [
240 $tableName => [
241 $uid => [
242 'copy' => $pageId,
243 ],
244 ],
245 ];
246 if ($recordData !== null) {
247 $commandMap[$tableName][$uid]['copy'] = [
248 'action' => 'paste',
249 'target' => $pageId,
250 'update' => $recordData,
251 ];
252 }
253 $this->createDataHandler();
254 $this->dataHandler->start([], $commandMap);
255 $this->dataHandler->process_cmdmap();
256 return $this->dataHandler->copyMappingArray;
257 }
258
259 /**
260 * @param string $tableName
261 * @param int $uid
262 * @param int $pageId
263 * @param NULL|array $recordData
264 * @return array
265 */
266 public function moveRecord($tableName, $uid, $pageId, array $recordData = null)
267 {
268 $commandMap = [
269 $tableName => [
270 $uid => [
271 'move' => $pageId,
272 ],
273 ],
274 ];
275 if ($recordData !== null) {
276 $commandMap[$tableName][$uid]['move'] = [
277 'action' => 'paste',
278 'target' => $pageId,
279 'update' => $recordData,
280 ];
281 }
282 $this->createDataHandler();
283 $this->dataHandler->start([], $commandMap);
284 $this->dataHandler->process_cmdmap();
285 return $this->dataHandler->copyMappingArray;
286 }
287
288 /**
289 * @param string $tableName
290 * @param int $uid
291 * @param int $languageId
292 * @return array
293 */
294 public function localizeRecord($tableName, $uid, $languageId)
295 {
296 $commandMap = [
297 $tableName => [
298 $uid => [
299 'localize' => $languageId,
300 ],
301 ],
302 ];
303 $this->createDataHandler();
304 $this->dataHandler->start([], $commandMap);
305 $this->dataHandler->process_cmdmap();
306 return $this->dataHandler->copyMappingArray;
307 }
308
309 /**
310 * @param string $tableName
311 * @param int $uid
312 * @param int $languageId
313 * @return array
314 */
315 public function copyRecordToLanguage($tableName, $uid, $languageId)
316 {
317 $commandMap = [
318 $tableName => [
319 $uid => [
320 'copyToLanguage' => $languageId,
321 ],
322 ],
323 ];
324 $this->createDataHandler();
325 $this->dataHandler->start([], $commandMap);
326 $this->dataHandler->process_cmdmap();
327 return $this->dataHandler->copyMappingArray;
328 }
329
330 /**
331 * @param string $tableName
332 * @param int $uid
333 * @param string $fieldName
334 * @param array $referenceIds
335 */
336 public function modifyReferences($tableName, $uid, $fieldName, array $referenceIds)
337 {
338 $dataMap = [
339 $tableName => [
340 $uid => [
341 $fieldName => implode(',', $referenceIds),
342 ],
343 ]
344 ];
345 $this->createDataHandler();
346 $this->dataHandler->start($dataMap, []);
347 $this->dataHandler->process_datamap();
348 }
349
350 /**
351 * @param string $tableName
352 * @param int $liveUid
353 * @param bool $throwException
354 */
355 public function publishRecord($tableName, $liveUid, $throwException = true)
356 {
357 $this->publishRecords([$tableName => [$liveUid]], $throwException);
358 }
359
360 /**
361 * @param array $tableLiveUids
362 * @param bool $throwException
363 * @throws \TYPO3\TestingFramework\Core\Exception
364 */
365 public function publishRecords(array $tableLiveUids, $throwException = true)
366 {
367 $commandMap = [];
368 foreach ($tableLiveUids as $tableName => $liveUids) {
369 foreach ($liveUids as $liveUid) {
370 $versionedUid = $this->getVersionedId($tableName, $liveUid);
371 if (empty($versionedUid)) {
372 if ($throwException) {
373 throw new \TYPO3\TestingFramework\Core\Exception('Versioned UID could not be determined', 1476049592);
374 } else {
375 continue;
376 }
377 }
378
379 $commandMap[$tableName][$liveUid] = [
380 'version' => [
381 'action' => 'swap',
382 'swapWith' => $versionedUid,
383 'notificationAlternativeRecipients' => [],
384 ],
385 ];
386 }
387 }
388 $this->createDataHandler();
389 $this->dataHandler->start([], $commandMap);
390 $this->dataHandler->process_cmdmap();
391 }
392
393 /**
394 * @param int $workspaceId
395 */
396 public function publishWorkspace($workspaceId)
397 {
398 $commandMap = $this->getWorkspaceService()->getCmdArrayForPublishWS($workspaceId, false);
399 $this->createDataHandler();
400 $this->dataHandler->start([], $commandMap);
401 $this->dataHandler->process_cmdmap();
402 }
403
404 /**
405 * @param int $workspaceId
406 */
407 public function swapWorkspace($workspaceId)
408 {
409 $commandMap = $this->getWorkspaceService()->getCmdArrayForPublishWS($workspaceId, true);
410 $this->createDataHandler();
411 $this->dataHandler->start([], $commandMap);
412 $this->dataHandler->process_cmdmap();
413 }
414
415 /**
416 * @param array $recordData
417 * @param NULL|string|int $previousUid
418 * @return array
419 */
420 protected function resolvePreviousUid(array $recordData, $previousUid)
421 {
422 if ($previousUid === null) {
423 return $recordData;
424 }
425 foreach ($recordData as $fieldName => $fieldValue) {
426 if (strpos($fieldValue, '__previousUid') === false) {
427 continue;
428 }
429 $recordData[$fieldName] = str_replace('__previousUid', $previousUid, $fieldValue);
430 }
431 return $recordData;
432 }
433
434 /**
435 * @param array $recordData
436 * @param NULL|string|int $nextUid
437 * @return array
438 */
439 protected function resolveNextUid(array $recordData, $nextUid)
440 {
441 if ($nextUid === null) {
442 return $recordData;
443 }
444 foreach ($recordData as $fieldName => $fieldValue) {
445 if (strpos($fieldValue, '__nextUid') === false) {
446 continue;
447 }
448 $recordData[$fieldName] = str_replace('__nextUid', $nextUid, $fieldValue);
449 }
450 return $recordData;
451 }
452
453 /**
454 * @param string $tableName
455 * @param int $liveUid
456 * @return NULL|int
457 */
458 protected function getVersionedId($tableName, $liveUid)
459 {
460 $versionedId = null;
461 $liveUid = (int)$liveUid;
462 $workspaceId = (int)$this->getBackendUser()->workspace;
463
464 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
465 ->getQueryBuilderForTable($tableName);
466 $queryBuilder->getRestrictions()->removeAll();
467 $statement = $queryBuilder
468 ->select('uid')
469 ->from($tableName)
470 ->where(
471 $queryBuilder->expr()->eq(
472 'pid',
473 $queryBuilder->createNamedParameter(-1, \PDO::PARAM_INT)
474 ),
475 $queryBuilder->expr()->eq(
476 't3ver_oid',
477 $queryBuilder->createNamedParameter($liveUid, \PDO::PARAM_INT)
478 ),
479 $queryBuilder->expr()->eq(
480 't3ver_wsid',
481 $queryBuilder->createNamedParameter($workspaceId, \PDO::PARAM_INT)
482 )
483 )
484 ->execute();
485
486 $row = $statement->fetch();
487 if (!empty($row['uid'])) {
488 $versionedId = (int)$row['uid'];
489 }
490 return $versionedId;
491 }
492
493 /**
494 * @return DataHandler
495 */
496 protected function createDataHandler()
497 {
498 $this->dataHandler = GeneralUtility::makeInstance(DataHandler::class);
499 $backendUser = $this->getBackendUser();
500 if (isset($backendUser->uc['copyLevels'])) {
501 $this->dataHandler->copyTree = $backendUser->uc['copyLevels'];
502 }
503 return $this->dataHandler;
504 }
505
506 /**
507 * @return \TYPO3\CMS\Workspaces\Service\WorkspaceService
508 */
509 protected function getWorkspaceService()
510 {
511 return GeneralUtility::makeInstance(
512 \TYPO3\CMS\Workspaces\Service\WorkspaceService::class
513 );
514 }
515
516 /**
517 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
518 */
519 protected function getBackendUser()
520 {
521 return $GLOBALS['BE_USER'];
522 }
523 }