Passed
Push — master ( f74189...01a97a )
by
unknown
23:17 queued 10:09
created

RemoteServer::getBackendUser()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the TYPO3 CMS project.
5
 *
6
 * It is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License, either version 2
8
 * of the License, or any later version.
9
 *
10
 * For the full copyright and license information, please read the
11
 * LICENSE.txt file that was distributed with this source code.
12
 *
13
 * The TYPO3 project - inspiring people to share!
14
 */
15
16
namespace TYPO3\CMS\Workspaces\Controller\Remote;
17
18
use TYPO3\CMS\Backend\Backend\Avatar\Avatar;
19
use TYPO3\CMS\Backend\Form\FormDataCompiler;
20
use TYPO3\CMS\Backend\Form\FormDataGroup\TcaDatabaseRecord;
21
use TYPO3\CMS\Backend\Utility\BackendUtility;
22
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
23
use TYPO3\CMS\Core\Database\ConnectionPool;
24
use TYPO3\CMS\Core\Imaging\Icon;
25
use TYPO3\CMS\Core\Imaging\IconFactory;
26
use TYPO3\CMS\Core\Localization\LanguageService;
27
use TYPO3\CMS\Core\Resource\FileReference;
28
use TYPO3\CMS\Core\Resource\ProcessedFile;
29
use TYPO3\CMS\Core\Utility\DiffUtility;
30
use TYPO3\CMS\Core\Utility\GeneralUtility;
31
use TYPO3\CMS\Core\Utility\MathUtility;
32
use TYPO3\CMS\Core\Utility\PathUtility;
33
use TYPO3\CMS\Core\Utility\StringUtility;
34
use TYPO3\CMS\Core\Versioning\VersionState;
35
use TYPO3\CMS\Workspaces\Domain\Model\CombinedRecord;
36
use TYPO3\CMS\Workspaces\Service\GridDataService;
37
use TYPO3\CMS\Workspaces\Service\HistoryService;
38
use TYPO3\CMS\Workspaces\Service\IntegrityService;
39
use TYPO3\CMS\Workspaces\Service\StagesService;
40
use TYPO3\CMS\Workspaces\Service\WorkspaceService;
41
42
/**
43
 * Class RemoteServer
44
 * @internal This is a specific Backend Controller implementation and is not considered part of the Public TYPO3 API.
45
 */
