[CLEANUP] The correct case must be used for standard PHP types in phpdoc
[Packages/TYPO3.CMS.git] / typo3 / sysext / workspaces / Classes / Service / GridDataService.php
1 <?php
2 namespace TYPO3\CMS\Workspaces\Service;
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\Imaging\Icon;
19 use TYPO3\CMS\Core\Imaging\IconFactory;
20 use TYPO3\CMS\Core\Utility\GeneralUtility;
21
22 /**
23 * Grid data service
24 */
25 class GridDataService
26 {
27 const SIGNAL_GenerateDataArray_BeforeCaching = 'generateDataArray.beforeCaching';
28 const SIGNAL_GenerateDataArray_PostProcesss = 'generateDataArray.postProcess';
29 const SIGNAL_GetDataArray_PostProcesss = 'getDataArray.postProcess';
30 const SIGNAL_SortDataArray_PostProcesss = 'sortDataArray.postProcess';
31
32 const GridColumn_Collection = 'Workspaces_Collection';
33 const GridColumn_CollectionLevel = 'Workspaces_CollectionLevel';
34 const GridColumn_CollectionParent = 'Workspaces_CollectionParent';
35 const GridColumn_CollectionCurrent = 'Workspaces_CollectionCurrent';
36 const GridColumn_CollectionChildren = 'Workspaces_CollectionChildren';
37
38 /**
39 * Id of the current active workspace.
40 *
41 * @var int
42 */
43 protected $currentWorkspace = null;
44
45 /**
46 * Version record information (filtered, sorted and limited)
47 *
48 * @var array
49 */
50 protected $dataArray = [];
51
52 /**
53 * Name of the field used for sorting.
54 *
55 * @var string
56 */
57 protected $sort = '';
58
59 /**
60 * Direction used for sorting (ASC, DESC).
61 *
62 * @var string
63 */
64 protected $sortDir = '';
65
66 /**
67 * @var \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface
68 */
69 protected $workspacesCache = null;
70
71 /**
72 * @var array
73 */
74 protected $systemLanguages;
75
76 /**
77 * @var \TYPO3\CMS\Workspaces\Service\IntegrityService
78 */
79 protected $integrityService;
80
81 /**
82 * Generates grid list array from given versions.
83 *
84 * @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
85 * @param \stdClass $parameter Parameters as submitted by JavaScript component
86 * @param int $currentWorkspace The current workspace
87 * @return array Version record information (filtered, sorted and limited)
88 * @throws \InvalidArgumentException
89 */
90 public function generateGridListFromVersions($versions, $parameter, $currentWorkspace)
91 {
92 // Read the given parameters from grid. If the parameter is not set use default values.
93 $filterTxt = isset($parameter->filterTxt) ? $parameter->filterTxt : '';
94 $start = isset($parameter->start) ? (int)$parameter->start : 0;
95 $limit = isset($parameter->limit) ? (int)$parameter->limit : 30;
96 $this->sort = isset($parameter->sort) ? $parameter->sort : 't3ver_oid';
97 $this->sortDir = isset($parameter->dir) ? $parameter->dir : 'ASC';
98 if (is_int($currentWorkspace)) {
99 $this->currentWorkspace = $currentWorkspace;
100 } else {
101 throw new \InvalidArgumentException('No such workspace defined', 1476048304);
102 }
103 $data = [];
104 $data['data'] = [];
105 $this->generateDataArray($versions, $filterTxt);
106 $data['total'] = count($this->dataArray);
107 $data['data'] = $this->getDataArray($start, $limit);
108 return $data;
109 }
110
111 /**
112 * Generates grid list array from given versions.
113 *
114 * @param array $versions All available version records
115 * @param string $filterTxt Text to be used to filter record result
116 */
117 protected function generateDataArray(array $versions, $filterTxt)
118 {
119 $workspaceAccess = $GLOBALS['BE_USER']->checkWorkspace($GLOBALS['BE_USER']->workspace);
120 $swapStage = $workspaceAccess['publish_access'] & 1 ? \TYPO3\CMS\Workspaces\Service\StagesService::STAGE_PUBLISH_ID : 0;
121 $swapAccess = $GLOBALS['BE_USER']->workspacePublishAccess($GLOBALS['BE_USER']->workspace) && $GLOBALS['BE_USER']->workspaceSwapAccess();
122 $this->initializeWorkspacesCachingFramework();
123 /** @var IconFactory $iconFactory */
124 $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
125 // check for dataArray in cache
126 if ($this->getDataArrayFromCache($versions, $filterTxt) === false) {
127 /** @var $stagesObj \TYPO3\CMS\Workspaces\Service\StagesService */
128 $stagesObj = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\StagesService::class);
129 $defaultGridColumns = [
130 self::GridColumn_Collection => 0,
131 self::GridColumn_CollectionLevel => 0,
132 self::GridColumn_CollectionParent => '',
133 self::GridColumn_CollectionCurrent => '',
134 self::GridColumn_CollectionChildren => 0,
135 ];
136 foreach ($versions as $table => $records) {
137 $hiddenField = $this->getTcaEnableColumnsFieldName($table, 'disabled');
138 $isRecordTypeAllowedToModify = $GLOBALS['BE_USER']->check('tables_modify', $table);
139
140 foreach ($records as $record) {
141 $origRecord = BackendUtility::getRecord($table, $record['t3ver_oid']);
142 $versionRecord = BackendUtility::getRecord($table, $record['uid']);
143 $combinedRecord = \TYPO3\CMS\Workspaces\Domain\Model\CombinedRecord::createFromArrays($table, $origRecord, $versionRecord);
144 $this->getIntegrityService()->checkElement($combinedRecord);
145
146 if ($hiddenField !== null) {
147 $recordState = $this->workspaceState($versionRecord['t3ver_state'], $origRecord[$hiddenField], $versionRecord[$hiddenField]);
148 } else {
149 $recordState = $this->workspaceState($versionRecord['t3ver_state']);
150 }
151
152 $isDeletedPage = $table === 'pages' && $recordState === 'deleted';
153 $viewUrl = \TYPO3\CMS\Workspaces\Service\WorkspaceService::viewSingleRecord($table, $record['uid'], $origRecord, $versionRecord);
154 $versionArray = [];
155 $versionArray['table'] = $table;
156 $versionArray['id'] = $table . ':' . $record['uid'];
157 $versionArray['uid'] = $record['uid'];
158 $versionArray['workspace'] = $versionRecord['t3ver_id'];
159 $versionArray = array_merge($versionArray, $defaultGridColumns);
160 $versionArray['label_Workspace'] = htmlspecialchars(BackendUtility::getRecordTitle($table, $versionRecord));
161 $versionArray['label_Live'] = htmlspecialchars(BackendUtility::getRecordTitle($table, $origRecord));
162 $versionArray['label_Stage'] = htmlspecialchars($stagesObj->getStageTitle($versionRecord['t3ver_stage']));
163 $tempStage = $stagesObj->getNextStage($versionRecord['t3ver_stage']);
164 $versionArray['label_nextStage'] = htmlspecialchars($stagesObj->getStageTitle($tempStage['uid']));
165 $versionArray['value_nextStage'] = (int)$tempStage['uid'];
166 $tempStage = $stagesObj->getPrevStage($versionRecord['t3ver_stage']);
167 $versionArray['label_prevStage'] = htmlspecialchars($stagesObj->getStageTitle($tempStage['uid']));
168 $versionArray['value_prevStage'] = (int)$tempStage['uid'];
169 $versionArray['path_Live'] = htmlspecialchars(BackendUtility::getRecordPath($record['livepid'], '', 999));
170 $versionArray['path_Workspace'] = htmlspecialchars(BackendUtility::getRecordPath($record['wspid'], '', 999));
171 $versionArray['workspace_Title'] = htmlspecialchars(\TYPO3\CMS\Workspaces\Service\WorkspaceService::getWorkspaceTitle($versionRecord['t3ver_wsid']));
172 $versionArray['workspace_Tstamp'] = $versionRecord['tstamp'];
173 $versionArray['workspace_Formated_Tstamp'] = BackendUtility::datetime($versionRecord['tstamp']);
174 $versionArray['t3ver_wsid'] = $versionRecord['t3ver_wsid'];
175 $versionArray['t3ver_oid'] = $record['t3ver_oid'];
176 $versionArray['livepid'] = $record['livepid'];
177 $versionArray['stage'] = $versionRecord['t3ver_stage'];
178 $versionArray['icon_Live'] = $iconFactory->getIconForRecord($table, $origRecord, Icon::SIZE_SMALL)->render();
179 $versionArray['icon_Workspace'] = $iconFactory->getIconForRecord($table, $versionRecord, Icon::SIZE_SMALL)->render();
180 $languageValue = $this->getLanguageValue($table, $versionRecord);
181 $versionArray['languageValue'] = $languageValue;
182 $versionArray['language'] = [
183 'icon' => $iconFactory->getIcon($this->getSystemLanguageValue($languageValue, 'flagIcon'), Icon::SIZE_SMALL)->render()
184 ];
185 $versionArray['allowedAction_nextStage'] = $isRecordTypeAllowedToModify && $stagesObj->isNextStageAllowedForUser($versionRecord['t3ver_stage']);
186 $versionArray['allowedAction_prevStage'] = $isRecordTypeAllowedToModify && $stagesObj->isPrevStageAllowedForUser($versionRecord['t3ver_stage']);
187 if ($swapAccess && $swapStage != 0 && $versionRecord['t3ver_stage'] == $swapStage) {
188 $versionArray['allowedAction_swap'] = $isRecordTypeAllowedToModify && $stagesObj->isNextStageAllowedForUser($swapStage);
189 } elseif ($swapAccess && $swapStage == 0) {
190 $versionArray['allowedAction_swap'] = $isRecordTypeAllowedToModify;
191 } else {
192 $versionArray['allowedAction_swap'] = false;
193 }
194 $versionArray['allowedAction_delete'] = $isRecordTypeAllowedToModify;
195 // preview and editing of a deleted page won't work ;)
196 $versionArray['allowedAction_view'] = !$isDeletedPage && $viewUrl;
197 $versionArray['allowedAction_edit'] = $isRecordTypeAllowedToModify && !$isDeletedPage;
198 $versionArray['allowedAction_editVersionedPage'] = $isRecordTypeAllowedToModify && !$isDeletedPage;
199 $versionArray['state_Workspace'] = $recordState;
200
201 $versionArray = array_merge(
202 $versionArray,
203 $this->getAdditionalColumnService()->getData($combinedRecord)
204 );
205
206 if ($filterTxt == '' || $this->isFilterTextInVisibleColumns($filterTxt, $versionArray)) {
207 $versionIdentifier = $versionArray['id'];
208 $this->dataArray[$versionIdentifier] = $versionArray;
209 }
210 }
211 }
212 // Suggested slot method:
213 // methodName(\TYPO3\CMS\Workspaces\Service\GridDataService $gridData, array $dataArray, array $versions)
214 list($this->dataArray, $versions) = $this->emitSignal(self::SIGNAL_GenerateDataArray_BeforeCaching, $this->dataArray, $versions);
215 // Enrich elements after everything has been processed:
216 foreach ($this->dataArray as &$element) {
217 $identifier = $element['table'] . ':' . $element['t3ver_oid'];
218 $element['integrity'] = [
219 'status' => $this->getIntegrityService()->getStatusRepresentation($identifier),
220 'messages' => htmlspecialchars($this->getIntegrityService()->getIssueMessages($identifier, true))
221 ];
222 }
223 $this->setDataArrayIntoCache($versions, $filterTxt);
224 }
225 // Suggested slot method:
226 // methodName(\TYPO3\CMS\Workspaces\Service\GridDataService $gridData, array $dataArray, array $versions)
227 list($this->dataArray, $versions) = $this->emitSignal(self::SIGNAL_GenerateDataArray_PostProcesss, $this->dataArray, $versions);
228 $this->sortDataArray();
229 $this->resolveDataArrayDependencies();
230 }
231
232 /**
233 * Resolves dependencies of nested structures
234 * and sort data elements considering these dependencies.
235 */
236 protected function resolveDataArrayDependencies()
237 {
238 $collectionService = $this->getDependencyCollectionService();
239 $dependencyResolver = $collectionService->getDependencyResolver();
240
241 foreach ($this->dataArray as $dataElement) {
242 $dependencyResolver->addElement($dataElement['table'], $dataElement['uid']);
243 }
244
245 $this->dataArray = $collectionService->process($this->dataArray);
246 }
247
248 /**
249 * Gets the data array by considering the page to be shown in the grid view.
250 *
251 * @param int $start
252 * @param int $limit
253 * @return array
254 */
255 protected function getDataArray($start, $limit)
256 {
257 $dataArrayPart = [];
258 $dataArrayCount = count($this->dataArray);
259 $end = ($start + $limit < $dataArrayCount ? $start + $limit : $dataArrayCount);
260
261 // Ensure that there are numerical indexes
262 $this->dataArray = array_values(($this->dataArray));
263 for ($i = $start; $i < $end; $i++) {
264 $dataArrayPart[] = $this->dataArray[$i];
265 }
266
267 // Ensure that collections are not cut for the pagination
268 if (!empty($this->dataArray[$i][self::GridColumn_Collection])) {
269 $collectionIdentifier = $this->dataArray[$i][self::GridColumn_Collection];
270 for ($i = $i + 1; $i < $dataArrayCount && $collectionIdentifier === $this->dataArray[$i][self::GridColumn_Collection]; $i++) {
271 $dataArrayPart[] = $this->dataArray[$i];
272 }
273 }
274
275 // Suggested slot method:
276 // methodName(\TYPO3\CMS\Workspaces\Service\GridDataService $gridData, array $dataArray, $start, $limit, array $dataArrayPart)
277 list($this->dataArray, $start, $limit, $dataArrayPart) = $this->emitSignal(self::SIGNAL_GetDataArray_PostProcesss, $this->dataArray, $start, $limit, $dataArrayPart);
278 return $dataArrayPart;
279 }
280
281 /**
282 * Initializes the workspace cache
283 */
284 protected function initializeWorkspacesCachingFramework()
285 {
286 $this->workspacesCache = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Cache\CacheManager::class)->getCache('workspaces_cache');
287 }
288
289 /**
290 * Puts the generated dataArray into the workspace cache.
291 *
292 * @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
293 * @param string $filterTxt The given filter text from the grid.
294 */
295 protected function setDataArrayIntoCache(array $versions, $filterTxt)
296 {
297 $hash = $this->calculateHash($versions, $filterTxt);
298 $this->workspacesCache->set($hash, $this->dataArray, [$this->currentWorkspace, 'user_' . $GLOBALS['BE_USER']->user['uid']]);
299 }
300
301 /**
302 * Checks if a cache entry is given for given versions and filter text and tries to load the data array from cache.
303 *
304 * @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
305 * @param string $filterTxt The given filter text from the grid.
306 * @return bool TRUE if cache entry was successfully fetched from cache and content put to $this->dataArray
307 */
308 protected function getDataArrayFromCache(array $versions, $filterTxt)
309 {
310 $cacheEntry = false;
311 $hash = $this->calculateHash($versions, $filterTxt);
312 $content = $this->workspacesCache->get($hash);
313 if ($content !== false) {
314 $this->dataArray = $content;
315 $cacheEntry = true;
316 }
317 return $cacheEntry;
318 }
319
320 /**
321 * 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.
322 *
323 * @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
324 * @param string $filterTxt The given filter text from the grid.
325 * @return string
326 */
327 protected function calculateHash(array $versions, $filterTxt)
328 {
329 $hashArray = [
330 $GLOBALS['BE_USER']->workspace,
331 $GLOBALS['BE_USER']->user['uid'],
332 $versions,
333 $filterTxt,
334 $this->sort,
335 $this->sortDir,
336 $this->currentWorkspace
337 ];
338 $hash = md5(serialize($hashArray));
339 return $hash;
340 }
341
342 /**
343 * Performs sorting on the data array accordant to the
344 * selected column in the grid view to be used for sorting.
345 */
346 protected function sortDataArray()
347 {
348 if (is_array($this->dataArray)) {
349 switch ($this->sort) {
350 case 'uid':
351 case 'change':
352 case 'workspace_Tstamp':
353 case 't3ver_oid':
354 case 'liveid':
355 case 'livepid':
356 case 'languageValue':
357 uasort($this->dataArray, [$this, 'intSort']);
358 break;
359 case 'label_Workspace':
360 case 'label_Live':
361 case 'label_Stage':
362 case 'workspace_Title':
363 case 'path_Live':
364 // case 'path_Workspace': This is the first sorting attribute
365 uasort($this->dataArray, [$this, 'stringSort']);
366 break;
367 default:
368 // Do nothing
369 }
370 } else {
371 GeneralUtility::sysLog(
372 '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 reproduced yet.',
373 'workspaces',
374 GeneralUtility::SYSLOG_SEVERITY_ERROR
375 );
376 }
377 // Suggested slot method:
378 // methodName(\TYPO3\CMS\Workspaces\Service\GridDataService $gridData, array $dataArray, $sortColumn, $sortDirection)
379 list($this->dataArray, $this->sort, $this->sortDir) = $this->emitSignal(self::SIGNAL_SortDataArray_PostProcesss, $this->dataArray, $this->sort, $this->sortDir);
380 }
381
382 /**
383 * Implements individual sorting for columns based on integer comparison.
384 *
385 * @param array $a First value
386 * @param array $b Second value
387 * @return int
388 */
389 protected function intSort(array $a, array $b)
390 {
391 if (!$this->isSortable($a, $b)) {
392 return 0;
393 }
394 // First sort by using the page-path in current workspace
395 $path_cmp = strcasecmp($a['path_Workspace'], $b['path_Workspace']);
396 if ($path_cmp < 0) {
397 return $path_cmp;
398 }
399 if ($path_cmp == 0) {
400 if ($a[$this->sort] == $b[$this->sort]) {
401 return 0;
402 }
403 if ($this->sortDir === 'ASC') {
404 return $a[$this->sort] < $b[$this->sort] ? -1 : 1;
405 }
406 if ($this->sortDir === 'DESC') {
407 return $a[$this->sort] > $b[$this->sort] ? -1 : 1;
408 }
409 } elseif ($path_cmp > 0) {
410 return $path_cmp;
411 }
412 return 0;
413 }
414
415 /**
416 * Implements individual sorting for columns based on string comparison.
417 *
418 * @param string $a First value
419 * @param string $b Second value
420 * @return int
421 */
422 protected function stringSort($a, $b)
423 {
424 if (!$this->isSortable($a, $b)) {
425 return 0;
426 }
427 $path_cmp = strcasecmp($a['path_Workspace'], $b['path_Workspace']);
428 if ($path_cmp < 0) {
429 return $path_cmp;
430 }
431 if ($path_cmp == 0) {
432 if ($a[$this->sort] == $b[$this->sort]) {
433 return 0;
434 }
435 if ($this->sortDir === 'ASC') {
436 return strcasecmp($a[$this->sort], $b[$this->sort]);
437 }
438 if ($this->sortDir === 'DESC') {
439 return strcasecmp($a[$this->sort], $b[$this->sort]) * -1;
440 }
441 } elseif ($path_cmp > 0) {
442 return $path_cmp;
443 }
444 return 0;
445 }
446
447 /**
448 * Determines whether dataArray elements are sortable.
449 * Only elements on the first level (0) or below the same
450 * parent element are directly sortable.
451 *
452 * @param array $a
453 * @param array $b
454 * @return bool
455 */
456 protected function isSortable(array $a, array $b)
457 {
458 return
459 $a[self::GridColumn_CollectionLevel] === 0 && $b[self::GridColumn_CollectionLevel] === 0
460 || $a[self::GridColumn_CollectionParent] === $b[self::GridColumn_CollectionParent]
461 ;
462 }
463
464 /**
465 * Determines whether the text used to filter the results is part of
466 * a column that is visible in the grid view.
467 *
468 * @param string $filterText
469 * @param array $versionArray
470 * @return bool
471 */
472 protected function isFilterTextInVisibleColumns($filterText, array $versionArray)
473 {
474 if (is_array($GLOBALS['BE_USER']->uc['moduleData']['Workspaces'][$GLOBALS['BE_USER']->workspace]['columns'])) {
475 foreach ($GLOBALS['BE_USER']->uc['moduleData']['Workspaces'][$GLOBALS['BE_USER']->workspace]['columns'] as $column => $value) {
476 if (isset($value['hidden']) && isset($column) && isset($versionArray[$column])) {
477 if ($value['hidden'] == 0) {
478 switch ($column) {
479 case 'workspace_Tstamp':
480 if (stripos($versionArray['workspace_Formated_Tstamp'], $filterText) !== false) {
481 return true;
482 }
483 break;
484 case 'change':
485 if (stripos(strval($versionArray[$column]), str_replace('%', '', $filterText)) !== false) {
486 return true;
487 }
488 break;
489 default:
490 if (stripos(strval($versionArray[$column]), $filterText) !== false) {
491 return true;
492 }
493 }
494 }
495 }
496 }
497 }
498 return false;
499 }
500
501 /**
502 * Gets the state of a given state value.
503 *
504 * @param int $stateId stateId of offline record
505 * @param bool $hiddenOnline hidden status of online record
506 * @param bool $hiddenOffline hidden status of offline record
507 * @return string
508 */
509 protected function workspaceState($stateId, $hiddenOnline = false, $hiddenOffline = false)
510 {
511 $hiddenState = null;
512 if ($hiddenOnline == 0 && $hiddenOffline == 1) {
513 $hiddenState = 'hidden';
514 } elseif ($hiddenOnline == 1 && $hiddenOffline == 0) {
515 $hiddenState = 'unhidden';
516 }
517 switch ($stateId) {
518 case -1:
519 $state = 'new';
520 break;
521 case 2:
522 $state = 'deleted';
523 break;
524 case 4:
525 $state = 'moved';
526 break;
527 default:
528 $state = ($hiddenState ?: 'modified');
529 }
530 return $state;
531 }
532
533 /**
534 * Gets the field name of the enable-columns as defined in $TCA.
535 *
536 * @param string $table Name of the table
537 * @param string $type Type to be fetches (e.g. 'disabled', 'starttime', 'endtime', 'fe_group)
538 * @return string|null The accordant field name or NULL if not defined
539 */
540 protected function getTcaEnableColumnsFieldName($table, $type)
541 {
542 $fieldName = null;
543
544 if (!(empty($GLOBALS['TCA'][$table]['ctrl']['enablecolumns'][$type]))) {
545 $fieldName = $GLOBALS['TCA'][$table]['ctrl']['enablecolumns'][$type];
546 }
547
548 return $fieldName;
549 }
550
551 /**
552 * Gets the used language value (sys_language.uid) of
553 * a given database record.
554 *
555 * @param string $table Name of the table
556 * @param array $record Database record
557 * @return int
558 */
559 protected function getLanguageValue($table, array $record)
560 {
561 $languageValue = 0;
562 if (BackendUtility::isTableLocalizable($table)) {
563 $languageField = $GLOBALS['TCA'][$table]['ctrl']['languageField'];
564 if (!empty($record[$languageField])) {
565 $languageValue = $record[$languageField];
566 }
567 }
568 return $languageValue;
569 }
570
571 /**
572 * Gets a named value of the available sys_language elements.
573 *
574 * @param int $id sys_language uid
575 * @param string $key Name of the value to be fetched (e.g. title)
576 * @return string|null
577 * @see getSystemLanguages
578 */
579 protected function getSystemLanguageValue($id, $key)
580 {
581 $value = null;
582 $systemLanguages = $this->getSystemLanguages();
583 if (!empty($systemLanguages[$id][$key])) {
584 $value = $systemLanguages[$id][$key];
585 }
586 return $value;
587 }
588
589 /**
590 * Gets all available system languages.
591 *
592 * @return array
593 */
594 public function getSystemLanguages()
595 {
596 if (!isset($this->systemLanguages)) {
597 /** @var $translateTools \TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider */
598 $translateTools = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider::class);
599 $this->systemLanguages = $translateTools->getSystemLanguages();
600 }
601 return $this->systemLanguages;
602 }
603
604 /**
605 * Gets an instance of the integrity service.
606 *
607 * @return \TYPO3\CMS\Workspaces\Service\IntegrityService
608 */
609 protected function getIntegrityService()
610 {
611 if (!isset($this->integrityService)) {
612 $this->integrityService = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\IntegrityService::class);
613 }
614 return $this->integrityService;
615 }
616
617 /**
618 * Emits a signal to be handled by any registered slots.
619 *
620 * @param string $signalName Name of the signal
621 * @param array<int, mixed> $arguments
622 * @return array
623 */
624 protected function emitSignal($signalName, ...$arguments)
625 {
626 // Arguments are always ($this, [method argument], [method argument], ...)
627 $signalArguments = $arguments;
628 array_unshift($signalArguments, $this);
629 $slotReturn = $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Workspaces\Service\GridDataService::class, $signalName, $signalArguments);
630 return array_slice($slotReturn, 1);
631 }
632
633 /**
634 * @return \TYPO3\CMS\Workspaces\Service\Dependency\CollectionService
635 */
636 protected function getDependencyCollectionService()
637 {
638 return GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\Dependency\CollectionService::class);
639 }
640
641 /**
642 * @return \TYPO3\CMS\Workspaces\Service\AdditionalColumnService
643 */
644 protected function getAdditionalColumnService()
645 {
646 return $this->getObjectManager()->get(\TYPO3\CMS\Workspaces\Service\AdditionalColumnService::class);
647 }
648
649 /**
650 * @return \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
651 */
652 protected function getSignalSlotDispatcher()
653 {
654 return $this->getObjectManager()->get(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher::class);
655 }
656
657 /**
658 * @return \TYPO3\CMS\Extbase\Object\ObjectManager
659 */
660 protected function getObjectManager()
661 {
662 return GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\ObjectManager::class);
663 }
664 }