[BUGFIX] Fix interaction with versions on "All workspaces" tab
[Packages/TYPO3.CMS.git] / typo3 / sysext / workspaces / Classes / ExtDirect / ActionHandler.php
1 <?php
2 namespace TYPO3\CMS\Workspaces\ExtDirect;
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\Workspaces\Domain\Record\StageRecord;
20 use TYPO3\CMS\Workspaces\Domain\Record\WorkspaceRecord;
21 use TYPO3\CMS\Workspaces\Service\StagesService;
22
23 /**
24 * ExtDirect action handler
25 */
26 class ActionHandler extends AbstractHandler
27 {
28 /**
29 * @var StagesService
30 */
31 protected $stageService;
32
33 /**
34 * Creates this object.
35 */
36 public function __construct()
37 {
38 $this->stageService = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\StagesService::class);
39 }
40
41 /**
42 * Generates a workspace preview link.
43 *
44 * @param int $uid The ID of the record to be linked
45 * @return string the full domain including the protocol http:// or https://, but without the trailing '/'
46 */
47 public function generateWorkspacePreviewLink($uid)
48 {
49 return $this->getWorkspaceService()->generateWorkspacePreviewLink($uid);
50 }
51
52 /**
53 * Generates workspace preview links for all available languages of a page.
54 *
55 * @param int $uid
56 * @return array
57 */
58 public function generateWorkspacePreviewLinksForAllLanguages($uid)
59 {
60 return $this->getWorkspaceService()->generateWorkspacePreviewLinksForAllLanguages($uid);
61 }
62
63 /**
64 * Swaps a single record.
65 *
66 * @param string $table
67 * @param int $t3ver_oid
68 * @param int $orig_uid
69 * @return void
70 * @todo What about reporting errors back to the ExtJS interface? /olly/
71 */
72 public function swapSingleRecord($table, $t3ver_oid, $orig_uid)
73 {
74 $versionRecord = BackendUtility::getRecord($table, $orig_uid);
75 $currentWorkspace = $this->setTemporaryWorkspace($versionRecord['t3ver_wsid']);
76
77 $cmd[$table][$t3ver_oid]['version'] = [
78 'action' => 'swap',
79 'swapWith' => $orig_uid,
80 'swapIntoWS' => 1
81 ];
82 $this->processTcaCmd($cmd);
83
84 $this->setTemporaryWorkspace($currentWorkspace);
85 }
86
87 /**
88 * Deletes a single record.
89 *
90 * @param string $table
91 * @param int $uid
92 * @return void
93 * @todo What about reporting errors back to the ExtJS interface? /olly/
94 */
95 public function deleteSingleRecord($table, $uid)
96 {
97 $versionRecord = BackendUtility::getRecord($table, $uid);
98 $currentWorkspace = $this->setTemporaryWorkspace($versionRecord['t3ver_wsid']);
99
100 $cmd[$table][$uid]['version'] = [
101 'action' => 'clearWSID'
102 ];
103 $this->processTcaCmd($cmd);
104
105 $this->setTemporaryWorkspace($currentWorkspace);
106 }
107
108 /**
109 * Generates a view link for a page.
110 *
111 * @param string $table
112 * @param string $uid
113 * @return string
114 */
115 public function viewSingleRecord($table, $uid)
116 {
117 return \TYPO3\CMS\Workspaces\Service\WorkspaceService::viewSingleRecord($table, $uid);
118 }
119
120 /**
121 * Executes an action (publish, discard, swap) to a selection set.
122 *
123 * @param \stdClass $parameter
124 * @return array
125 */
126 public function executeSelectionAction($parameter)
127 {
128 $result = [];
129
130 if (empty($parameter->action) || empty($parameter->selection)) {
131 $result['error'] = 'No action or record selection given';
132 return $result;
133 }
134
135 $commands = [];
136 $swapIntoWorkspace = ($parameter->action === 'swap');
137 if ($parameter->action === 'publish' || $swapIntoWorkspace) {
138 $commands = $this->getPublishSwapCommands($parameter->selection, $swapIntoWorkspace);
139 } elseif ($parameter->action === 'discard') {
140 $commands = $this->getFlushCommands($parameter->selection);
141 }
142
143 $result = $this->processTcaCmd($commands);
144 $result['total'] = count($commands);
145 return $result;
146 }
147
148 /**
149 * Get publish swap commands
150 *
151 * @param array|\stdClass[] $selection
152 * @param bool $swapIntoWorkspace
153 * @return array
154 */
155 protected function getPublishSwapCommands(array $selection, $swapIntoWorkspace)
156 {
157 $commands = [];
158 foreach ($selection as $record) {
159 $commands[$record->table][$record->liveId]['version'] = [
160 'action' => 'swap',
161 'swapWith' => $record->versionId,
162 'swapIntoWS' => (bool)$swapIntoWorkspace,
163 ];
164 }
165 return $commands;
166 }
167
168 /**
169 * Get flush commands
170 *
171 * @param array|\stdClass[] $selection
172 * @return array
173 */
174 protected function getFlushCommands(array $selection)
175 {
176 $commands = [];
177 foreach ($selection as $record) {
178 $commands[$record->table][$record->versionId]['version'] = [
179 'action' => 'clearWSID',
180 ];
181 }
182 return $commands;
183 }
184
185 /**
186 * Saves the selected columns to be shown to the preferences of the current backend user.
187 *
188 * @param \stdClass $model
189 * @return void
190 */
191 public function saveColumnModel($model)
192 {
193 $data = [];
194 foreach ($model as $column) {
195 $data[$column->column] = [
196 'position' => $column->position,
197 'hidden' => $column->hidden
198 ];
199 }
200 $GLOBALS['BE_USER']->uc['moduleData']['Workspaces'][$GLOBALS['BE_USER']->workspace]['columns'] = $data;
201 $GLOBALS['BE_USER']->writeUC();
202 }
203
204 public function loadColumnModel()
205 {
206 if (is_array($GLOBALS['BE_USER']->uc['moduleData']['Workspaces'][$GLOBALS['BE_USER']->workspace]['columns'])) {
207 return $GLOBALS['BE_USER']->uc['moduleData']['Workspaces'][$GLOBALS['BE_USER']->workspace]['columns'];
208 } else {
209 return [];
210 }
211 }
212
213 /**
214 * Saves the selected language.
215 *
216 * @param int|string $language
217 * @return void
218 */
219 public function saveLanguageSelection($language)
220 {
221 if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($language) === false && $language !== 'all') {
222 $language = 'all';
223 }
224 $GLOBALS['BE_USER']->uc['moduleData']['Workspaces'][$GLOBALS['BE_USER']->workspace]['language'] = $language;
225 $GLOBALS['BE_USER']->writeUC();
226 }
227
228 /**
229 * Gets the dialog window to be displayed before a record can be sent to the next stage.
230 *
231 * @param int $uid
232 * @param string $table
233 * @param int $t3ver_oid
234 * @return array
235 */
236 public function sendToNextStageWindow($uid, $table, $t3ver_oid)
237 {
238 $elementRecord = BackendUtility::getRecord($table, $uid);
239 $currentWorkspace = $this->setTemporaryWorkspace($elementRecord['t3ver_wsid']);
240
241 if (is_array($elementRecord)) {
242 $workspaceRecord = WorkspaceRecord::get($elementRecord['t3ver_wsid']);
243 $nextStageRecord = $workspaceRecord->getNextStage($elementRecord['t3ver_stage']);
244 if ($nextStageRecord !== null) {
245 $this->stageService->getRecordService()->add($table, $uid);
246 $result = $this->getSentToStageWindow($nextStageRecord);
247 $result['affects'] = [
248 'table' => $table,
249 'nextStage' => $nextStageRecord->getUid(),
250 't3ver_oid' => $t3ver_oid,
251 'uid' => $uid
252 ];
253 } else {
254 $result = $this->getErrorResponse('error.stageId.invalid', 1291111644);
255 }
256 } else {
257 $result = $this->getErrorResponse('error.sendToNextStage.noRecordFound', 1287264776);
258 }
259
260 $this->setTemporaryWorkspace($currentWorkspace);
261 return $result;
262 }
263
264 /**
265 * Gets the dialog window to be displayed before a record can be sent to the previous stage.
266 *
267 * @param int $uid
268 * @param string $table
269 * @return array
270 */
271 public function sendToPrevStageWindow($uid, $table)
272 {
273 $elementRecord = BackendUtility::getRecord($table, $uid);
274 $currentWorkspace = $this->setTemporaryWorkspace($elementRecord['t3ver_wsid']);
275
276 if (is_array($elementRecord)) {
277 $workspaceRecord = WorkspaceRecord::get($elementRecord['t3ver_wsid']);
278 $stageRecord = $workspaceRecord->getStage($elementRecord['t3ver_stage']);
279
280 if ($stageRecord !== null) {
281 if (!$stageRecord->isEditStage()) {
282 $this->stageService->getRecordService()->add($table, $uid);
283 $previousStageRecord = $stageRecord->getPrevious();
284 $result = $this->getSentToStageWindow($previousStageRecord);
285 $result['affects'] = [
286 'table' => $table,
287 'uid' => $uid,
288 'nextStage' => $previousStageRecord->getUid()
289 ];
290 } else {
291 // element is already in edit stage, there is no prev stage - return an error message
292 $result = $this->getErrorResponse('error.sendToPrevStage.noPreviousStage', 1287264746);
293 }
294 } else {
295 $result = $this->getErrorResponse('error.stageId.invalid', 1291111644);
296 }
297 } else {
298 $result = $this->getErrorResponse('error.sendToNextStage.noRecordFound', 1287264765);
299 }
300
301 $this->setTemporaryWorkspace($currentWorkspace);
302 return $result;
303 }
304
305 /**
306 * Gets the dialog window to be displayed before a record can be sent to a specific stage.
307 *
308 * @param int $nextStageId
309 * @param array|\stdClass[] $elements
310 * @return array
311 */
312 public function sendToSpecificStageWindow($nextStageId, array $elements)
313 {
314 foreach ($elements as $element) {
315 $this->stageService->getRecordService()->add(
316 $element->table,
317 $element->uid
318 );
319 }
320
321 $result = $this->getSentToStageWindow($nextStageId);
322 $result['affects'] = [
323 'nextStage' => $nextStageId
324 ];
325 return $result;
326 }
327
328 /**
329 * Gets a merged variant of recipient defined by uid and custom ones.
330 *
331 * @param array $uidOfRecipients list of recipients
332 * @param string $additionalRecipients given user string of additional recipients
333 * @param int $stageId stage id
334 * @return array
335 * @throws \InvalidArgumentException
336 */
337 public function getRecipientList(array $uidOfRecipients, $additionalRecipients, $stageId)
338 {
339 $stageRecord = WorkspaceRecord::get($this->getCurrentWorkspace())->getStage($stageId);
340
341 if ($stageRecord === null) {
342 throw new \InvalidArgumentException($GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:error.stageId.integer'));
343 }
344
345 $recipients = [];
346 $finalRecipients = [];
347 $backendUserIds = $stageRecord->getAllRecipients();
348 foreach ($uidOfRecipients as $userUid) {
349 // Ensure that only configured backend users are considered
350 if (!in_array($userUid, $backendUserIds)) {
351 continue;
352 }
353 $beUserRecord = BackendUtility::getRecord('be_users', (int)$userUid);
354 if (is_array($beUserRecord) && $beUserRecord['email'] !== '') {
355 $uc = $beUserRecord['uc'] ? unserialize($beUserRecord['uc']) : [];
356 $recipients[$beUserRecord['email']] = [
357 'email' => $beUserRecord['email'],
358 'lang' => isset($uc['lang']) ? $uc['lang'] : $beUserRecord['lang']
359 ];
360 }
361 }
362
363 if ($stageRecord->hasPreselection() && !$stageRecord->isPreselectionChangeable()) {
364 $preselectedBackendUsers = $this->getStageService()->getBackendUsers(
365 implode(',', $this->stageService->getPreselectedRecipients($stageRecord))
366 );
367
368 foreach ($preselectedBackendUsers as $preselectedBackendUser) {
369 if (empty($preselectedBackendUser['email']) || !GeneralUtility::validEmail($preselectedBackendUser['email'])) {
370 continue;
371 }
372 if (!isset($recipients[$preselectedBackendUser['email']])) {
373 $uc = (!empty($preselectedBackendUser['uc']) ? unserialize($preselectedBackendUser['uc']) : []);
374 $recipients[$preselectedBackendUser['email']] = [
375 'email' => $preselectedBackendUser['email'],
376 'lang' => (isset($uc['lang']) ? $uc['lang'] : $preselectedBackendUser['lang'])
377 ];
378 }
379 }
380 }
381
382 if ($additionalRecipients !== '') {
383 $emails = GeneralUtility::trimExplode(LF, $additionalRecipients, true);
384 $additionalRecipients = [];
385 foreach ($emails as $email) {
386 $additionalRecipients[$email] = ['email' => $email];
387 }
388 } else {
389 $additionalRecipients = [];
390 }
391 // We merge $recipients on top of $additionalRecipients because $recipients
392 // possibly is more complete with a user language. Furthermore, the list of
393 // recipients is automatically unique since we indexed $additionalRecipients
394 // and $recipients with the email address
395 $allRecipients = array_merge($additionalRecipients, $recipients);
396 foreach ($allRecipients as $email => $recipientInformation) {
397 if (GeneralUtility::validEmail($email)) {
398 $finalRecipients[] = $recipientInformation;
399 }
400 }
401 return $finalRecipients;
402 }
403
404 /**
405 * Discard all items from given page id.
406 *
407 * @param int $pageId
408 * @return array
409 */
410 public function discardStagesFromPage($pageId)
411 {
412 $cmdMapArray = [];
413 /** @var $workspaceService \TYPO3\CMS\Workspaces\Service\WorkspaceService */
414 $workspaceService = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\WorkspaceService::class);
415 /** @var $stageService StagesService */
416 $stageService = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\StagesService::class);
417 $workspaceItemsArray = $workspaceService->selectVersionsInWorkspace($stageService->getWorkspaceId(), ($filter = 1), ($stage = -99), $pageId, ($recursionLevel = 0), ($selectionType = 'tables_modify'));
418 foreach ($workspaceItemsArray as $tableName => $items) {
419 foreach ($items as $item) {
420 $cmdMapArray[$tableName][$item['uid']]['version']['action'] = 'clearWSID';
421 }
422 }
423 $this->processTcaCmd($cmdMapArray);
424 return [
425 'success' => true
426 ];
427 }
428
429 /**
430 * Push the given element collection to the next workspace stage.
431 *
432 * <code>
433 * $parameters->additional = your@mail.com
434 * $parameters->affects->__TABLENAME__
435 * $parameters->comments
436 * $parameters->receipients
437 * $parameters->stageId
438 * </code>
439 *
440 * @param stdClass $parameters
441 * @return array
442 */
443 public function sentCollectionToStage(\stdClass $parameters)
444 {
445 $cmdMapArray = [];
446 $comment = $parameters->comments;
447 $stageId = $parameters->stageId;
448 if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($stageId) === false) {
449 throw new \InvalidArgumentException('Missing "stageId" in $parameters array.', 1319488194);
450 }
451 if (!is_object($parameters->affects) || empty($parameters->affects)) {
452 throw new \InvalidArgumentException('Missing "affected items" in $parameters array.', 1319488195);
453 }
454 $recipients = $this->getRecipientList($parameters->receipients, $parameters->additional, $stageId);
455 foreach ($parameters->affects as $tableName => $items) {
456 foreach ($items as $item) {
457 // Publishing uses live id in command map
458 if ($stageId == StagesService::STAGE_PUBLISH_EXECUTE_ID) {
459 $cmdMapArray[$tableName][$item->t3ver_oid]['version']['action'] = 'swap';
460 $cmdMapArray[$tableName][$item->t3ver_oid]['version']['swapWith'] = $item->uid;
461 $cmdMapArray[$tableName][$item->t3ver_oid]['version']['comment'] = $comment;
462 $cmdMapArray[$tableName][$item->t3ver_oid]['version']['notificationAlternativeRecipients'] = $recipients;
463 // Setting stage uses version id in command map
464 } else {
465 $cmdMapArray[$tableName][$item->uid]['version']['action'] = 'setStage';
466 $cmdMapArray[$tableName][$item->uid]['version']['stageId'] = $stageId;
467 $cmdMapArray[$tableName][$item->uid]['version']['comment'] = $comment;
468 $cmdMapArray[$tableName][$item->uid]['version']['notificationAlternativeRecipients'] = $recipients;
469 }
470 }
471 }
472 $this->processTcaCmd($cmdMapArray);
473 return [
474 'success' => true,
475 // force refresh after publishing changes
476 'refreshLivePanel' => $parameters->stageId == -20
477 ];
478 }
479
480 /**
481 * Process TCA command map array.
482 *
483 * @param array $cmdMapArray
484 * @return array
485 */
486 protected function processTcaCmd(array $cmdMapArray)
487 {
488 $result = [];
489
490 if (empty($cmdMapArray)) {
491 $result['error'] = 'No commands given to be processed';
492 return $result;
493 }
494
495 /** @var \TYPO3\CMS\Core\DataHandling\DataHandler $dataHandler */
496 $dataHandler = GeneralUtility::makeInstance(\TYPO3\CMS\Core\DataHandling\DataHandler::class);
497 $dataHandler->start([], $cmdMapArray);
498 $dataHandler->process_cmdmap();
499
500 if ($dataHandler->errorLog) {
501 $result['error'] = implode('<br/>', $dataHandler->errorLog);
502 }
503
504 return $result;
505 }
506
507 /**
508 * Gets an object with this structure:
509 *
510 * affects: object
511 * table
512 * t3ver_oid
513 * nextStage
514 * uid
515 * receipients: array with uids
516 * additional: string
517 * comments: string
518 *
519 * @param stdClass $parameters
520 * @return array
521 */
522 public function sendToNextStageExecute(\stdClass $parameters)
523 {
524 $cmdArray = [];
525 $setStageId = $parameters->affects->nextStage;
526 $comments = $parameters->comments;
527 $table = $parameters->affects->table;
528 $uid = $parameters->affects->uid;
529 $t3ver_oid = $parameters->affects->t3ver_oid;
530
531 $elementRecord = BackendUtility::getRecord($table, $uid);
532 $currentWorkspace = $this->setTemporaryWorkspace($elementRecord['t3ver_wsid']);
533
534 $recipients = $this->getRecipientList($parameters->receipients, $parameters->additional, $setStageId);
535 if ($setStageId == StagesService::STAGE_PUBLISH_EXECUTE_ID) {
536 $cmdArray[$table][$t3ver_oid]['version']['action'] = 'swap';
537 $cmdArray[$table][$t3ver_oid]['version']['swapWith'] = $uid;
538 $cmdArray[$table][$t3ver_oid]['version']['comment'] = $comments;
539 $cmdArray[$table][$t3ver_oid]['version']['notificationAlternativeRecipients'] = $recipients;
540 } else {
541 $cmdArray[$table][$uid]['version']['action'] = 'setStage';
542 $cmdArray[$table][$uid]['version']['stageId'] = $setStageId;
543 $cmdArray[$table][$uid]['version']['comment'] = $comments;
544 $cmdArray[$table][$uid]['version']['notificationAlternativeRecipients'] = $recipients;
545 }
546 $this->processTcaCmd($cmdArray);
547 $result = [
548 'success' => true
549 ];
550
551 $this->setTemporaryWorkspace($currentWorkspace);
552 return $result;
553 }
554
555 /**
556 * Gets an object with this structure:
557 *
558 * affects: object
559 * table
560 * t3ver_oid
561 * nextStage
562 * receipients: array with uids
563 * additional: string
564 * comments: string
565 *
566 * @param stdClass $parameters
567 * @return array
568 */
569 public function sendToPrevStageExecute(\stdClass $parameters)
570 {
571 $cmdArray = [];
572 $setStageId = $parameters->affects->nextStage;
573 $comments = $parameters->comments;
574 $table = $parameters->affects->table;
575 $uid = $parameters->affects->uid;
576
577 $elementRecord = BackendUtility::getRecord($table, $uid);
578 $currentWorkspace = $this->setTemporaryWorkspace($elementRecord['t3ver_wsid']);
579
580 $recipients = $this->getRecipientList($parameters->receipients, $parameters->additional, $setStageId);
581 $cmdArray[$table][$uid]['version']['action'] = 'setStage';
582 $cmdArray[$table][$uid]['version']['stageId'] = $setStageId;
583 $cmdArray[$table][$uid]['version']['comment'] = $comments;
584 $cmdArray[$table][$uid]['version']['notificationAlternativeRecipients'] = $recipients;
585 $this->processTcaCmd($cmdArray);
586 $result = [
587 'success' => true
588 ];
589
590 $this->setTemporaryWorkspace($currentWorkspace);
591 return $result;
592 }
593
594 /**
595 * Gets an object with this structure:
596 *
597 * affects: object
598 * elements: array
599 * 0: object
600 * table
601 * t3ver_oid
602 * uid
603 * 1: object
604 * table
605 * t3ver_oid
606 * uid
607 * nextStage
608 * recipients: array with uids
609 * additional: string
610 * comments: string
611 *
612 * @param \stdClass $parameters
613 * @return array
614 */
615 public function sendToSpecificStageExecute(\stdClass $parameters)
616 {
617 $cmdArray = [];
618 $setStageId = $parameters->affects->nextStage;
619 $comments = $parameters->comments;
620 $elements = $parameters->affects->elements;
621 $recipients = $this->getRecipientList($parameters->receipients, $parameters->additional, $setStageId);
622 foreach ($elements as $element) {
623 // Avoid any action on records that have already been published to live
624 $elementRecord = BackendUtility::getRecord($element->table, $element->uid);
625 if ((int)$elementRecord['t3ver_wsid'] === 0) {
626 continue;
627 }
628
629 if ($setStageId == StagesService::STAGE_PUBLISH_EXECUTE_ID) {
630 $cmdArray[$element->table][$element->t3ver_oid]['version']['action'] = 'swap';
631 $cmdArray[$element->table][$element->t3ver_oid]['version']['swapWith'] = $element->uid;
632 $cmdArray[$element->table][$element->t3ver_oid]['version']['comment'] = $comments;
633 $cmdArray[$element->table][$element->t3ver_oid]['version']['notificationAlternativeRecipients'] = $recipients;
634 } else {
635 $cmdArray[$element->table][$element->uid]['version']['action'] = 'setStage';
636 $cmdArray[$element->table][$element->uid]['version']['stageId'] = $setStageId;
637 $cmdArray[$element->table][$element->uid]['version']['comment'] = $comments;
638 $cmdArray[$element->table][$element->uid]['version']['notificationAlternativeRecipients'] = $recipients;
639 }
640 }
641 $this->processTcaCmd($cmdArray);
642 $result = [
643 'success' => true
644 ];
645 return $result;
646 }
647
648 /**
649 * Gets the dialog window to be displayed before a record can be sent to a stage.
650 *
651 * @param StageRecord|int $nextStageId
652 * @return array
653 */
654 protected function getSentToStageWindow($nextStage)
655 {
656 if (!$nextStage instanceof StageRecord) {
657 $nextStage = WorkspaceRecord::get($this->getCurrentWorkspace())->getStage($nextStage);
658 }
659
660 $result = [
661 'title' => $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:actionSendToStage'),
662 'items' => [
663 [
664 'xtype' => 'panel',
665 'bodyStyle' => 'margin-bottom: 7px; border: none;',
666 'html' => $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:window.sendToNextStageWindow.itemsWillBeSentTo') . ' ' . $nextStage->getTitle()
667 ]
668 ]
669 ];
670
671 if ($nextStage->isDialogEnabled()) {
672 $result['items'][] = [
673 'fieldLabel' => $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:window.sendToNextStageWindow.sendMailTo'),
674 'xtype' => 'checkboxgroup',
675 'itemCls' => 'x-check-group-alt',
676 'columns' => 1,
677 'style' => 'max-height: 200px',
678 'autoScroll' => true,
679 'items' => [
680 $this->getReceipientsOfStage($nextStage)
681 ]
682 ];
683 $result['items'][] = [
684 'fieldLabel' => $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:window.sendToNextStageWindow.additionalRecipients'),
685 'name' => 'additional',
686 'xtype' => 'textarea',
687 'width' => 250
688 ];
689 }
690 $result['items'][] = [
691 'fieldLabel' => $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:window.sendToNextStageWindow.comments'),
692 'name' => 'comments',
693 'xtype' => 'textarea',
694 'width' => 250,
695 'value' => ($nextStage->isInternal() ? '' : $nextStage->getDefaultComment())
696 ];
697
698 return $result;
699 }
700
701 /**
702 * Gets all assigned recipients of a particular stage.
703 *
704 * @param StageRecord|int $stageRecord
705 * @return array
706 */
707 protected function getReceipientsOfStage($stageRecord)
708 {
709 if (!$stageRecord instanceof StageRecord) {
710 $stageRecord = WorkspaceRecord::get($this->getCurrentWorkspace())->getStage($stageRecord);
711 }
712
713 $result = [];
714 $allRecipients = $this->getStageService()->getResponsibleBeUser($stageRecord);
715 $preselectedRecipients = $this->stageService->getPreselectedRecipients($stageRecord);
716 $isPreselectionChangeable = $stageRecord->isPreselectionChangeable();
717
718 foreach ($allRecipients as $backendUserId => $backendUser) {
719 if (empty($backendUser['email']) || !GeneralUtility::validEmail($backendUser['email'])) {
720 continue;
721 }
722
723 $name = (!empty($backendUser['realName']) ? $backendUser['realName'] : $backendUser['username']);
724 $checked = in_array($backendUserId, $preselectedRecipients);
725 $disabled = ($checked && !$isPreselectionChangeable);
726
727 $result[] = [
728 'boxLabel' => sprintf('%s (%s)', $name, $backendUser['email']),
729 'name' => 'receipients-' . $backendUserId,
730 'checked' => $checked,
731 'disabled' => $disabled
732 ];
733 }
734
735 return $result;
736 }
737
738 /**
739 * Gets the default comment of a particular stage.
740 *
741 * @param int $stage
742 * @return string
743 */
744 protected function getDefaultCommentOfStage($stage)
745 {
746 $result = $this->getStageService()->getPropertyOfCurrentWorkspaceStage($stage, 'default_mailcomment');
747 return $result;
748 }
749
750 /**
751 * Gets an instance of the Stage service.
752 *
753 * @return StagesService
754 */
755 protected function getStageService()
756 {
757 if (!isset($this->stageService)) {
758 $this->stageService = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\StagesService::class);
759 }
760 return $this->stageService;
761 }
762
763 /**
764 * Send all available workspace records to the previous stage.
765 *
766 * @param int $id Current page id to process items to previous stage.
767 * @return array
768 */
769 public function sendPageToPreviousStage($id)
770 {
771 $workspaceService = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\WorkspaceService::class);
772 $workspaceItemsArray = $workspaceService->selectVersionsInWorkspace($this->stageService->getWorkspaceId(), ($filter = 1), ($stage = -99), $id, ($recursionLevel = 0), ($selectionType = 'tables_modify'));
773 list($currentStage, $previousStage) = $this->getStageService()->getPreviousStageForElementCollection($workspaceItemsArray);
774 // get only the relevant items for processing
775 $workspaceItemsArray = $workspaceService->selectVersionsInWorkspace($this->stageService->getWorkspaceId(), ($filter = 1), $currentStage['uid'], $id, ($recursionLevel = 0), ($selectionType = 'tables_modify'));
776 return [
777 'title' => 'Status message: Page send to next stage - ID: ' . $id . ' - Next stage title: ' . $previousStage['title'],
778 'items' => $this->getSentToStageWindow($previousStage['uid']),
779 'affects' => $workspaceItemsArray,
780 'stageId' => $previousStage['uid']
781 ];
782 }
783
784 /**
785 * @param int $id Current Page id to select Workspace items from.
786 * @return array
787 */
788 public function sendPageToNextStage($id)
789 {
790 $workspaceService = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\WorkspaceService::class);
791 $workspaceItemsArray = $workspaceService->selectVersionsInWorkspace($this->stageService->getWorkspaceId(), ($filter = 1), ($stage = -99), $id, ($recursionLevel = 0), ($selectionType = 'tables_modify'));
792 list($currentStage, $nextStage) = $this->getStageService()->getNextStageForElementCollection($workspaceItemsArray);
793 // get only the relevant items for processing
794 $workspaceItemsArray = $workspaceService->selectVersionsInWorkspace($this->stageService->getWorkspaceId(), ($filter = 1), $currentStage['uid'], $id, ($recursionLevel = 0), ($selectionType = 'tables_modify'));
795 return [
796 'title' => 'Status message: Page send to next stage - ID: ' . $id . ' - Next stage title: ' . $nextStage['title'],
797 'items' => $this->getSentToStageWindow($nextStage['uid']),
798 'affects' => $workspaceItemsArray,
799 'stageId' => $nextStage['uid']
800 ];
801 }
802
803 /**
804 * Fetch the current label and visible state of the buttons.
805 *
806 * @param int $id
807 * @return array Contains the visibility state and label of the stage change buttons.
808 */
809 public function updateStageChangeButtons($id)
810 {
811 $stageService = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\StagesService::class);
812 $workspaceService = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\WorkspaceService::class);
813 // fetch the next and previous stage
814 $workspaceItemsArray = $workspaceService->selectVersionsInWorkspace($stageService->getWorkspaceId(), ($filter = 1), ($stage = -99), $id, ($recursionLevel = 0), ($selectionType = 'tables_modify'));
815 list(, $nextStage) = $stageService->getNextStageForElementCollection($workspaceItemsArray);
816 list(, $previousStage) = $stageService->getPreviousStageForElementCollection($workspaceItemsArray);
817 $toolbarButtons = [
818 'feToolbarButtonNextStage' => [
819 'visible' => is_array($nextStage) && !empty($nextStage),
820 'text' => $nextStage['title']
821 ],
822 'feToolbarButtonPreviousStage' => [
823 'visible' => is_array($previousStage) && !empty($previousStage),
824 'text' => $previousStage['title']
825 ],
826 'feToolbarButtonDiscardStage' => [
827 'visible' => is_array($nextStage) && !empty($nextStage) || is_array($previousStage) && !empty($previousStage),
828 'text' => $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:label_doaction_discard', true)
829 ]
830 ];
831 return $toolbarButtons;
832 }
833
834 /**
835 * @param int $workspaceId
836 * @return int Id of the original workspace
837 * @throws \TYPO3\CMS\Core\Exception
838 */
839 protected function setTemporaryWorkspace($workspaceId)
840 {
841 $workspaceId = (int)$workspaceId;
842 $currentWorkspace = (int)$this->getBackendUser()->workspace;
843
844 if ($currentWorkspace !== $workspaceId) {
845 if (!$this->getBackendUser()->setTemporaryWorkspace($workspaceId)) {
846 throw new \TYPO3\CMS\Core\Exception(
847 'Cannot set temporary workspace to "' . $workspaceId . '"',
848 1371484524
849 );
850 }
851 }
852
853 return $currentWorkspace;
854 }
855
856 /**
857 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
858 */
859 protected function getBackendUser()
860 {
861 return $GLOBALS['BE_USER'];
862 }
863 }