46
class RemoteServer
47
{
48
    /**
49
     * @var GridDataService
50
     */
51
    protected $gridDataService;
52
53
    /**
54
     * @var StagesService
55
     */
56
    protected $stagesService;
57
58
    /**
59
     * @var WorkspaceService
60
     */
61
    protected $workspaceService;
62
63
    /**
64
     * @var DiffUtility
65
     */
66
    protected $differenceHandler;
67
68
    public function __construct()
69
    {
70
        $this->workspaceService = GeneralUtility::makeInstance(WorkspaceService::class);
71
        $this->gridDataService = GeneralUtility::makeInstance(GridDataService::class);
72
        $this->stagesService = GeneralUtility::makeInstance(StagesService::class);
73
    }
74
75
    /**
76
     * Checks integrity of elements before performing actions on them.
77
     *
78
     * @param \stdClass $parameters
79
     * @return array
80
     */
81
    public function checkIntegrity(\stdClass $parameters)
82
    {
83
        $integrity = $this->createIntegrityService($this->getAffectedElements($parameters));
84
        $integrity->check();
85
        $response = [
86
            'result' => $integrity->getStatusRepresentation()
87
        ];
88
        return $response;
89
    }
90
91
    /**
92
     * Get List of workspace changes
93
     *
94
     * @param \stdClass $parameter
95
     * @return array $data
96
     */
97
    public function getWorkspaceInfos($parameter)
98
    {
99
        // To avoid too much work we use -1 to indicate that every page is relevant
100
        $pageId = $parameter->id > 0 ? $parameter->id : -1;
101
        if (!isset($parameter->language) || !MathUtility::canBeInterpretedAsInteger($parameter->language)) {
102
            $parameter->language = null;
103
        }
104
        $versions = $this->workspaceService->selectVersionsInWorkspace(
105
            $this->getCurrentWorkspace(),
106
            -99,
107
            $pageId,
108
            $parameter->depth,
109
            'tables_select',
110
            $parameter->language
111
        );
112
        $data = $this->gridDataService->generateGridListFromVersions($versions, $parameter, $this->getCurrentWorkspace());
113
        return $data;
114
    }
115
116
    /**
117
     * Get List of available workspace actions
118
     *
119
     * @return array $data
120
     */
121
    public function getStageActions()
122
    {
123
        $stages = $this->stagesService->getStagesForWSUser();
124
        $data = [
125
            'total' => count($stages),
126
            'data' => $stages
127
        ];
128
        return $data;
129
    }
130
131
    /**
132
     * Fetch further information to current selected workspace record.
133
     *
134
     * @param \stdClass $parameter
135
     * @return array $data
136
     */
137
    public function getRowDetails($parameter)
138
    {
139
        $diffReturnArray = [];
140
        $liveReturnArray = [];
141
        $diffUtility = $this->getDifferenceHandler();
142
        $liveRecord = (array)BackendUtility::getRecord($parameter->table, $parameter->t3ver_oid);
143
        $versionRecord = (array)BackendUtility::getRecord($parameter->table, $parameter->uid);
144
        $versionState = VersionState::cast((int)($versionRecord['t3ver_state'] ?? 0));
145
        $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
146
        $icon_Live = $iconFactory->getIconForRecord($parameter->table, $liveRecord, Icon::SIZE_SMALL)->render();
147
        $icon_Workspace = $iconFactory->getIconForRecord($parameter->table, $versionRecord, Icon::SIZE_SMALL)->render();
148
        $stagePosition = $this->stagesService->getPositionOfCurrentStage($parameter->stage);
149
        $fieldsOfRecords = array_keys($liveRecord);
150
        $isNewOrDeletePlaceholder = $versionState->equals(VersionState::NEW_PLACEHOLDER) || $versionState->equals(VersionState::DELETE_PLACEHOLDER);
151
        $suitableFields = ($isNewOrDeletePlaceholder && ($parameter->filterFields ?? false)) ? array_flip($this->getSuitableFields($parameter->table, $parameter->t3ver_oid)) : [];
152
        foreach ($fieldsOfRecords as $fieldName) {
153
            if (
154
                empty($GLOBALS['TCA'][$parameter->table]['columns'][$fieldName]['config'])
155
            ) {
156
                continue;
157
            }
158
            // Disable internal fields
159
            if (($GLOBALS['TCA'][$parameter->table]['ctrl']['transOrigDiffSourceField'] ?? '') === $fieldName) {
160
                continue;
161
            }
162
            if (($GLOBALS['TCA'][$parameter->table]['ctrl']['origUid'] ?? '') === $fieldName) {
163
                continue;
164
            }
165
            // Get the field's label. If not available, use the field name
166
            $fieldTitle = $this->getLanguageService()->sL(BackendUtility::getItemLabel($parameter->table, $fieldName));
167
            if (empty($fieldTitle)) {
168
                $fieldTitle = $fieldName;
169
            }
170
            // Gets the TCA configuration for the current field
171
            $configuration = $GLOBALS['TCA'][$parameter->table]['columns'][$fieldName]['config'];
172
            // check for exclude fields
173
            if ($this->getBackendUser()->isAdmin() || $GLOBALS['TCA'][$parameter->table]['columns'][$fieldName]['exclude'] == 0 || GeneralUtility::inList($this->getBackendUser()->groupData['non_exclude_fields'], $parameter->table . ':' . $fieldName)) {
174
                // call diff class only if there is a difference
175
                if ($configuration['type'] === 'inline' && $configuration['foreign_table'] === 'sys_file_reference') {
176
                    $useThumbnails = false;
177
                    if (!empty($configuration['overrideChildTca']['columns']['uid_local']['config']['appearance']['elementBrowserAllowed']) && !empty($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'])) {
178
                        $fileExtensions = GeneralUtility::trimExplode(',', $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], true);
179
                        $allowedExtensions = GeneralUtility::trimExplode(',', $configuration['overrideChildTca']['columns']['uid_local']['config']['appearance']['elementBrowserAllowed'], true);
180
                        $differentExtensions = array_diff($allowedExtensions, $fileExtensions);
181
                        $useThumbnails = empty($differentExtensions);
182
                    }
183
184
                    $liveFileReferences = (array)BackendUtility::resolveFileReferences(
185
                        $parameter->table,
186
                        $fieldName,
187
                        $liveRecord,
188
                        0
189
                    );
190
                    $versionFileReferences = (array)BackendUtility::resolveFileReferences(
191
                        $parameter->table,
192
                        $fieldName,
193
                        $versionRecord,
194
                        $this->getCurrentWorkspace()
195
                    );
196
                    $fileReferenceDifferences = $this->prepareFileReferenceDifferences(
197
                        $liveFileReferences,
198
                        $versionFileReferences,
199
                        $useThumbnails
200
                    );
201
202
                    if ($fileReferenceDifferences === null) {
203
                        continue;
204
                    }
205
206
                    $diffReturnArray[] = [
207
                        'field' => $fieldName,
208
                        'label' => $fieldTitle,
209
                        'content' => $fileReferenceDifferences['differences']
210
                    ];
211
                    $liveReturnArray[] = [
212
                        'field' => $fieldName,
213
                        'label' => $fieldTitle,
214
                        'content' => $fileReferenceDifferences['live']
215
                    ];
216
                } elseif ($isNewOrDeletePlaceholder && isset($suitableFields[$fieldName])) {
217
                    // If this is a new or delete placeholder, add diff view for all appropriate fields
218
                    $versionRecord[$fieldName] = BackendUtility::getProcessedValue(
219
                        $parameter->table,
220
                        $fieldName,
221
                        $versionRecord[$fieldName],
222
                        0,
223
                        true,
224
                        false,
225
                        $versionRecord['uid']
226
                    ) ?? '';
227
228
                    // Don't add empty fields
229
                    if ($versionRecord[$fieldName] === '') {
230
                        continue;
231
                    }
232
233
                    $diffReturnArray[] = [
234
                        'field' => $fieldName,
235
                        'label' => $fieldTitle,
236
                        'content' => $versionState->equals(VersionState::NEW_PLACEHOLDER)
237
                            ? $diffUtility->makeDiffDisplay('', $versionRecord[$fieldName])
238
                            : $diffUtility->makeDiffDisplay($versionRecord[$fieldName], '')
239
                    ];
240
241
                    // Generally not needed by Core, but let's make it available for further processing in hooks
242
                    $liveReturnArray[] = [
243
                        'field' => $fieldName,
244
                        'label' => $fieldTitle,
245
                        'content' => BackendUtility::getProcessedValue(
246
                            $parameter->table,
247
                            $fieldName,
248
                            $liveReturnArray[$fieldName],
249
                            0,
250
                            true,
251
                            false,
252
                            $liveReturnArray['uid']
253
                        )
254
                    ];
255
                } elseif ((string)$liveRecord[$fieldName] !== (string)$versionRecord[$fieldName]) {
256
                    // Select the human readable values before diff
257
                    $liveRecord[$fieldName] = BackendUtility::getProcessedValue(
258
                        $parameter->table,
259
                        $fieldName,
260
                        $liveRecord[$fieldName],
261
                        0,
262
                        true,
263
                        false,
264
                        $liveRecord['uid']
265
                    );
266
                    $versionRecord[$fieldName] = BackendUtility::getProcessedValue(
267
                        $parameter->table,
268
                        $fieldName,
269
                        $versionRecord[$fieldName],
270
                        0,
271
                        true,
272
                        false,
273
                        $versionRecord['uid']
274
                    );
275
276
                    $diffReturnArray[] = [
277
                        'field' => $fieldName,
278
                        'label' => $fieldTitle,
279
                        'content' => $diffUtility->makeDiffDisplay($liveRecord[$fieldName], $versionRecord[$fieldName])
280
                    ];
281
                    $liveReturnArray[] = [
282
                        'field' => $fieldName,
283
                        'label' => $fieldTitle,
284
                        'content' => $liveRecord[$fieldName]
285
                    ];
286
                }
287
            }
288
        }
289
        // Hook for modifying the difference and live arrays
290
        // (this may be used by custom or dynamically-defined fields)
291
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['workspaces']['modifyDifferenceArray'] ?? [] as $className) {
292
            $hookObject = GeneralUtility::makeInstance($className);
293
            if (method_exists($hookObject, 'modifyDifferenceArray')) {
294
                $hookObject->modifyDifferenceArray($parameter, $diffReturnArray, $liveReturnArray, $diffUtility);
295
            }
296
        }
297
        $commentsForRecord = $this->getCommentsForRecord($parameter->uid, $parameter->table);
298
299
        $historyService = GeneralUtility::makeInstance(HistoryService::class);
300
        $history = $historyService->getHistory($parameter->table, $parameter->t3ver_oid);
301
302
        if ($this->stagesService->isPrevStageAllowedForUser($parameter->stage)) {
303
            $prevStage = $this->stagesService->getPrevStage($parameter->stage);
304
            if (isset($prevStage[0])) {
305
                $prevStage = current($prevStage);
0 ignored issues
show
Bug introduced by
It seems like $prevStage can also be of type boolean; however, parameter $array of current() does only seem to accept array|object, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

305
                $prevStage = current(/** @scrutinizer ignore-type */ $prevStage);
Loading history...
306
            }
307
        }
308
        if ($this->stagesService->isNextStageAllowedForUser($parameter->stage)) {
309
            $nextStage = $this->stagesService->getNextStage($parameter->stage);
310
            if (isset($nextStage[0])) {
311
                $nextStage = current($nextStage);
312
            }
313
        }
314
315
        return [
316
            'total' => 1,
317
            'data' => [
318
                [
319
                    // these parts contain HTML (don't escape)
320
                    'diff' => $diffReturnArray,
321
                    'live_record' => $liveReturnArray,
322
                    'icon_Live' => $icon_Live,
323
                    'icon_Workspace' => $icon_Workspace,
324
                    // this part is already escaped in getCommentsForRecord()
325
                    'comments' => $commentsForRecord,
326
                    // escape/sanitize the others
327
                    'path_Live' => htmlspecialchars(BackendUtility::getRecordPath($liveRecord['pid'], '', 999)),
0 ignored issues
show
Bug introduced by
It seems like TYPO3\CMS\Backend\Utilit...Record['pid'], '', 999) can also be of type array<integer,string>; however, parameter $string of htmlspecialchars() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

327
                    'path_Live' => htmlspecialchars(/** @scrutinizer ignore-type */ BackendUtility::getRecordPath($liveRecord['pid'], '', 999)),
Loading history...
328
                    'label_Stage' => htmlspecialchars($this->stagesService->getStageTitle($parameter->stage)),
329
                    'label_PrevStage' => $prevStage ?? false,
330
                    'label_NextStage' => $nextStage ?? false,
331
                    'stage_position' => (int)$stagePosition['position'],
332
                    'stage_count' => (int)$stagePosition['count'],
333
                    'parent' => [
334
                        'table' => htmlspecialchars($parameter->table),
335
                        'uid' => (int)$parameter->uid
336
                    ],
337
                    'history' => [
338
                        'data' => $history,
339
                        'total' => count($history)
340
                    ]
341
                ]
342
            ]
343
        ];
