[TASK] Update php-cs-fixer to 2.5.0
[Packages/TYPO3.CMS.git] / typo3 / sysext / workspaces / Classes / Service / StagesService.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\Database\Connection;
19 use TYPO3\CMS\Core\Database\ConnectionPool;
20 use TYPO3\CMS\Core\Utility\GeneralUtility;
21 use TYPO3\CMS\Core\Utility\MathUtility;
22 use TYPO3\CMS\Workspaces\Domain\Record\StageRecord;
23 use TYPO3\CMS\Workspaces\Domain\Record\WorkspaceRecord;
24
25 /**
26 * Stages service
27 */
28 class StagesService implements \TYPO3\CMS\Core\SingletonInterface
29 {
30 const TABLE_STAGE = 'sys_workspace_stage';
31 // if a record is in the "ready to publish" stage STAGE_PUBLISH_ID the nextStage is STAGE_PUBLISH_EXECUTE_ID, this id wont be saved at any time in db
32 const STAGE_PUBLISH_EXECUTE_ID = -20;
33 // ready to publish stage
34 const STAGE_PUBLISH_ID = -10;
35 const STAGE_EDIT_ID = 0;
36 const MODE_NOTIFY_SOMEONE = 0;
37 const MODE_NOTIFY_ALL = 1;
38 const MODE_NOTIFY_ALL_STRICT = 2;
39
40 /**
41 * Path to the locallang file
42 *
43 * @var string
44 */
45 private $pathToLocallang = 'LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf';
46
47 /**
48 * @var RecordService
49 */
50 protected $recordService;
51
52 /**
53 * Local cache to reduce number of database queries for stages, groups, etc.
54 *
55 * @var array
56 */
57 protected $workspaceStageCache = [];
58
59 /**
60 * @var array
61 */
62 protected $workspaceStageAllowedCache = [];
63
64 /**
65 * @var array
66 */
67 protected $fetchGroupsCache = [];
68
69 /**
70 * @var array
71 */
72 protected $userGroups = [];
73
74 /**
75 * Getter for current workspace id
76 *
77 * @return int Current workspace id
78 */
79 public function getWorkspaceId()
80 {
81 return $this->getBackendUser()->workspace;
82 }
83
84 /**
85 * Find the highest possible "previous" stage for all $byTableName
86 *
87 * @param array $workspaceItems
88 * @param array $byTableName
89 * @return array Current and next highest possible stage
90 */
91 public function getPreviousStageForElementCollection(
92 $workspaceItems,
93 array $byTableName = ['tt_content', 'pages', 'pages_language_overlay']
94 ) {
95 $currentStage = [];
96 $previousStage = [];
97 $usedStages = [];
98 $found = false;
99 $availableStagesForWS = array_reverse($this->getStagesForWS());
100 $availableStagesForWSUser = $this->getStagesForWSUser();
101 $byTableName = array_flip($byTableName);
102 foreach ($workspaceItems as $tableName => $items) {
103 if (!array_key_exists($tableName, $byTableName)) {
104 continue;
105 }
106 foreach ($items as $item) {
107 $usedStages[$item['t3ver_stage']] = true;
108 }
109 }
110 foreach ($availableStagesForWS as $stage) {
111 if (isset($usedStages[$stage['uid']])) {
112 $currentStage = $stage;
113 $previousStage = $this->getPrevStage($stage['uid']);
114 break;
115 }
116 }
117 foreach ($availableStagesForWSUser as $userWS) {
118 if ($previousStage['uid'] == $userWS['uid']) {
119 $found = true;
120 break;
121 }
122 }
123 if ($found === false || !$this->isStageAllowedForUser($currentStage['uid'])) {
124 $previousStage = [];
125 }
126 return [
127 $currentStage,
128 $previousStage
129 ];
130 }
131
132 /**
133 * Retrieve the next stage based on the lowest stage given in the $workspaceItems record array.
134 *
135 * @param array $workspaceItems
136 * @param array $byTableName
137 * @return array Current and next possible stage.
138 */
139 public function getNextStageForElementCollection(
140 $workspaceItems,
141 array $byTableName = ['tt_content', 'pages', 'pages_language_overlay']
142 ) {
143 $currentStage = [];
144 $usedStages = [];
145 $nextStage = [];
146 $availableStagesForWS = $this->getStagesForWS();
147 $availableStagesForWSUser = $this->getStagesForWSUser();
148 $byTableName = array_flip($byTableName);
149 $found = false;
150 foreach ($workspaceItems as $tableName => $items) {
151 if (!array_key_exists($tableName, $byTableName)) {
152 continue;
153 }
154 foreach ($items as $item) {
155 $usedStages[$item['t3ver_stage']] = true;
156 }
157 }
158 foreach ($availableStagesForWS as $stage) {
159 if (isset($usedStages[$stage['uid']])) {
160 $currentStage = $stage;
161 $nextStage = $this->getNextStage($stage['uid']);
162 break;
163 }
164 }
165 foreach ($availableStagesForWSUser as $userWS) {
166 if ($nextStage['uid'] == $userWS['uid']) {
167 $found = true;
168 break;
169 }
170 }
171 if ($found === false || !$this->isStageAllowedForUser($currentStage['uid'])) {
172 $nextStage = [];
173 }
174 return [
175 $currentStage,
176 $nextStage
177 ];
178 }
179
180 /**
181 * Building an array with all stage ids and titles related to the given workspace
182 *
183 * @return array id and title of the stages
184 */
185 public function getStagesForWS()
186 {
187 if (isset($this->workspaceStageCache[$this->getWorkspaceId()])) {
188 $stages = $this->workspaceStageCache[$this->getWorkspaceId()];
189 } elseif ($this->getWorkspaceId() === 0) {
190 $stages = [];
191 } else {
192 $stages = $this->prepareStagesArray($this->getWorkspaceRecord()->getStages());
193 $this->workspaceStageCache[$this->getWorkspaceId()] = $stages;
194 }
195 return $stages;
196 }
197
198 /**
199 * Returns an array of stages, the user is allowed to send to
200 *
201 * @return array id and title of stages
202 */
203 public function getStagesForWSUser()
204 {
205 if ($GLOBALS['BE_USER']->isAdmin()) {
206 return $this->getStagesForWS();
207 }
208
209 /** @var $allowedStages StageRecord[] */
210 $allowedStages = [];
211 $stageRecords = $this->getWorkspaceRecord()->getStages();
212
213 // Only use stages that are allowed for current backend user
214 foreach ($stageRecords as $stageRecord) {
215 if ($stageRecord->isAllowed()) {
216 $allowedStages[$stageRecord->getUid()] = $stageRecord;
217 }
218 }
219
220 // Add previous and next stages (even if they are not allowed!)
221 foreach ($allowedStages as $allowedStage) {
222 $previousStage = $allowedStage->getPrevious();
223 $nextStage = $allowedStage->getNext();
224 if ($previousStage !== null && !isset($allowedStages[$previousStage->getUid()])) {
225 $allowedStages[$previousStage->getUid()] = $previousStage;
226 }
227 if ($nextStage !== null && !isset($allowedStages[$nextStage->getUid()])) {
228 $allowedStages[$nextStage->getUid()] = $nextStage;
229 }
230 }
231
232 uasort($allowedStages, function (StageRecord $first, StageRecord $second) {
233 return $first->determineOrder($second);
234 });
235 return $this->prepareStagesArray($allowedStages);
236 }
237
238 /**
239 * Prepares simplified stages array to be used in ExtJs components.
240 *
241 * @param StageRecord[] $stageRecords
242 * @return array
243 */
244 protected function prepareStagesArray(array $stageRecords)
245 {
246 $stagesArray = [];
247 foreach ($stageRecords as $stageRecord) {
248 $stage = [
249 'uid' => $stageRecord->getUid(),
250 'label' => $stageRecord->getTitle(),
251 ];
252 if (!$stageRecord->isExecuteStage()) {
253 $stage['title'] = $GLOBALS['LANG']->sL(($this->pathToLocallang . ':actionSendToStage')) . ' "' . $stageRecord->getTitle() . '"';
254 } else {
255 $stage['title'] = $GLOBALS['LANG']->sL($this->pathToLocallang . ':publish_execute_action_option');
256 }
257 $stagesArray[] = $stage;
258 }
259 return $stagesArray;
260 }
261
262 /**
263 * Gets the title of a stage.
264 *
265 * @param int $ver_stage
266 * @return string
267 */
268 public function getStageTitle($ver_stage)
269 {
270 switch ($ver_stage) {
271 case self::STAGE_PUBLISH_EXECUTE_ID:
272 $stageTitle = $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang_mod_user_ws.xlf:stage_publish');
273 break;
274 case self::STAGE_PUBLISH_ID:
275 $stageTitle = $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang_mod.xlf:stage_ready_to_publish');
276 break;
277 case self::STAGE_EDIT_ID:
278 $stageTitle = $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang_mod_user_ws.xlf:stage_editing');
279 break;
280 default:
281 $stageTitle = $this->getPropertyOfCurrentWorkspaceStage($ver_stage, 'title');
282 if ($stageTitle == null) {
283 $stageTitle = $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:error.getStageTitle.stageNotFound');
284 }
285 }
286 return $stageTitle;
287 }
288
289 /**
290 * Gets a particular stage record.
291 *
292 * @param int $stageid
293 * @return array
294 */
295 public function getStageRecord($stageid)
296 {
297 return BackendUtility::getRecord('sys_workspace_stage', $stageid);
298 }
299
300 /**
301 * Gets next stage in process for given stage id
302 *
303 * @param int $stageId Id of the stage to fetch the next one for
304 * @return int The next stage Id
305 * @throws \InvalidArgumentException
306 */
307 public function getNextStage($stageId)
308 {
309 if (!MathUtility::canBeInterpretedAsInteger($stageId)) {
310 throw new \InvalidArgumentException(
311 $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:error.stageId.integer'),
312 1291109987
313 );
314 }
315 $nextStage = false;
316 $workspaceStageRecs = $this->getStagesForWS();
317 if (is_array($workspaceStageRecs) && !empty($workspaceStageRecs)) {
318 reset($workspaceStageRecs);
319 while (!is_null(($workspaceStageRecKey = key($workspaceStageRecs)))) {
320 $workspaceStageRec = current($workspaceStageRecs);
321 if ($workspaceStageRec['uid'] == $stageId) {
322 $nextStage = next($workspaceStageRecs);
323 break;
324 }
325 next($workspaceStageRecs);
326 }
327 }
328 if ($nextStage === false) {
329 $nextStage[] = [
330 'uid' => self::STAGE_EDIT_ID,
331 'title' => $GLOBALS['LANG']->sL(($this->pathToLocallang . ':actionSendToStage')) . ' "'
332 . $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang_mod_user_ws.xlf:stage_editing') . '"'
333 ];
334 }
335 return $nextStage;
336 }
337
338 /**
339 * Recursive function to get all next stages for a record depending on user permissions
340 *
341 * @param array $nextStageArray Next stages
342 * @param int $stageId Current stage id of the record
343 * @return array Next stages
344 */
345 public function getNextStages(array &$nextStageArray, $stageId)
346 {
347 // Current stage is "Ready to publish" - there is no next stage
348 if ($stageId == self::STAGE_PUBLISH_ID) {
349 return $nextStageArray;
350 }
351 $nextStageRecord = $this->getNextStage($stageId);
352 if (empty($nextStageRecord) || !is_array($nextStageRecord)) {
353 // There is no next stage
354 return $nextStageArray;
355 }
356 // Check if the user has the permission to for the current stage
357 // If this next stage record is the first next stage after the current the user
358 // has always the needed permission
359 if ($this->isStageAllowedForUser($stageId)) {
360 $nextStageArray[] = $nextStageRecord;
361 return $this->getNextStages($nextStageArray, $nextStageRecord['uid']);
362 }
363 // He hasn't - return given next stage array
364 return $nextStageArray;
365 }
366
367 /**
368 * Get next stage in process for given stage id
369 *
370 * @param int $stageId Id of the stage to fetch the previous one for
371 * @return int The previous stage Id
372 * @throws \InvalidArgumentException
373 */
374 public function getPrevStage($stageId)
375 {
376 if (!MathUtility::canBeInterpretedAsInteger($stageId)) {
377 throw new \InvalidArgumentException(
378 $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:error.stageId.integer'),
379 1476048351
380 );
381 }
382 $prevStage = false;
383 $workspaceStageRecs = $this->getStagesForWS();
384 if (is_array($workspaceStageRecs) && !empty($workspaceStageRecs)) {
385 end($workspaceStageRecs);
386 while (!is_null(($workspaceStageRecKey = key($workspaceStageRecs)))) {
387 $workspaceStageRec = current($workspaceStageRecs);
388 if ($workspaceStageRec['uid'] == $stageId) {
389 $prevStage = prev($workspaceStageRecs);
390 break;
391 }
392 prev($workspaceStageRecs);
393 }
394 }
395
396 return $prevStage;
397 }
398
399 /**
400 * Recursive function to get all prev stages for a record depending on user permissions
401 *
402 * @param array $prevStageArray Prev stages
403 * @param int $stageId Current stage id of the record
404 * @return array prev stages
405 */
406 public function getPrevStages(array &$prevStageArray, $stageId)
407 {
408 // Current stage is "Editing" - there is no prev stage
409 if ($stageId != self::STAGE_EDIT_ID) {
410 $prevStageRecord = $this->getPrevStage($stageId);
411 if (!empty($prevStageRecord) && is_array($prevStageRecord)) {
412 // Check if the user has the permission to switch to that stage
413 // If this prev stage record is the first previous stage before the current
414 // the user has always the needed permission
415 if ($this->isStageAllowedForUser($stageId)) {
416 $prevStageArray[] = $prevStageRecord;
417 $prevStageArray = $this->getPrevStages($prevStageArray, $prevStageRecord['uid']);
418 }
419 }
420 }
421 return $prevStageArray;
422 }
423
424 /**
425 * Gets all backend user records that are considered to be responsible
426 * for a particular stage or workspace.
427 *
428 * @param StageRecord|int $stageRecord Stage
429 * @param bool $selectDefaultUserField If field notification_defaults should be selected instead of responsible users
430 * @return array be_users with e-mail and name
431 */
432 public function getResponsibleBeUser($stageRecord, $selectDefaultUserField = false)
433 {
434 if (!$stageRecord instanceof StageRecord) {
435 $stageRecord = $this->getWorkspaceRecord()->getStage($stageRecord);
436 }
437
438 $recipientArray = [];
439
440 if (!$selectDefaultUserField) {
441 $backendUserIds = $stageRecord->getAllRecipients();
442 } else {
443 $backendUserIds = $stageRecord->getDefaultRecipients();
444 }
445
446 $userList = implode(',', $backendUserIds);
447 $userRecords = $this->getBackendUsers($userList);
448 foreach ($userRecords as $userUid => $userRecord) {
449 $recipientArray[$userUid] = $userRecord;
450 }
451 return $recipientArray;
452 }
453
454 /**
455 * Gets backend user ids from a mixed list of backend users
456 * and backend users groups. This is used for notifying persons
457 * responsible for a particular stage or workspace.
458 *
459 * @param string $stageRespValue Responsible_person value from stage record
460 * @return string List of backend user ids
461 */
462 public function getResponsibleUser($stageRespValue)
463 {
464 return implode(',', $this->resolveBackendUserIds($stageRespValue));
465 }
466
467 /**
468 * Resolves backend user ids from a mixed list of backend users
469 * and backend user groups (e.g. "be_users_1,be_groups_3,be_users_4,...")
470 *
471 * @param string $backendUserGroupList
472 * @return array
473 */
474 public function resolveBackendUserIds($backendUserGroupList)
475 {
476 $elements = GeneralUtility::trimExplode(',', $backendUserGroupList, true);
477 $backendUserIds = [];
478 $backendGroupIds = [];
479
480 foreach ($elements as $element) {
481 if (strpos($element, 'be_users_') === 0) {
482 // Current value is a uid of a be_user record
483 $backendUserIds[] = str_replace('be_users_', '', $element);
484 } elseif (strpos($element, 'be_groups_') === 0) {
485 $backendGroupIds[] = str_replace('be_groups_', '', $element);
486 } elseif ((int)$element) {
487 $backendUserIds[] = (int)$element;
488 }
489 }
490
491 if (!empty($backendGroupIds)) {
492 $allBeUserArray = BackendUtility::getUserNames();
493 $backendGroupList = implode(',', $backendGroupIds);
494 $this->userGroups = [];
495 $backendGroups = $this->fetchGroups($backendGroupList);
496 foreach ($backendGroups as $backendGroup) {
497 foreach ($allBeUserArray as $backendUserId => $backendUser) {
498 if (GeneralUtility::inList($backendUser['usergroup_cached_list'], $backendGroup['uid'])) {
499 $backendUserIds[] = $backendUserId;
500 }
501 }
502 }
503 }
504
505 return array_unique($backendUserIds);
506 }
507
508 /**
509 * Gets backend user records from a given list of ids.
510 *
511 * @param string $backendUserList
512 * @return array
513 */
514 public function getBackendUsers($backendUserList)
515 {
516 if (empty($backendUserList)) {
517 return [];
518 }
519
520 $backendUserList = implode(',', GeneralUtility::intExplode(',', $backendUserList));
521 $backendUsers = BackendUtility::getUserNames(
522 'username, uid, email, realName, lang, uc',
523 'AND uid IN (' . $backendUserList . ')' . BackendUtility::BEenableFields('be_users')
524 );
525
526 if (empty($backendUsers)) {
527 $backendUsers = [];
528 }
529 return $backendUsers;
530 }
531
532 /**
533 * @param StageRecord $stageRecord
534 * @return array
535 */
536 public function getPreselectedRecipients(StageRecord $stageRecord)
537 {
538 if ($stageRecord->areEditorsPreselected()) {
539 return array_merge(
540 $stageRecord->getPreselectedRecipients(),
541 $this->getRecordService()->getCreateUserIds()
542 );
543 }
544 return $stageRecord->getPreselectedRecipients();
545 }
546
547 /**
548 * @return WorkspaceRecord
549 */
550 protected function getWorkspaceRecord()
551 {
552 return WorkspaceRecord::get($this->getWorkspaceId());
553 }
554
555 /**
556 * @param $grList
557 * @param string $idList
558 * @return array
559 */
560 private function fetchGroups($grList, $idList = '')
561 {
562 $cacheKey = md5($grList . $idList);
563 $groupList = [];
564 if (isset($this->fetchGroupsCache[$cacheKey])) {
565 $groupList = $this->fetchGroupsCache[$cacheKey];
566 } else {
567 if ($idList === '') {
568 // we're at the beginning of the recursion and therefore we need to reset the userGroups member
569 $this->userGroups = [];
570 }
571 $groupList = $this->fetchGroupsRecursive($grList);
572 $this->fetchGroupsCache[$cacheKey] = $groupList;
573 }
574 return $groupList;
575 }
576
577 /**
578 * @param array $groups
579 */
580 private function fetchGroupsFromDB(array $groups)
581 {
582 // The userGroups array is filled
583 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('be_groups');
584
585 $result = $queryBuilder
586 ->select('*')
587 ->from('be_groups')
588 ->where(
589 $queryBuilder->expr()->in(
590 'uid',
591 $queryBuilder->createNamedParameter($groups, Connection::PARAM_INT_ARRAY)
592 )
593 )
594 ->execute();
595
596 while ($row = $result->fetch()) {
597 $this->userGroups[$row['uid']] = $row;
598 }
599 }
600
601 /**
602 * Fetches particular groups recursively.
603 *
604 * @param $grList
605 * @param string $idList
606 * @return array
607 */
608 private function fetchGroupsRecursive($grList, $idList = '')
609 {
610 $requiredGroups = GeneralUtility::intExplode(',', $grList, true);
611 $existingGroups = array_keys($this->userGroups);
612 $missingGroups = array_diff($requiredGroups, $existingGroups);
613 if (!empty($missingGroups)) {
614 $this->fetchGroupsFromDB($missingGroups);
615 }
616 // Traversing records in the correct order
617 foreach ($requiredGroups as $uid) {
618 // traversing list
619 // Get row:
620 $row = $this->userGroups[$uid];
621 if (is_array($row) && !GeneralUtility::inList($idList, $uid)) {
622 // Must be an array and $uid should not be in the idList, because then it is somewhere previously in the grouplist
623 // If the localconf.php option isset the user of the sub- sub- groups will also be used
624 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['customStageShowRecipientRecursive'] == 1) {
625 // Include sub groups
626 if (trim($row['subgroup'])) {
627 // Make integer list
628 $theList = implode(',', GeneralUtility::intExplode(',', $row['subgroup']));
629 // Get the subarray
630 $subbarray = $this->fetchGroups($theList, $idList . ',' . $uid);
631 list($subUid, $subArray) = each($subbarray);
632 // Merge the subarray to the already existing userGroups array
633 $this->userGroups[$subUid] = $subArray;
634 }
635 }
636 }
637 }
638 return $this->userGroups;
639 }
640
641 /**
642 * Gets a property of a workspaces stage.
643 *
644 * @param int $stageId
645 * @param string $property
646 * @return string
647 * @throws \InvalidArgumentException
648 */
649 public function getPropertyOfCurrentWorkspaceStage($stageId, $property)
650 {
651 $result = null;
652 if (!MathUtility::canBeInterpretedAsInteger($stageId)) {
653 throw new \InvalidArgumentException(
654 $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:error.stageId.integer'),
655 1476048371
656 );
657 }
658 $workspaceStage = BackendUtility::getRecord(self::TABLE_STAGE, $stageId);
659 if (is_array($workspaceStage) && isset($workspaceStage[$property])) {
660 $result = $workspaceStage[$property];
661 }
662 return $result;
663 }
664
665 /**
666 * Gets the position of the given workspace in the hole process
667 * f.e. 3 means step 3 of 20, by which 1 is edit and 20 is ready to publish
668 *
669 * @param int $stageId
670 * @return array position => 3, count => 20
671 */
672 public function getPositionOfCurrentStage($stageId)
673 {
674 $stagesOfWS = $this->getStagesForWS();
675 $countOfStages = count($stagesOfWS);
676 switch ($stageId) {
677 case self::STAGE_PUBLISH_ID:
678 $position = $countOfStages;
679 break;
680 case self::STAGE_EDIT_ID:
681 $position = 1;
682 break;
683 default:
684 $position = 1;
685 foreach ($stagesOfWS as $key => $stageInfoArray) {
686 $position++;
687 if ($stageId == $stageInfoArray['uid']) {
688 break;
689 }
690 }
691 }
692 return ['position' => $position, 'count' => $countOfStages];
693 }
694
695 /**
696 * Check if the user has access to the previous stage, relative to the given stage
697 *
698 * @param int $stageId
699 * @return bool
700 */
701 public function isPrevStageAllowedForUser($stageId)
702 {
703 $isAllowed = false;
704 try {
705 $prevStage = $this->getPrevStage($stageId);
706 // if there's no prev-stage the stageIds match,
707 // otherwise we've to check if the user is permitted to use the stage
708 if (!empty($prevStage) && $prevStage['uid'] != $stageId) {
709 // if the current stage is allowed for the user, the user is also allowed to send to prev
710 $isAllowed = $this->isStageAllowedForUser($stageId);
711 }
712 } catch (\Exception $e) {
713 }
714 return $isAllowed;
715 }
716
717 /**
718 * Check if the user has access to the next stage, relative to the given stage
719 *
720 * @param int $stageId
721 * @return bool
722 */
723 public function isNextStageAllowedForUser($stageId)
724 {
725 $isAllowed = false;
726 try {
727 $nextStage = $this->getNextStage($stageId);
728 // if there's no next-stage the stageIds match,
729 // otherwise we've to check if the user is permitted to use the stage
730 if (!empty($nextStage) && $nextStage['uid'] != $stageId) {
731 // if the current stage is allowed for the user, the user is also allowed to send to next
732 $isAllowed = $this->isStageAllowedForUser($stageId);
733 }
734 } catch (\Exception $e) {
735 }
736 return $isAllowed;
737 }
738
739 /**
740 * @param $stageId
741 * @return bool
742 */
743 protected function isStageAllowedForUser($stageId)
744 {
745 $cacheKey = $this->getWorkspaceId() . '_' . $stageId;
746 $isAllowed = false;
747 if (isset($this->workspaceStageAllowedCache[$cacheKey])) {
748 $isAllowed = $this->workspaceStageAllowedCache[$cacheKey];
749 } else {
750 $isAllowed = $GLOBALS['BE_USER']->workspaceCheckStageForCurrent($stageId);
751 $this->workspaceStageAllowedCache[$cacheKey] = $isAllowed;
752 }
753 return $isAllowed;
754 }
755
756 /**
757 * Determines whether a stage Id is valid.
758 *
759 * @param int $stageId The stage Id to be checked
760 * @return bool
761 */
762 public function isValid($stageId)
763 {
764 $isValid = false;
765 $stages = $this->getStagesForWS();
766 foreach ($stages as $stage) {
767 if ($stage['uid'] == $stageId) {
768 $isValid = true;
769 break;
770 }
771 }
772 return $isValid;
773 }
774
775 /**
776 * @return RecordService
777 */
778 public function getRecordService()
779 {
780 if (!isset($this->recordService)) {
781 $this->recordService = GeneralUtility::makeInstance(RecordService::class);
782 }
783 return $this->recordService;
784 }
785
786 /**
787 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
788 */
789 protected function getBackendUser()
790 {
791 return $GLOBALS['BE_USER'];
792 }
793 }