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