344
    }
345
346
    /**
347
     * Prepares difference view for file references.
348
     *
349
     * @param FileReference[] $liveFileReferences
350
     * @param FileReference[] $versionFileReferences
351
     * @param bool|false $useThumbnails
352
     * @return array|null
353
     */
354
    protected function prepareFileReferenceDifferences(array $liveFileReferences, array $versionFileReferences, $useThumbnails = false)
355
    {
356
        $randomValue = StringUtility::getUniqueId('file');
357
358
        $liveValues = [];
359
        $versionValues = [];
360
        $candidates = [];
361
        $substitutes = [];
362
363
        // Process live references
364
        foreach ($liveFileReferences as $identifier => $liveFileReference) {
365
            $identifierWithRandomValue = $randomValue . '__' . $liveFileReference->getUid() . '__' . $randomValue;
366
            $candidates[$identifierWithRandomValue] = $liveFileReference;
367
            $liveValues[] = $identifierWithRandomValue;
368
        }
369
370
        // Process version references
371
        foreach ($versionFileReferences as $identifier => $versionFileReference) {
372
            $identifierWithRandomValue = $randomValue . '__' . $versionFileReference->getUid() . '__' . $randomValue;
373
            $candidates[$identifierWithRandomValue] = $versionFileReference;
374
            $versionValues[] = $identifierWithRandomValue;
375
        }
376
377
        // Combine values and surround by spaces
378
        // (to reduce the chunks Diff will find)
379
        $liveInformation = ' ' . implode(' ', $liveValues) . ' ';
380
        $versionInformation = ' ' . implode(' ', $versionValues) . ' ';
381
382
        // Return if information has not changed
383
        if ($liveInformation === $versionInformation) {
384
            return null;
385
        }
386
387
        /**
388
         * @var string $identifierWithRandomValue
389
         * @var FileReference $fileReference
390
         */
391
        foreach ($candidates as $identifierWithRandomValue => $fileReference) {
392
            if ($useThumbnails) {
393
                $thumbnailFile = $fileReference->getOriginalFile()->process(
394
                    ProcessedFile::CONTEXT_IMAGEPREVIEW,
395
                    ['width' => 40, 'height' => 40]
396
                );
397
                $thumbnailMarkup = '<img src="' . PathUtility::getAbsoluteWebPath($thumbnailFile->getPublicUrl()) . '" />';
398
                $substitutes[$identifierWithRandomValue] = $thumbnailMarkup;
399
            } else {
400
                $substitutes[$identifierWithRandomValue] = $fileReference->getPublicUrl();
401
            }
402
        }
403
404
        $differences = $this->getDifferenceHandler()->makeDiffDisplay($liveInformation, $versionInformation);
405
        $liveInformation = str_replace(array_keys($substitutes), array_values($substitutes), trim($liveInformation));
406
        $differences = str_replace(array_keys($substitutes), array_values($substitutes), trim($differences));
407
408
        return [
409
            'live' => $liveInformation,
410
            'differences' => $differences
411
        ];
412
    }
