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