Completed
Push — master ( 51fbd3...febe3a )
by
unknown
18:21
created

GridDataService::__construct()   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
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
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\Service;
17
18
use Psr\EventDispatcher\EventDispatcherInterface;
19
use Psr\Log\LoggerAwareInterface;
20
use Psr\Log\LoggerAwareTrait;
21
use TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider;
22
use TYPO3\CMS\Backend\Utility\BackendUtility;
23
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
24
use TYPO3\CMS\Core\Cache\CacheManager;
25
use TYPO3\CMS\Core\Imaging\Icon;
26
use TYPO3\CMS\Core\Imaging\IconFactory;
27
use TYPO3\CMS\Core\Utility\GeneralUtility;
28
use TYPO3\CMS\Core\Versioning\VersionState;
29
use TYPO3\CMS\Workspaces\Domain\Model\CombinedRecord;
30
use TYPO3\CMS\Workspaces\Event\AfterCompiledCacheableDataForWorkspaceEvent;
31
use TYPO3\CMS\Workspaces\Event\AfterDataGeneratedForWorkspaceEvent;
32
use TYPO3\CMS\Workspaces\Event\GetVersionedDataEvent;
33
use TYPO3\CMS\Workspaces\Event\SortVersionedDataEvent;
34
use TYPO3\CMS\Workspaces\Preview\PreviewUriBuilder;
35
use TYPO3\CMS\Workspaces\Service\Dependency\CollectionService;
36
37
/**
38
 * Grid data service
39
 */