413
414
    /**
415
     * Gets an array with all sys_log entries and their comments for the given record uid and table
416
     *
417
     * @param int $uid uid of changed element to search for in log
418
     * @param string $table Name of the record's table
419
     * @return array
420
     */
421
    public function getCommentsForRecord($uid, $table)
422
    {
423
        $sysLogReturnArray = [];
424
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_log');
425
426
        $result = $queryBuilder
427
            ->select('log_data', 'tstamp', 'userid')
428
            ->from('sys_log')
429
            ->where(
430
                $queryBuilder->expr()->eq(
431
                    'action',
432
                    $queryBuilder->createNamedParameter(6, \PDO::PARAM_INT)
433
                ),
434
                $queryBuilder->expr()->eq(
435
                    'details_nr',
436
                    $queryBuilder->createNamedParameter(30, \PDO::PARAM_INT)
437
                ),
438
                $queryBuilder->expr()->eq(
439
                    'tablename',
440
                    $queryBuilder->createNamedParameter($table, \PDO::PARAM_STR)
441
                ),
442
                $queryBuilder->expr()->eq(
443
                    'recuid',
444
                    $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
445
                )
446
            )
447
            ->orderBy('tstamp', 'DESC')
448
            ->execute();
449
450
        /** @var Avatar $avatar */
451
        $avatar = GeneralUtility::makeInstance(Avatar::class);
452
453
        while ($sysLogRow = $result->fetch()) {
454
            $sysLogEntry = [];
455
            $data = unserialize($sysLogRow['log_data']);
456
            $beUserRecord = BackendUtility::getRecord('be_users', $sysLogRow['userid']);
457
            $sysLogEntry['stage_title'] = htmlspecialchars($this->stagesService->getStageTitle($data['stage']));
458
            $sysLogEntry['user_uid'] = (int)$sysLogRow['userid'];
459
            $sysLogEntry['user_username'] = is_array($beUserRecord) ? htmlspecialchars($beUserRecord['username']) : '';
460
            $sysLogEntry['tstamp'] = htmlspecialchars(BackendUtility::datetime($sysLogRow['tstamp']));
461
            $sysLogEntry['user_comment'] = nl2br(htmlspecialchars($data['comment']));
462
            $sysLogEntry['user_avatar'] = $avatar->render($beUserRecord);
463
            $sysLogReturnArray[] = $sysLogEntry;
464
        }
465
        return $sysLogReturnArray;
466
    }
