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