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