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