40
class GridDataService implements LoggerAwareInterface
41
{
42
    use LoggerAwareTrait;
43
44
    const GridColumn_Collection = 'Workspaces_Collection';
0 ignored issues
show
Coding Style introduced by
This class constant is not uppercase (expected GRIDCOLUMN_COLLECTION).
Loading history...
45
    const GridColumn_CollectionLevel = 'Workspaces_CollectionLevel';
0 ignored issues
show
Coding Style introduced by
This class constant is not uppercase (expected GRIDCOLUMN_COLLECTIONLEVEL).
Loading history...
46
    const GridColumn_CollectionParent = 'Workspaces_CollectionParent';
0 ignored issues
show
Coding Style introduced by
This class constant is not uppercase (expected GRIDCOLUMN_COLLECTIONPARENT).
Loading history...
47
    const GridColumn_CollectionCurrent = 'Workspaces_CollectionCurrent';
0 ignored issues
show
Coding Style introduced by
This class constant is not uppercase (expected GRIDCOLUMN_COLLECTIONCURRENT).
Loading history...
48
    const GridColumn_CollectionChildren = 'Workspaces_CollectionChildren';
0 ignored issues
show
Coding Style introduced by
This class constant is not uppercase (expected GRIDCOLUMN_COLLECTIONCHILDREN).
Loading history...
49
50
    /**
51
     * Id of the current active workspace.
52
     *
53
     * @var int
54
     */
55
    protected $currentWorkspace;
56
57
    /**
58
     * Version record information (filtered, sorted and limited)
59
     *
60
     * @var array
61
     */
62
    protected $dataArray = [];
63
64
    /**
65
     * Name of the field used for sorting.
66
     *
67
     * @var string
68
     */
69
    protected $sort = '';
70
71
    /**
72
     * Direction used for sorting (ASC, DESC).
73
     *
74
     * @var string
75
     */
76
    protected $sortDir = '';
77
78
    /**
79
     * @var \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface
80
     */
81
    protected $workspacesCache;
82
83
    /**
84
     * @var IntegrityService
85
     */
86
    protected $integrityService;
87
88
    /**
89
     * @var EventDispatcherInterface
90
     */
91
    protected $eventDispatcher;
92
93
    public function __construct(EventDispatcherInterface $eventDispatcher)
94
    {
95
        $this->eventDispatcher = $eventDispatcher;
96
    }
97
98
    /**
99
     * Generates grid list array from given versions.
100
     *
101
     * @param array $versions All records uids etc. First key is table name, second key incremental integer. Records are associative arrays with uid and t3ver_oid fields. The pid of the online record is found as "livepid" the pid of the offline record is found in "wspid
102
     * @param \stdClass $parameter Parameters as submitted by JavaScript component
103
     * @param int $currentWorkspace The current workspace
104
     * @return array Version record information (filtered, sorted and limited)
105
     * @throws \InvalidArgumentException
106
     */
107
    public function generateGridListFromVersions($versions, $parameter, $currentWorkspace)
108
    {
109
        // Read the given parameters from grid. If the parameter is not set use default values.
110
        $filterTxt = $parameter->filterTxt ?? '';
111
        $start = isset($parameter->start) ? (int)$parameter->start : 0;
112
        $limit = isset($parameter->limit) ? (int)$parameter->limit : 30;
113
        $this->sort = $parameter->sort ?? 't3ver_oid';
114
        $this->sortDir = $parameter->dir ?? 'ASC';
115
        if (is_int($currentWorkspace)) {
0 ignored issues
show
introduced by
The condition is_int($currentWorkspace) is always true.
Loading history...
116
            $this->currentWorkspace = $currentWorkspace;
117
        } else {
118
            throw new \InvalidArgumentException('No such workspace defined', 1476048304);
119
        }
120
        $data = [];
121
        $data['data'] = [];
122
        $this->generateDataArray($versions, $filterTxt);
123
        $data['total'] = count($this->dataArray);
124
        $data['data'] = $this->getDataArray($start, $limit);
125
        return $data;
126
    }
127
128
    /**
129
     * Generates grid list array from given versions.
130
     *
131
     * @param array $versions All available version records
132
     * @param string $filterTxt Text to be used to filter record result
133
     */
134
    protected function generateDataArray(array $versions, $filterTxt)
135
    {
136
        $backendUser = $this->getBackendUser();
137
        $workspaceAccess = $backendUser->checkWorkspace($backendUser->workspace);
138
        $swapStage = $workspaceAccess['publish_access'] & 1 ? StagesService::STAGE_PUBLISH_ID : 0;
139
        $swapAccess = $backendUser->workspacePublishAccess($backendUser->workspace) && $backendUser->workspaceSwapAccess();
140
        $this->initializeWorkspacesCachingFramework();
141
        $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
142
        // check for dataArray in cache
143
        if ($this->getDataArrayFromCache($versions, $filterTxt) === false) {
144
            $stagesObj = GeneralUtility::makeInstance(StagesService::class);
145
            $defaultGridColumns = [
146
                self::GridColumn_Collection => 0,
147
                self::GridColumn_CollectionLevel => 0,
148
                self::GridColumn_CollectionParent => '',
149
                self::GridColumn_CollectionCurrent => '',
150
                self::GridColumn_CollectionChildren => 0,
151
            ];
152
            foreach ($versions as $table => $records) {
153
                $hiddenField = $this->getTcaEnableColumnsFieldName($table, 'disabled');
154
                $isRecordTypeAllowedToModify = $backendUser->check('tables_modify', $table);
155
156
                foreach ($records as $record) {
157
                    $origRecord = BackendUtility::getRecord($table, $record['t3ver_oid']);
158
                    $versionRecord = BackendUtility::getRecord($table, $record['uid']);
159
                    $combinedRecord = CombinedRecord::createFromArrays($table, $origRecord, $versionRecord);
0 ignored issues
show
Bug introduced by
It seems like $versionRecord can also be of type null; however, parameter $versionRow of TYPO3\CMS\Workspaces\Dom...ord::createFromArrays() does only seem to accept array, 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

159
                    $combinedRecord = CombinedRecord::createFromArrays($table, $origRecord, /** @scrutinizer ignore-type */ $versionRecord);
Loading history...
Bug introduced by
It seems like $origRecord can also be of type null; however, parameter $liveRow of TYPO3\CMS\Workspaces\Dom...ord::createFromArrays() does only seem to accept array, 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

159
                    $combinedRecord = CombinedRecord::createFromArrays($table, /** @scrutinizer ignore-type */ $origRecord, $versionRecord);
Loading history...
160
                    $this->getIntegrityService()->checkElement($combinedRecord);
161
162
                    if ($hiddenField !== null) {
163
                        $recordState = $this->workspaceState($versionRecord['t3ver_state'], $origRecord[$hiddenField], $versionRecord[$hiddenField]);
164
                    } else {
165
                        $recordState = $this->workspaceState($versionRecord['t3ver_state']);
166
                    }
167
168
                    $isDeletedPage = $table === 'pages' && $recordState === 'deleted';
169
                    $pageId = $table === 'pages' ? $record['uid'] : $record['pid'];
170
                    $viewUrl = GeneralUtility::makeInstance(PreviewUriBuilder::class)->buildUriForElement($table, $record['uid'], $origRecord, $versionRecord);
171
                    $workspaceRecordLabel = BackendUtility::getRecordTitle($table, $versionRecord);
172
                    $liveRecordLabel = BackendUtility::getRecordTitle($table, $origRecord);
173
                    [$pathWorkspaceCropped, $pathWorkspace] = BackendUtility::getRecordPath((int)$record['wspid'], '', 15, 1000);
174
                    $versionArray = [];
175
                    $versionArray['table'] = $table;
176
                    $versionArray['id'] = $table . ':' . $record['uid'];
177
                    $versionArray['uid'] = $record['uid'];
178
                    $versionArray = array_merge($versionArray, $defaultGridColumns);
179
                    $versionArray['label_Workspace'] = htmlspecialchars($workspaceRecordLabel);
180
                    $versionArray['label_Workspace_crop'] = htmlspecialchars(GeneralUtility::fixed_lgd_cs($workspaceRecordLabel, $backendUser->uc['titleLen']));
181
                    $versionArray['label_Live'] = htmlspecialchars($liveRecordLabel);
182
                    $versionArray['label_Live_crop'] = htmlspecialchars(GeneralUtility::fixed_lgd_cs($liveRecordLabel, $backendUser->uc['titleLen']));
183
                    $versionArray['label_Stage'] = htmlspecialchars($stagesObj->getStageTitle($versionRecord['t3ver_stage']));
184
                    $tempStage = $stagesObj->getNextStage($versionRecord['t3ver_stage']);
185
                    $versionArray['label_nextStage'] = htmlspecialchars($stagesObj->getStageTitle($tempStage['uid']));
186
                    $versionArray['value_nextStage'] = (int)$tempStage['uid'];
187
                    $tempStage = $stagesObj->getPrevStage($versionRecord['t3ver_stage']);
188
                    $versionArray['label_prevStage'] = htmlspecialchars($stagesObj->getStageTitle($tempStage['uid']));
189
                    $versionArray['value_prevStage'] = (int)$tempStage['uid'];
190
                    $versionArray['path_Live'] = htmlspecialchars(BackendUtility::getRecordPath($record['livepid'], '', 999));
0 ignored issues
show
Bug introduced by
It seems like TYPO3\CMS\Backend\Utilit...rd['livepid'], '', 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

190
                    $versionArray['path_Live'] = htmlspecialchars(/** @scrutinizer ignore-type */ BackendUtility::getRecordPath($record['livepid'], '', 999));
Loading history...
191
                    $versionArray['path_Workspace'] = htmlspecialchars($pathWorkspace);
192
                    $versionArray['path_Workspace_crop'] = htmlspecialchars($pathWorkspaceCropped);
193
                    $versionArray['workspace_Title'] = htmlspecialchars(WorkspaceService::getWorkspaceTitle($versionRecord['t3ver_wsid']));
194
                    $versionArray['workspace_Tstamp'] = $versionRecord['tstamp'];
195
                    $versionArray['workspace_Formated_Tstamp'] = BackendUtility::datetime($versionRecord['tstamp']);
196
                    $versionArray['t3ver_wsid'] = $versionRecord['t3ver_wsid'];
197
                    $versionArray['t3ver_oid'] = $record['t3ver_oid'];
198
                    $versionArray['livepid'] = $record['livepid'];
199
                    $versionArray['stage'] = $versionRecord['t3ver_stage'];
200
                    $versionArray['icon_Live'] = $iconFactory->getIconForRecord($table, $origRecord, Icon::SIZE_SMALL)->render();
201
                    $versionArray['icon_Workspace'] = $iconFactory->getIconForRecord($table, $versionRecord, Icon::SIZE_SMALL)->render();
202
                    $languageValue = $this->getLanguageValue($table, $versionRecord);
0 ignored issues
show
Bug introduced by
It seems like $versionRecord can also be of type null; however, parameter $record of TYPO3\CMS\Workspaces\Ser...ice::getLanguageValue() does only seem to accept array, 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

202
                    $languageValue = $this->getLanguageValue($table, /** @scrutinizer ignore-type */ $versionRecord);
Loading history...
203
                    $versionArray['languageValue'] = $languageValue;
204
                    $versionArray['language'] = [
205
                        'icon' => $iconFactory->getIcon($this->getSystemLanguageValue($languageValue, $pageId, 'flagIcon'), Icon::SIZE_SMALL)->render()
206
                    ];
207
                    $versionArray['allowedAction_nextStage'] = $isRecordTypeAllowedToModify && $stagesObj->isNextStageAllowedForUser($versionRecord['t3ver_stage']);
208
                    $versionArray['allowedAction_prevStage'] = $isRecordTypeAllowedToModify && $stagesObj->isPrevStageAllowedForUser($versionRecord['t3ver_stage']);
209
                    if ($swapAccess && $swapStage != 0 && $versionRecord['t3ver_stage'] == $swapStage) {
210
                        $versionArray['allowedAction_swap'] = $isRecordTypeAllowedToModify && $stagesObj->isNextStageAllowedForUser($swapStage);
211
                    } elseif ($swapAccess && $swapStage == 0) {
212
                        $versionArray['allowedAction_swap'] = $isRecordTypeAllowedToModify;
213
                    } else {
214
                        $versionArray['allowedAction_swap'] = false;
215
                    }
216
                    $versionArray['allowedAction_delete'] = $isRecordTypeAllowedToModify;
217
                    // preview and editing of a deleted page won't work ;)
218
                    $versionArray['allowedAction_view'] = !$isDeletedPage && $viewUrl;
219
                    $versionArray['allowedAction_edit'] = $isRecordTypeAllowedToModify && !$isDeletedPage;
220
                    $versionArray['allowedAction_editVersionedPage'] = $isRecordTypeAllowedToModify && !$isDeletedPage;
221
                    $versionArray['state_Workspace'] = $recordState;
222
223
                    $versionArray = array_merge(
224
                        $versionArray,
225
                        $this->getAdditionalColumnService()->getData($combinedRecord)
226
                    );
227
228
                    if ($filterTxt == '' || $this->isFilterTextInVisibleColumns($filterTxt, $versionArray)) {
229
                        $versionIdentifier = $versionArray['id'];
230
                        $this->dataArray[$versionIdentifier] = $versionArray;
231
                    }
232
                }
233
            }
234
235
            // Trigger a PSR-14 event
236
            $event = new AfterCompiledCacheableDataForWorkspaceEvent($this, $this->dataArray, $versions);
237
            $this->eventDispatcher->dispatch($event);
238
            $this->dataArray = $event->getData();
239
            $versions = $event->getVersions();
240
            // Enrich elements after everything has been processed:
241
            foreach ($this->dataArray as &$element) {
242
                $identifier = $element['table'] . ':' . $element['t3ver_oid'];
243
                $element['integrity'] = [
244
                    'status' => $this->getIntegrityService()->getStatusRepresentation($identifier),
245
                    'messages' => htmlspecialchars($this->getIntegrityService()->getIssueMessages($identifier, true))
246
                ];
247
            }
248
            $this->setDataArrayIntoCache($versions, $filterTxt);
249
        }
250
251
        // Trigger a PSR-14 event
252
        $event = new AfterDataGeneratedForWorkspaceEvent($this, $this->dataArray, $versions);
253
        $this->eventDispatcher->dispatch($event);
254
        $this->dataArray = $event->getData();
255
        $this->sortDataArray();
256
        $this->resolveDataArrayDependencies();
257
    }
258
259
    /**
260
     * Resolves dependencies of nested structures
261
     * and sort data elements considering these dependencies.
262
     */
263
    protected function resolveDataArrayDependencies()
264
    {
265
        $collectionService = $this->getDependencyCollectionService();
266
        $dependencyResolver = $collectionService->getDependencyResolver();
267
268
        foreach ($this->dataArray as $dataElement) {
269
            $dependencyResolver->addElement($dataElement['table'], $dataElement['uid']);
270
        }
271
272
        $this->dataArray = $collectionService->process($this->dataArray);
273
    }
274
275
    /**
276
     * Gets the data array by considering the page to be shown in the grid view.
277
     *
278
     * @param int $start
279
     * @param int $limit
280
     * @return array
281
     */
282
    protected function getDataArray($start, $limit)
283
    {
284
        $dataArrayPart = [];
285
        $dataArrayCount = count($this->dataArray);
286
        $end = ($start + $limit < $dataArrayCount ? $start + $limit : $dataArrayCount);
287
288
        // Ensure that there are numerical indexes
289
        $this->dataArray = array_values($this->dataArray);
290
        for ($i = $start; $i < $end; $i++) {
291
            $dataArrayPart[] = $this->dataArray[$i];
292
        }
293
294
        // Ensure that collections are not cut for the pagination
295
        if (!empty($this->dataArray[$i][self::GridColumn_Collection])) {
296
            $collectionIdentifier = $this->dataArray[$i][self::GridColumn_Collection];
297
            for ($i = $i + 1; $i < $dataArrayCount && $collectionIdentifier === $this->dataArray[$i][self::GridColumn_Collection]; $i++) {
298
                $dataArrayPart[] = $this->dataArray[$i];
299
            }
300
        }
301
302
        // Trigger a PSR-14 event
303
        $event = new GetVersionedDataEvent($this, $this->dataArray, $start, $limit, $dataArrayPart);
304
        $this->eventDispatcher->dispatch($event);
305
        $this->dataArray = $event->getData();
306
        return $event->getDataArrayPart();
307
    }
308
309
    /**
310
     * Initializes the workspace cache
311
     */
312
    protected function initializeWorkspacesCachingFramework()
313
    {
314
        $this->workspacesCache = GeneralUtility::makeInstance(CacheManager::class)->getCache('workspaces_cache');
315
    }
316
317
    /**
318
     * Puts the generated dataArray into the workspace cache.
319
     *
320
     * @param array $versions All records uids etc. First key is table name, second key incremental integer. Records are associative arrays with uid and t3ver_oid fields. The pid of the online record is found as "livepid" the pid of the offline record is found in "wspid
321
     * @param string $filterTxt The given filter text from the grid.
322
     */
323
    protected function setDataArrayIntoCache(array $versions, $filterTxt)
324
    {
325
        $hash = $this->calculateHash($versions, $filterTxt);
326
        $this->workspacesCache->set(
327
            $hash,
328
            $this->dataArray,
329
            [
330
                (string)$this->currentWorkspace,
331
                'user_' . $this->getBackendUser()->user['uid']
332
            ]
333
        );
334
    }
335
336
    /**
337
     * Checks if a cache entry is given for given versions and filter text and tries to load the data array from cache.
338
     *
339
     * @param array $versions All records uids etc. First key is table name, second key incremental integer. Records are associative arrays with uid and t3ver_oid fields. The pid of the online record is found as "livepid" the pid of the offline record is found in "wspid
340
     * @param string $filterTxt The given filter text from the grid.
341
     * @return bool TRUE if cache entry was successfully fetched from cache and content put to $this->dataArray
342
     */
343
    protected function getDataArrayFromCache(array $versions, $filterTxt)
344
    {
345
        $cacheEntry = false;
346
        $hash = $this->calculateHash($versions, $filterTxt);
347
        $content = $this->workspacesCache->get($hash);
348
        if ($content !== false) {
349
            $this->dataArray = $content;
350
            $cacheEntry = true;
351
        }
352
        return $cacheEntry;
353
    }
354
355
    /**
356
     * Calculates the hash value of the used workspace, the user id, the versions array, the filter text, the sorting attribute, the workspace selected in grid and the sorting direction.
357
     *
358
     * @param array $versions All records uids etc. First key is table name, second key incremental integer. Records are associative arrays with uid and t3ver_oid fields. The pid of the online record is found as "livepid" the pid of the offline record is found in "wspid
359
     * @param string $filterTxt The given filter text from the grid.
360
     * @return string
361
     */
362
    protected function calculateHash(array $versions, $filterTxt)
363
    {
364
        $backendUser = $this->getBackendUser();
365
        $hashArray = [
366
            $backendUser->workspace,
367
            $backendUser->user['uid'],
368
            $versions,
369
            $filterTxt,
370
            $this->sort,
371
            $this->sortDir,
372
            $this->currentWorkspace
373
        ];
374
        $hash = md5(serialize($hashArray));
375
        return $hash;
376
    }
377
378
    /**
379
     * Performs sorting on the data array accordant to the
380
     * selected column in the grid view to be used for sorting.
381
     */
382
    protected function sortDataArray()
383
    {
384
        if (is_array($this->dataArray)) {
0 ignored issues
show
introduced by
The condition is_array($this->dataArray) is always true.
Loading history...
385
            switch ($this->sort) {
386
                case 'uid':
387
                case 'change':
388
                case 'workspace_Tstamp':
389
                case 't3ver_oid':
390
                case 'liveid':
391
                case 'livepid':
392
                case 'languageValue':
393
                    uasort($this->dataArray, [$this, 'intSort']);
394
                    break;
395
                case 'label_Workspace':
396
                case 'label_Live':
397
                case 'label_Stage':
398
                case 'workspace_Title':
399
                case 'path_Live':
400
                    // case 'path_Workspace': This is the first sorting attribute
401
                    uasort($this->dataArray, [$this, 'stringSort']);
402
                    break;
403
                default:
404
                    // Do nothing
405
            }
406
        } else {
407
            $this->logger->critical('Try to sort "' . $this->sort . '" in "\\TYPO3\\CMS\\Workspaces\\Service\\GridDataService::sortDataArray" but $this->dataArray is empty! This might be the bug #26422 which could not be reproduced yet.');
408
        }
409
        // Trigger an event for extensibility
410
        $event = new SortVersionedDataEvent($this, $this->dataArray, $this->sort, $this->sortDir);
411
        $this->eventDispatcher->dispatch($event);
412
        $this->dataArray = $event->getData();
413
        $this->sort = $event->getSortColumn();
414
        $this->sortDir = $event->getSortDirection();
415
    }
416
417
    /**
418
     * Implements individual sorting for columns based on integer comparison.
419
     *
420
     * @param array $a First value
421
     * @param array $b Second value
422
     * @return int
423
     */
424
    protected function intSort(array $a, array $b)
425
    {
426
        if (!$this->isSortable($a, $b)) {
427
            return 0;
428
        }
429
        // First sort by using the page-path in current workspace
430
        $path_cmp = strcasecmp($a['path_Workspace'], $b['path_Workspace']);
431
        if ($path_cmp < 0) {
432
            return $path_cmp;
433
        }
434
        if ($path_cmp == 0) {
435
            if ($a[$this->sort] == $b[$this->sort]) {
436
                return 0;
437
            }
438
            if ($this->sortDir === 'ASC') {
439
                return $a[$this->sort] < $b[$this->sort] ? -1 : 1;
440
            }
441
            if ($this->sortDir === 'DESC') {
442
                return $a[$this->sort] > $b[$this->sort] ? -1 : 1;
443
            }
444
        } elseif ($path_cmp > 0) {
445
            return $path_cmp;
446
        }
447
        return 0;
448
    }
449
450
    /**
451
     * Implements individual sorting for columns based on string comparison.
452
     *
453
     * @param string $a First value
454
     * @param string $b Second value
455
     * @return int
456
     */
457
    protected function stringSort($a, $b)
458
    {
459
        if (!$this->isSortable($a, $b)) {
0 ignored issues
show
Bug introduced by
$b of type string is incompatible with the type array expected by parameter $b of TYPO3\CMS\Workspaces\Ser...taService::isSortable(). ( Ignorable by Annotation )

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

459
        if (!$this->isSortable($a, /** @scrutinizer ignore-type */ $b)) {
Loading history...
Bug introduced by
$a of type string is incompatible with the type array expected by parameter $a of TYPO3\CMS\Workspaces\Ser...taService::isSortable(). ( Ignorable by Annotation )

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

459
        if (!$this->isSortable(/** @scrutinizer ignore-type */ $a, $b)) {
Loading history...
460
            return 0;
461
        }
462
        $path_cmp = strcasecmp($a['path_Workspace'], $b['path_Workspace']);
463
        if ($path_cmp < 0) {
464
            return $path_cmp;
465
        }
466
        if ($path_cmp == 0) {
467
            if ($a[$this->sort] == $b[$this->sort]) {
468
                return 0;
469
            }
470
            if ($this->sortDir === 'ASC') {
471
                return strcasecmp($a[$this->sort], $b[$this->sort]);
472
            }
473
            if ($this->sortDir === 'DESC') {
474
                return strcasecmp($a[$this->sort], $b[$this->sort]) * -1;
475
            }
476
        } elseif ($path_cmp > 0) {
477
            return $path_cmp;
478
        }
479
        return 0;
480
    }
481
482
    /**
483
     * Determines whether dataArray elements are sortable.
484
     * Only elements on the first level (0) or below the same
485
     * parent element are directly sortable.
486
     *
487
     * @param array $a
488
     * @param array $b
489
     * @return bool
490
     */
491
    protected function isSortable(array $a, array $b)
492
    {
493
        return
494
            $a[self::GridColumn_CollectionLevel] === 0 && $b[self::GridColumn_CollectionLevel] === 0
495
            || $a[self::GridColumn_CollectionParent] === $b[self::GridColumn_CollectionParent]
496
        ;
0 ignored issues
show
Coding Style introduced by
Space found before semicolon; expected "];" but found "]
;"
Loading history...
497
    }
498
499
    /**
500
     * Determines whether the text used to filter the results is part of
501
     * a column that is visible in the grid view.
502
     *
503
     * @param string $filterText
504
     * @param array $versionArray
505
     * @return bool
506
     */
507
    protected function isFilterTextInVisibleColumns($filterText, array $versionArray)
508
    {
509
        $backendUser = $this->getBackendUser();
510
        if (is_array($backendUser->uc['moduleData']['Workspaces'][$backendUser->workspace]['columns'])) {
511
            $visibleColumns = $backendUser->uc['moduleData']['Workspaces'][$backendUser->workspace]['columns'];
512
        } else {
513
            $visibleColumns = [
514
                'workspace_Formated_Tstamp' => ['hidden' => 0],
515
                'change' => ['hidden' => 0],
516
                'path_Workspace' => ['hidden' => 0],
517
                'path_Live' => ['hidden' => 0],
518
                'label_Live' => ['hidden' => 0],
519
                'label_Stage' => ['hidden' => 0],
520
                'label_Workspace' => ['hidden' => 0],
521
            ];
522
        }
523
        foreach ($visibleColumns as $column => $value) {
524
            if (isset($value['hidden']) && isset($column) && isset($versionArray[$column])) {
525
                if ($value['hidden'] == 0) {
526
                    switch ($column) {
527
                        case 'workspace_Tstamp':
528
                            if (stripos($versionArray['workspace_Formated_Tstamp'], $filterText) !== false) {
529
                                return true;
530
                            }
531
                            break;
532
                        case 'change':
533
                            if (stripos((string)$versionArray[$column], str_replace('%', '', $filterText)) !== false) {
534
                                return true;
535
                            }
536
                            break;
537
                        default:
538
                            if (stripos((string)$versionArray[$column], $filterText) !== false) {
539
                                return true;
540
                            }
541
                    }
542
                }
543
            }
544
        }
545
        return false;
546
    }
547
548
    /**
549
     * Gets the state of a given state value.
550
     *
551
     * @param int $stateId stateId of offline record
552
     * @param bool $hiddenOnline hidden status of online record
553
     * @param bool $hiddenOffline hidden status of offline record
554
     * @return string
555
     */
556
    protected function workspaceState($stateId, $hiddenOnline = false, $hiddenOffline = false)
557
    {
558
        $hiddenState = null;
559
        if ($hiddenOnline == 0 && $hiddenOffline == 1) {
560
            $hiddenState = 'hidden';
561
        } elseif ($hiddenOnline == 1 && $hiddenOffline == 0) {
562
            $hiddenState = 'unhidden';
563
        }
564
        switch ($stateId) {
565
            case VersionState::NEW_PLACEHOLDER_VERSION:
566
                $state = 'new';
567
                break;
568
            case VersionState::DELETE_PLACEHOLDER:
569
                $state = 'deleted';
570
                break;
571
            case VersionState::MOVE_POINTER:
572
                $state = 'moved';
573
                break;
574
            default:
575
                $state = ($hiddenState ?: 'modified');
576
        }
577
        return $state;
578
    }
579
580
    /**
581
     * Gets the field name of the enable-columns as defined in $TCA.
582
     *
583
     * @param string $table Name of the table
584
     * @param string $type Type to be fetches (e.g. 'disabled', 'starttime', 'endtime', 'fe_group)
585
     * @return string|null The accordant field name or NULL if not defined
586
     */
587
    protected function getTcaEnableColumnsFieldName($table, $type)
588
    {
589
        $fieldName = null;
590
591
        if (!empty($GLOBALS['TCA'][$table]['ctrl']['enablecolumns'][$type])) {
592
            $fieldName = $GLOBALS['TCA'][$table]['ctrl']['enablecolumns'][$type];
593
        }
594
595
        return $fieldName;
596
    }
597
598
    /**
599
     * Gets the used language value (sys_language.uid) of
600
     * a given database record.
601
     *
602
     * @param string $table Name of the table
603
     * @param array $record Database record
604
     * @return int
605
     */
606
    protected function getLanguageValue($table, array $record)
607
    {
608
        $languageValue = 0;
609
        if (BackendUtility::isTableLocalizable($table)) {
610
            $languageField = $GLOBALS['TCA'][$table]['ctrl']['languageField'];
611
            if (!empty($record[$languageField])) {
612
                $languageValue = $record[$languageField];
613
            }
614
        }
615
        return $languageValue;
616
    }
617
618
    /**
619
     * Gets a named value of the available sys_language elements.
620
     *
621
     * @param int $id sys_language uid
622
     * @param int $pageId page id of a site
623
     * @param string $key Name of the value to be fetched (e.g. title)
624
     * @return string|null
625
     * @see getSystemLanguages
626
     */
627
    protected function getSystemLanguageValue($id, $pageId, $key)
628
    {
629
        $value = null;
630
        $systemLanguages = $this->getSystemLanguages((int)$pageId);
631
        if (!empty($systemLanguages[$id][$key])) {
632
            $value = $systemLanguages[$id][$key];
633
        }
634
        return $value;
635
    }
636
637
    /**
638
     * Gets all available system languages.
639
     *
640
     * @param int $pageId
641
     * @return array
642
     */
643
    public function getSystemLanguages(int $pageId)
644
    {
645
        return GeneralUtility::makeInstance(TranslationConfigurationProvider::class)->getSystemLanguages($pageId);
646
    }
647
648
    /**
649
     * Gets an instance of the integrity service.
650
     *
651
     * @return IntegrityService
652
     */
653
    protected function getIntegrityService()
654
    {
655
        if (!isset($this->integrityService)) {
656
            $this->integrityService = GeneralUtility::makeInstance(IntegrityService::class);
657
        }
658
        return $this->integrityService;
659
    }
660
661
    /**
662
     * @return Dependency\CollectionService
663
     */
664
    protected function getDependencyCollectionService()
665
    {
666
        return GeneralUtility::makeInstance(CollectionService::class);
667
    }
668
669
    /**
670
     * @return AdditionalColumnService
671
     */
672
    protected function getAdditionalColumnService()
673
    {
674
        return GeneralUtility::makeInstance(AdditionalColumnService::class);
675
    }
676
677
    /**
678
     * @return BackendUserAuthentication
679
     */
680
    protected function getBackendUser()
681
    {
682
        return $GLOBALS['BE_USER'];
683
    }
684
}
685