467
468
    /**
469
     * Gets all available system languages.
470
     *
471
     * @param \stdClass $parameters
472
     * @return array
473
     */
474
    public function getSystemLanguages(\stdClass $parameters)
475
    {
476
        $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
477
        $systemLanguages = [
478
            [
479
                'uid' => 'all',
480
                'title' => $this->getLanguageService()->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:language.allLanguages'),
481
                'icon' => $iconFactory->getIcon('empty-empty', Icon::SIZE_SMALL)->render()
482
            ]
483
        ];
484
        foreach ($this->gridDataService->getSystemLanguages($parameters->pageUid ?? 0) as $id => $systemLanguage) {
485
            if ($id < 0) {
486
                continue;
487
            }
488
            $systemLanguages[] = [
489
                'uid' => $id,
490
                'title' => htmlspecialchars($systemLanguage['title']),
491
                'icon' => $iconFactory->getIcon($systemLanguage['flagIcon'], Icon::SIZE_SMALL)->render()
492
            ];
493
        }
494
        $result = [
495
            'total' => count($systemLanguages),
496
            'data' => $systemLanguages
497
        ];
498
        return $result;
499
    }
500
501
    protected function getBackendUser(): BackendUserAuthentication
502
    {
503
        return $GLOBALS['BE_USER'];
504
    }
