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