505
506
    protected function getLanguageService(): LanguageService
507
    {
508
        return $GLOBALS['LANG'];
509
    }
510
511
    /**
512
     * Gets the difference handler, parsing differences based on sentences.
513
     *
514
     * @return DiffUtility
515
     */
516
    protected function getDifferenceHandler()
517
    {
518
        if (!isset($this->differenceHandler)) {
519
            $this->differenceHandler = GeneralUtility::makeInstance(DiffUtility::class);
520
            $this->differenceHandler->stripTags = false;
521
        }
522
        return $this->differenceHandler;
523
    }
524
525
    /**
526
     * Creates a new instance of the integrity service for the
527
     * given set of affected elements.
528
     *
529
     * @param CombinedRecord[] $affectedElements
530
     * @return IntegrityService
531
     * @see getAffectedElements
532
     */
533
    protected function createIntegrityService(array $affectedElements)
534
    {
535
        $integrityService = GeneralUtility::makeInstance(IntegrityService::class);
536
        $integrityService->setAffectedElements($affectedElements);
537
        return $integrityService;
538
    }
539
540
    /**
541
     * Gets affected elements on publishing/swapping actions.
542
     * Affected elements have a dependency, e.g. translation overlay
543
     * and the default origin record - thus, the default record would be
544
     * affected if the translation overlay shall be published.
545
     *
546
     * @param \stdClass $parameters
547
     * @return array
548
     */
549
    protected function getAffectedElements(\stdClass $parameters)
550
    {
551
        $affectedElements = [];
552
        if ($parameters->type === 'selection') {
553
            foreach ((array)$parameters->selection as $element) {
554
                $affectedElements[] = CombinedRecord::create($element->table, $element->liveId, $element->versionId);
555
            }
556
        } elseif ($parameters->type === 'all') {
557
            $versions = $this->workspaceService->selectVersionsInWorkspace(
558
                $this->getCurrentWorkspace(),
559
                -99,
560
                -1,
561
                0,
562
                'tables_select',
563
                $this->validateLanguageParameter($parameters)
564
            );
565
            foreach ($versions as $table => $tableElements) {
566
                foreach ($tableElements as $element) {
567
                    $affectedElement = CombinedRecord::create($table, $element['t3ver_oid'], $element['uid']);
568
                    $affectedElement->getVersionRecord()->setRow($element);
569
                    $affectedElements[] = $affectedElement;
570
                }
571
            }
572
        }
573
        return $affectedElements;
574
    }
575
576
    /**
577
     * Validates whether the submitted language parameter can be
578
     * interpreted as integer value.
579
     *
580
     * @param \stdClass $parameters
581
     * @return int|null
582
     */
583
    protected function validateLanguageParameter(\stdClass $parameters)
584
    {
585
        $language = null;
586
        if (isset($parameters->language) && MathUtility::canBeInterpretedAsInteger($parameters->language)) {
587
            $language = $parameters->language;
588
        }
589
        return $language;
590
    }
591
592
    /**
593
     * Gets the current workspace ID.
594
     *
595
     * @return int The current workspace ID
596
     */
597
    protected function getCurrentWorkspace()
598
    {
599
        return $this->workspaceService->getCurrentWorkspace();
600
    }
601
602
    /**
603
     * Gets the fields suitable for being displayed in new and delete diff views
604
     *
605
     * @param string $table
606
     * @param int $uid
607
     * @return array
608
     */
609
    protected function getSuitableFields(string $table, int $uid): array
610
    {
611
        $formDataCompiler = GeneralUtility::makeInstance(
612
            FormDataCompiler::class,
613
            GeneralUtility::makeInstance(TcaDatabaseRecord::class)
614
        );
615
616
        try {
617
            $result = $formDataCompiler->compile(['command' => 'edit', 'tableName' => $table, 'vanillaUid' => $uid]);
618
            $fieldList = array_unique(array_values($result['columnsToProcess']));
619
        } catch (\Exception $exception) {
620
            $fieldList = [];
621
        }
622
623
        // Skip sys_file_reference since it contains fields like "tablenames" in searchFields
624
        $searchFields = $table !== 'sys_file_reference'
625
            ? GeneralUtility::trimExplode(',', (string)($GLOBALS['TCA'][$table]['ctrl']['searchFields'] ?? ''))
626
            : [];
627
628
        return array_unique(array_merge($fieldList, $searchFields));
629
    }
630
}
631