Completed
Push — master ( d53f54...2e1e23 )
by
unknown
17:40
created

ActionHandler   F

Complexity

Total Complexity 93

Size/Duplication

Total Lines 825
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 344
dl 0
loc 825
rs 2
c 1
b 0
f 0
wmc 93

32 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A generateWorkspacePreviewLinksForAllLanguages() 0 3 1
A generateWorkspacePreviewLink() 0 3 1
A getPublishCommands() 0 10 2
B updateStageChangeButtons() 0 30 6
A sendPageToNextStage() 0 25 1
A loadColumnModel() 0 6 2
A sendToSpecificStageWindow() 0 14 2
A sendToSpecificStageExecute() 0 31 4
A getFlushCommands() 0 9 2
A getErrorResponse() 0 11 1
A executeSelectionAction() 0 19 5
A sendToPrevStageExecute() 0 19 1
A sendToPrevStageWindow() 0 28 4
A getLanguageService() 0 3 1
B sentCollectionToStage() 0 34 7
A sendToNextStageExecute() 0 27 2
D getRecipientList() 0 68 18
A saveLanguageSelection() 0 7 3
A processTcaCmd() 0 18 3
A deleteSingleRecord() 0 7 1
A getDefaultCommentOfStage() 0 4 1
A getBackendUser() 0 3 1
A viewSingleRecord() 0 3 1
A saveColumnModel() 0 11 2
A sendToNextStageWindow() 0 22 3
B getRecipientsOfStage() 0 30 7
A publishSingleRecord() 0 8 1
A getSentToStageWindow() 0 20 4
A sendPageToPreviousStage() 0 26 1
A getCurrentWorkspace() 0 3 1
A discardStagesFromPage() 0 18 3

How to fix   Complexity   

Complex Class

Complex classes like ActionHandler often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ActionHandler, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * This file is part of the TYPO3 CMS project.
5
 *
6
 * It is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License, either version 2
8
 * of the License, or any later version.
9
 *
10
 * For the full copyright and license information, please read the
11
 * LICENSE.txt file that was distributed with this source code.
12
 *
13
 * The TYPO3 project - inspiring people to share!
14
 */
15
16
namespace TYPO3\CMS\Workspaces\Controller\Remote;
17
18
use TYPO3\CMS\Backend\Utility\BackendUtility;
19
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
20
use TYPO3\CMS\Core\DataHandling\DataHandler;
21
use TYPO3\CMS\Core\Localization\LanguageService;
22
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
23
use TYPO3\CMS\Core\Utility\GeneralUtility;
24
use TYPO3\CMS\Core\Utility\MathUtility;
25
use TYPO3\CMS\Fluid\View\StandaloneView;
26
use TYPO3\CMS\Workspaces\Domain\Record\StageRecord;
27
use TYPO3\CMS\Workspaces\Domain\Record\WorkspaceRecord;
28
use TYPO3\CMS\Workspaces\Preview\PreviewUriBuilder;
29
use TYPO3\CMS\Workspaces\Service\StagesService;
30
use TYPO3\CMS\Workspaces\Service\WorkspaceService;
31
32
/**
33
 * @internal This is a specific Backend Controller implementation and is not considered part of the Public TYPO3 API.
34
 */
35
class ActionHandler
36
{
37
    /**
38
     * @var StagesService
39
     */
40
    protected $stageService;
41
42
    /**
43
     * @var WorkspaceService
44
     */
45
    protected $workspaceService;
46
47
    /**
48
     * Creates this object.
49
     */
50
    public function __construct()
51
    {
52
        $this->stageService = GeneralUtility::makeInstance(StagesService::class);
53
        $this->workspaceService = GeneralUtility::makeInstance(WorkspaceService::class);
54
    }
55
56
    /**
57
     * Generates a workspace preview link.
58
     *
59
     * @param int $uid The ID of the record to be linked
60
     * @return string the full domain including the protocol http:// or https://, but without the trailing '/'
61
     */
62
    public function generateWorkspacePreviewLink($uid)
63
    {
64
        return GeneralUtility::makeInstance(PreviewUriBuilder::class)->buildUriForPage((int)$uid, 0);
65
    }
66
67
    /**
68
     * Generates workspace preview links for all available languages of a page.
69
     *
70
     * @param int $uid
71
     * @return array
72
     */
73
    public function generateWorkspacePreviewLinksForAllLanguages($uid)
74
    {
75
        return GeneralUtility::makeInstance(PreviewUriBuilder::class)->buildUrisForAllLanguagesOfPage((int)$uid);
76
    }
77
78
    /**
79
     * Publishes a single record.
80
     *
81
     * @param string $table
82
     * @param int $t3ver_oid
83
     * @param int $orig_uid
84
     * @todo What about reporting errors back to the interface? /olly/
85
     */
86
    public function publishSingleRecord($table, $t3ver_oid, $orig_uid)
87
    {
88
        $cmd = [];
89
        $cmd[$table][$t3ver_oid]['version'] = [
90
            'action' => 'publish',
91
            'swapWith' => $orig_uid
92
        ];
93
        $this->processTcaCmd($cmd);
94
    }
95
96
    /**
97
     * Deletes a single record.
98
     *
99
     * @param string $table
100
     * @param int $uid
101
     * @todo What about reporting errors back to the interface? /olly/
102
     */
103
    public function deleteSingleRecord($table, $uid)
104
    {
105
        $cmd = [];
106
        $cmd[$table][$uid]['version'] = [
107
            'action' => 'clearWSID'
108
        ];
109
        $this->processTcaCmd($cmd);
110
    }
111
112
    /**
113
     * Generates a view link for a page.
114
     *
115
     * @param string $table
116
     * @param string $uid
117
     * @return string
118
     */
119
    public function viewSingleRecord($table, $uid)
120
    {
121
        return GeneralUtility::makeInstance(PreviewUriBuilder::class)->buildUriForElement($table, $uid);
122
    }
123
124
    /**
125
     * Executes an action (publish, discard) to a selection set.
126
     *
127
     * @param \stdClass $parameter
128
     * @return array
129
     */
130
    public function executeSelectionAction($parameter)
131
    {
132
        $result = [];
133
134
        if (empty($parameter->action) || empty($parameter->selection)) {
135
            $result['error'] = 'No action or record selection given';
136
            return $result;
137
        }
138
139
        $commands = [];
140
        if ($parameter->action === 'publish') {
141
            $commands = $this->getPublishCommands($parameter->selection);
142
        } elseif ($parameter->action === 'discard') {
143
            $commands = $this->getFlushCommands($parameter->selection);
144
        }
145
146
        $result = $this->processTcaCmd($commands);
147
        $result['total'] = count($commands);
148
        return $result;
149
    }
150
151
    /**
152
     * Get publish commands
153
     *
154
     * @param array|\stdClass[] $selection
155
     * @return array
156
     */
157
    protected function getPublishCommands(array $selection)
158
    {
159
        $commands = [];
160
        foreach ($selection as $record) {
161
            $commands[$record->table][$record->liveId]['version'] = [
162
                'action' => 'publish',
163
                'swapWith' => $record->versionId,
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
        $this->getBackendUser()->uc['moduleData']['Workspaces'][$this->getBackendUser()->workspace]['columns'] = $data;
201
        $this->getBackendUser()->writeUC();
202
    }
203
204
    public function loadColumnModel()
205
    {
206
        if (is_array($this->getBackendUser()->uc['moduleData']['Workspaces'][$this->getBackendUser()->workspace]['columns'])) {
207
            return $this->getBackendUser()->uc['moduleData']['Workspaces'][$this->getBackendUser()->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 (MathUtility::canBeInterpretedAsInteger($language) === false && $language !== 'all') {
220
            $language = 'all';
221
        }
222
        $this->getBackendUser()->uc['moduleData']['Workspaces'][$this->getBackendUser()->workspace]['language'] = $language;
223
        $this->getBackendUser()->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
        if (is_array($elementRecord)) {
238
            $workspaceRecord = WorkspaceRecord::get($elementRecord['t3ver_wsid']);
239
            $nextStageRecord = $workspaceRecord->getNextStage($elementRecord['t3ver_stage']);
240
            if ($nextStageRecord !== null) {
241
                $this->stageService->getRecordService()->add($table, $uid);
242
                $result = $this->getSentToStageWindow($nextStageRecord);
243
                $result['affects'] = [
244
                    'table' => $table,
245
                    'nextStage' => $nextStageRecord->getUid(),
246
                    't3ver_oid' => $t3ver_oid,
247
                    'uid' => $uid
248
                ];
249
            } else {
250
                $result = $this->getErrorResponse('error.stageId.invalid', 1291111644);
251
            }
252
        } else {
253
            $result = $this->getErrorResponse('error.sendToNextStage.noRecordFound', 1287264776);
254
        }
255
        return $result;
256
    }
257
258
    /**
259
     * Gets the dialog window to be displayed before a record can be sent to the previous stage.
260
     *
261
     * @param int $uid
262
     * @param string $table
263
     * @return array
264
     */
265
    public function sendToPrevStageWindow($uid, $table)
266
    {
267
        $elementRecord = BackendUtility::getRecord($table, $uid);
268
        if (is_array($elementRecord)) {
269
            $workspaceRecord = WorkspaceRecord::get($elementRecord['t3ver_wsid']);
270
            $stageRecord = $workspaceRecord->getStage($elementRecord['t3ver_stage']);
271
272
            if ($stageRecord !== null) {
273
                if (!$stageRecord->isEditStage()) {
274
                    $this->stageService->getRecordService()->add($table, $uid);
275
                    $previousStageRecord = $stageRecord->getPrevious();
276
                    $result = $this->getSentToStageWindow($previousStageRecord);
277
                    $result['affects'] = [
278
                        'table' => $table,
279
                        'uid' => $uid,
280
                        'nextStage' => $previousStageRecord->getUid()
281
                    ];
282
                } else {
283
                    // element is already in edit stage, there is no prev stage - return an error message
284
                    $result = $this->getErrorResponse('error.sendToPrevStage.noPreviousStage', 1287264746);
285
                }
286
            } else {
287
                $result = $this->getErrorResponse('error.stageId.invalid', 1291111644);
288
            }
289
        } else {
290
            $result = $this->getErrorResponse('error.sendToNextStage.noRecordFound', 1287264765);
291
        }
292
        return $result;
293
    }
294
295
    /**
296
     * Gets the dialog window to be displayed before a record can be sent to a specific stage.
297
     *
298
     * @param int $nextStageId
299
     * @param array|\stdClass[] $elements
300
     * @return array
301
     */
302
    public function sendToSpecificStageWindow($nextStageId, array $elements)
303
    {
304
        foreach ($elements as $element) {
305
            $this->stageService->getRecordService()->add(
306
                $element->table,
307
                $element->uid
308
            );
309
        }
310
311
        $result = $this->getSentToStageWindow($nextStageId);
312
        $result['affects'] = [
313
            'nextStage' => $nextStageId
314
        ];
315
        return $result;
316
    }
317
318
    /**
319
     * Gets a merged variant of recipient defined by uid and custom ones.
320
     *
321
     * @param array $uidOfRecipients list of recipients
322
     * @param string $additionalRecipients given user string of additional recipients
323
     * @param int $stageId stage id
324
     * @return array
325
     * @throws \InvalidArgumentException
326
     */
327
    public function getRecipientList(array $uidOfRecipients, $additionalRecipients, $stageId)
328
    {
329
        $stageRecord = WorkspaceRecord::get($this->getCurrentWorkspace())->getStage($stageId);
330
331
        if ($stageRecord === null) {
332
            throw new \InvalidArgumentException(
333
                $this->getLanguageService()->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:error.stageId.integer'),
334
                1476044776
335
            );
336
        }
337
338
        $recipients = [];
339
        $finalRecipients = [];
340
        $backendUserIds = $stageRecord->getAllRecipients();
341
        foreach ($uidOfRecipients as $userUid) {
342
            // Ensure that only configured backend users are considered
343
            if (!in_array($userUid, $backendUserIds)) {
344
                continue;
345
            }
346
            $beUserRecord = BackendUtility::getRecord('be_users', (int)$userUid);
347
            if (is_array($beUserRecord) && $beUserRecord['email'] !== '') {
348
                $uc = $beUserRecord['uc'] ? unserialize($beUserRecord['uc'], ['allowed_classes' => false]) : [];
349
                $recipients[$beUserRecord['email']] = [
350
                    'email' => $beUserRecord['email'],
351
                    'lang' => $uc['lang'] ?? $beUserRecord['lang']
352
                ];
353
            }
354
        }
355
356
        if ($stageRecord->hasPreselection() && !$stageRecord->isPreselectionChangeable()) {
357
            $preselectedBackendUsers = $this->stageService->getBackendUsers(
358
                implode(',', $this->stageService->getPreselectedRecipients($stageRecord))
359
            );
360
361
            foreach ($preselectedBackendUsers as $preselectedBackendUser) {
362
                if (empty($preselectedBackendUser['email']) || !GeneralUtility::validEmail($preselectedBackendUser['email'])) {
363
                    continue;
364
                }
365
                if (!isset($recipients[$preselectedBackendUser['email']])) {
366
                    $uc = (!empty($preselectedBackendUser['uc']) ? unserialize($preselectedBackendUser['uc'], ['allowed_classes' => false]) : []);
367
                    $recipients[$preselectedBackendUser['email']] = [
368
                        'email' => $preselectedBackendUser['email'],
369
                        'lang' => $uc['lang'] ?? $preselectedBackendUser['lang']
370
                    ];
371
                }
372
            }
373
        }
374
375
        if ($additionalRecipients !== '') {
376
            $emails = GeneralUtility::trimExplode(LF, $additionalRecipients, true);
377
            $additionalRecipients = [];
378
            foreach ($emails as $email) {
379
                $additionalRecipients[$email] = ['email' => $email];
380
            }
381
        } else {
382
            $additionalRecipients = [];
383
        }
384
        // We merge $recipients on top of $additionalRecipients because $recipients
385
        // possibly is more complete with a user language. Furthermore, the list of
386
        // recipients is automatically unique since we indexed $additionalRecipients
387
        // and $recipients with the email address
388
        $allRecipients = array_merge($additionalRecipients, $recipients);
389
        foreach ($allRecipients as $email => $recipientInformation) {
390
            if (GeneralUtility::validEmail($email)) {
391
                $finalRecipients[] = $recipientInformation;
392
            }
393
        }
394
        return $finalRecipients;
395
    }
396
397
    /**
398
     * Discard all items from given page id.
399
     *
400
     * @param int $pageId
401
     * @return array
402
     */
403
    public function discardStagesFromPage($pageId)
404
    {
405
        $cmdMapArray = [];
406
        $workspaceItemsArray = $this->workspaceService->selectVersionsInWorkspace(
407
            $this->stageService->getWorkspaceId(),
408
            -99,
409
            $pageId,
410
            0,
411
            'tables_modify'
412
        );
413
        foreach ($workspaceItemsArray as $tableName => $items) {
414
            foreach ($items as $item) {
415
                $cmdMapArray[$tableName][$item['uid']]['version']['action'] = 'clearWSID';
416
            }
417
        }
418
        $this->processTcaCmd($cmdMapArray);
419
        return [
420
            'success' => true
421
        ];
422
    }
423
424
    /**
425
     * Push the given element collection to the next workspace stage.
426
     *
427
     * <code>
428
     * $parameters->additional = [email protected]
429
     * $parameters->affects->__TABLENAME__
430
     * $parameters->comments
431
     * $parameters->recipients
432
     * $parameters->stageId
433
     * </code>
434
     *
435
     * @param \stdClass $parameters
436
     * @return array
437
     */
438
    public function sentCollectionToStage(\stdClass $parameters)
439
    {
440
        $cmdMapArray = [];
441
        $comment = $parameters->comments;
442
        $stageId = $parameters->stageId;
443
        if (MathUtility::canBeInterpretedAsInteger($stageId) === false) {
444
            throw new \InvalidArgumentException('Missing "stageId" in $parameters array.', 1319488194);
445
        }
446
        if (!is_object($parameters->affects) || empty($parameters->affects)) {
447
            throw new \InvalidArgumentException('Missing "affected items" in $parameters array.', 1319488195);
448
        }
449
        $recipients = $this->getRecipientList((array)$parameters->recipients, $parameters->additional, $stageId);
450
        foreach ($parameters->affects as $tableName => $items) {
451
            foreach ($items as $item) {
452
                // Publishing uses live id in command map
453
                if ($stageId == StagesService::STAGE_PUBLISH_EXECUTE_ID) {
454
                    $cmdMapArray[$tableName][$item->t3ver_oid]['version']['action'] = 'publish';
455
                    $cmdMapArray[$tableName][$item->t3ver_oid]['version']['swapWith'] = $item->uid;
456
                    $cmdMapArray[$tableName][$item->t3ver_oid]['version']['comment'] = $comment;
457
                    $cmdMapArray[$tableName][$item->t3ver_oid]['version']['notificationAlternativeRecipients'] = $recipients;
458
                } else {
459
                    // Setting stage uses version id in command map
460
                    $cmdMapArray[$tableName][$item->uid]['version']['action'] = 'setStage';
461
                    $cmdMapArray[$tableName][$item->uid]['version']['stageId'] = $stageId;
462
                    $cmdMapArray[$tableName][$item->uid]['version']['comment'] = $comment;
463
                    $cmdMapArray[$tableName][$item->uid]['version']['notificationAlternativeRecipients'] = $recipients;
464
                }
465
            }
466
        }
467
        $this->processTcaCmd($cmdMapArray);
468
        return [
469
            'success' => true,
470
            // force refresh after publishing changes
471
            'refreshLivePanel' => $parameters->stageId == -20
472
        ];
473
    }
474
475
    /**
476
     * Process TCA command map array.
477
     *
478
     * @param array $cmdMapArray
479
     * @return array
480
     */
481
    protected function processTcaCmd(array $cmdMapArray)
482
    {
483
        $result = [];
484
485
        if (empty($cmdMapArray)) {
486
            $result['error'] = 'No commands given to be processed';
487
            return $result;
488
        }
489
490
        $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
491
        $dataHandler->start([], $cmdMapArray);
492
        $dataHandler->process_cmdmap();
493
494
        if ($dataHandler->errorLog) {
495
            $result['error'] = implode('<br/>', $dataHandler->errorLog);
496
        }
497
498
        return $result;
499
    }
500
501
    /**
502
     * Gets an object with this structure:
503
     *
504
     * affects: object
505
     * table
506
     * t3ver_oid
507
     * nextStage
508
     * uid
509
     * recipients: array with uids
510
     * additional: string
511
     * comments: string
512
     *
513
     * @param \stdClass $parameters
514
     * @return array
515
     */
516
    public function sendToNextStageExecute(\stdClass $parameters)
517
    {
518
        $cmdArray = [];
519
        $setStageId = (int)$parameters->affects->nextStage;
520
        $comments = $parameters->comments;
521
        $table = $parameters->affects->table;
522
        $uid = $parameters->affects->uid;
523
        $t3ver_oid = $parameters->affects->t3ver_oid;
524
525
        $recipients = $this->getRecipientList((array)$parameters->recipients, $parameters->additional, $setStageId);
526
        if ($setStageId === StagesService::STAGE_PUBLISH_EXECUTE_ID) {
527
            $cmdArray[$table][$t3ver_oid]['version']['action'] = 'publish';
528
            $cmdArray[$table][$t3ver_oid]['version']['swapWith'] = $uid;
529
            $cmdArray[$table][$t3ver_oid]['version']['comment'] = $comments;
530
            $cmdArray[$table][$t3ver_oid]['version']['notificationAlternativeRecipients'] = $recipients;
531
        } else {
532
            $cmdArray[$table][$uid]['version']['action'] = 'setStage';
533
            $cmdArray[$table][$uid]['version']['stageId'] = $setStageId;
534
            $cmdArray[$table][$uid]['version']['comment'] = $comments;
535
            $cmdArray[$table][$uid]['version']['notificationAlternativeRecipients'] = $recipients;
536
        }
537
        $this->processTcaCmd($cmdArray);
538
        $result = [
539
            'success' => true
540
        ];
541
542
        return $result;
543
    }
544
545
    /**
546
     * Gets an object with this structure:
547
     *
548
     * affects: object
549
     * table
550
     * t3ver_oid
551
     * nextStage
552
     * recipients: array with uids
553
     * additional: string
554
     * comments: string
555
     *
556
     * @param \stdClass $parameters
557
     * @return array
558
     */
559
    public function sendToPrevStageExecute(\stdClass $parameters)
560
    {
561
        $cmdArray = [];
562
        $setStageId = $parameters->affects->nextStage;
563
        $comments = $parameters->comments;
564
        $table = $parameters->affects->table;
565
        $uid = $parameters->affects->uid;
566
567
        $recipients = $this->getRecipientList((array)$parameters->recipients, $parameters->additional, $setStageId);
568
        $cmdArray[$table][$uid]['version']['action'] = 'setStage';
569
        $cmdArray[$table][$uid]['version']['stageId'] = $setStageId;
570
        $cmdArray[$table][$uid]['version']['comment'] = $comments;
571
        $cmdArray[$table][$uid]['version']['notificationAlternativeRecipients'] = $recipients;
572
        $this->processTcaCmd($cmdArray);
573
        $result = [
574
            'success' => true
575
        ];
576
577
        return $result;
578
    }
579
580
    /**
581
     * Gets an object with this structure:
582
     *
583
     * affects: object
584
     * elements: array
585
     * 0: object
586
     * table
587
     * t3ver_oid
588
     * uid
589
     * 1: object
590
     * table
591
     * t3ver_oid
592
     * uid
593
     * nextStage
594
     * recipients: array with uids
595
     * additional: string
596
     * comments: string
597
     *
598
     * @param \stdClass $parameters
599
     * @return array
600
     */
601
    public function sendToSpecificStageExecute(\stdClass $parameters)
602
    {
603
        $cmdArray = [];
604
        $setStageId = (int)$parameters->affects->nextStage;
605
        $comments = $parameters->comments;
606
        $elements = $parameters->affects->elements;
607
        $recipients = $this->getRecipientList((array)$parameters->recipients, $parameters->additional, $setStageId);
608
        foreach ($elements as $element) {
609
            // Avoid any action on records that have already been published to live
610
            $elementRecord = BackendUtility::getRecord($element->table, $element->uid);
611
            if ((int)$elementRecord['t3ver_wsid'] === 0) {
612
                continue;
613
            }
614
615
            if ($setStageId === StagesService::STAGE_PUBLISH_EXECUTE_ID) {
616
                $cmdArray[$element->table][$element->t3ver_oid]['version']['action'] = 'publish';
617
                $cmdArray[$element->table][$element->t3ver_oid]['version']['swapWith'] = $element->uid;
618
                $cmdArray[$element->table][$element->t3ver_oid]['version']['comment'] = $comments;
619
                $cmdArray[$element->table][$element->t3ver_oid]['version']['notificationAlternativeRecipients'] = $recipients;
620
            } else {
621
                $cmdArray[$element->table][$element->uid]['version']['action'] = 'setStage';
622
                $cmdArray[$element->table][$element->uid]['version']['stageId'] = $setStageId;
623
                $cmdArray[$element->table][$element->uid]['version']['comment'] = $comments;
624
                $cmdArray[$element->table][$element->uid]['version']['notificationAlternativeRecipients'] = $recipients;
625
            }
626
        }
627
        $this->processTcaCmd($cmdArray);
628
        $result = [
629
            'success' => true
630
        ];
631
        return $result;
632
    }
633
634
    /**
635
     * Gets the dialog window to be displayed before a record can be sent to a stage.
636
     *
637
     * @param StageRecord|int $nextStage
638
     * @return array
639
     */
640
    protected function getSentToStageWindow($nextStage)
641
    {
642
        if (!$nextStage instanceof StageRecord) {
643
            $nextStage = WorkspaceRecord::get($this->getCurrentWorkspace())->getStage($nextStage);
644
        }
645
646
        $result = [];
647
        if ($nextStage->isDialogEnabled()) {
648
            $result['sendMailTo'] = $this->getRecipientsOfStage($nextStage);
649
            $result['additional'] = [
650
                'type' => 'textarea',
651
                'value' => ''
652
            ];
653
        }
654
        $result['comments'] = [
655
            'type' => 'textarea',
656
            'value' => $nextStage->isInternal() ? '' : $nextStage->getDefaultComment()
657
        ];
658
659
        return $result;
660
    }
661
662
    /**
663
     * Gets all assigned recipients of a particular stage.
664
     *
665
     * @param StageRecord|int $stageRecord
666
     * @return array
667
     */
668
    protected function getRecipientsOfStage($stageRecord)
669
    {
670
        if (!$stageRecord instanceof StageRecord) {
671
            $stageRecord = WorkspaceRecord::get($this->getCurrentWorkspace())->getStage($stageRecord);
672
        }
673
674
        $result = [];
675
        $allRecipients = $this->stageService->getResponsibleBeUser($stageRecord);
676
        $preselectedRecipients = $this->stageService->getPreselectedRecipients($stageRecord);
0 ignored issues
show
Bug introduced by
It seems like $stageRecord can also be of type null; however, parameter $stageRecord of TYPO3\CMS\Workspaces\Ser...PreselectedRecipients() does only seem to accept TYPO3\CMS\Workspaces\Domain\Record\StageRecord, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

676
        $preselectedRecipients = $this->stageService->getPreselectedRecipients(/** @scrutinizer ignore-type */ $stageRecord);
Loading history...
677
        $isPreselectionChangeable = $stageRecord->isPreselectionChangeable();
678
679
        foreach ($allRecipients as $backendUserId => $backendUser) {
680
            if (empty($backendUser['email']) || !GeneralUtility::validEmail($backendUser['email'])) {
681
                continue;
682
            }
683
684
            $name = (!empty($backendUser['realName']) ? $backendUser['realName'] : $backendUser['username']);
685
            $checked = in_array($backendUserId, $preselectedRecipients);
686
            $disabled = ($checked && !$isPreselectionChangeable);
687
688
            $result[] = [
689
                'label' => sprintf('%s (%s)', $name, $backendUser['email']),
690
                'value' => $backendUserId,
691
                'name' => 'recipients-' . $backendUserId,
692
                'checked' => $checked,
693
                'disabled' => $disabled
694
            ];
695
        }
696
697
        return $result;
698
    }
699
700
    /**
701
     * Gets the default comment of a particular stage.
702
     *
703
     * @param int $stage
704
     * @return string
705
     */
706
    protected function getDefaultCommentOfStage($stage)
707
    {
708
        $result = $this->stageService->getPropertyOfCurrentWorkspaceStage($stage, 'default_mailcomment');
709
        return $result;
710
    }
711
712
    /**
713
     * Send all available workspace records to the previous stage.
714
     *
715
     * @param int $id Current page id to process items to previous stage.
716
     * @return array
717
     */
718
    public function sendPageToPreviousStage($id)
719
    {
720
        $workspaceItemsArray = $this->workspaceService->selectVersionsInWorkspace(
721
            $this->stageService->getWorkspaceId(),
722
            -99,
723
            $id,
724
            0,
725
            'tables_modify'
726
        );
727
        [$currentStage, $previousStage] = $this->stageService->getPreviousStageForElementCollection($workspaceItemsArray);
728
        // get only the relevant items for processing
729
        $workspaceItemsArray = $this->workspaceService->selectVersionsInWorkspace(
730
            $this->stageService->getWorkspaceId(),
731
            $currentStage['uid'],
732
            $id,
733
            0,
734
            'tables_modify'
735
        );
736
        $stageFormFields = $this->getSentToStageWindow($previousStage['uid']);
737
        $result = array_merge($stageFormFields, [
738
            'title' => 'Status message: Page send to next stage - ID: ' . $id . ' - Next stage title: ' . $previousStage['title'],
739
            'items' => $this->getSentToStageWindow($previousStage['uid']),
740
            'affects' => $workspaceItemsArray,
741
            'stageId' => $previousStage['uid']
742
        ]);
743
        return $result;
744
    }
745
746
    /**
747
     * @param int $id Current Page id to select Workspace items from.
748
     * @return array
749
     */
750
    public function sendPageToNextStage($id)
751
    {
752
        $workspaceItemsArray = $this->workspaceService->selectVersionsInWorkspace(
753
            $this->stageService->getWorkspaceId(),
754
            -99,
755
            $id,
756
            0,
757
            'tables_modify'
758
        );
759
        [$currentStage, $nextStage] = $this->stageService->getNextStageForElementCollection($workspaceItemsArray);
760
        // get only the relevant items for processing
761
        $workspaceItemsArray = $this->workspaceService->selectVersionsInWorkspace(
762
            $this->stageService->getWorkspaceId(),
763
            $currentStage['uid'],
764
            $id,
765
            0,
766
            'tables_modify'
767
        );
768
        $stageFormFields = $this->getSentToStageWindow($nextStage['uid']);
769
        $result = array_merge($stageFormFields, [
770
            'title' => 'Status message: Page send to next stage - ID: ' . $id . ' - Next stage title: ' . $nextStage['title'],
771
            'affects' => $workspaceItemsArray,
772
            'stageId' => $nextStage['uid']
773
        ]);
774
        return $result;
775
    }
776
777
    /**
778
     * Fetch the current label and visible state of the buttons.
779
     *
780
     * @param int $id
781
     * @return string The pre-rendered HTML for the stage buttons
782
     */
783
    public function updateStageChangeButtons($id)
784
    {
785
        // fetch the next and previous stage
786
        $workspaceItemsArray = $this->workspaceService->selectVersionsInWorkspace(
787
            $this->stageService->getWorkspaceId(),
788
            -99,
789
            $id,
790
            0,
791
            'tables_modify'
792
        );
793
        [, $nextStage] = $this->stageService->getNextStageForElementCollection($workspaceItemsArray);
794
        [, $previousStage] = $this->stageService->getPreviousStageForElementCollection($workspaceItemsArray);
795
796
        $view = GeneralUtility::makeInstance(StandaloneView::class);
797
        $extensionPath = ExtensionManagementUtility::extPath('workspaces');
798
        $view->setPartialRootPaths(['default' => $extensionPath . 'Resources/Private/Partials']);
799
        $view->setTemplatePathAndFilename($extensionPath . 'Resources/Private/Templates/Preview/Ajax/StageButtons.html');
800
        $request = $view->getRequest();
801
        $request->setControllerExtensionName('workspaces');
802
        $view->assignMultiple([
803
            'enablePreviousStageButton' => is_array($previousStage) && !empty($previousStage),
804
            'enableNextStageButton' => is_array($nextStage) && !empty($nextStage),
805
            'enableDiscardStageButton' => is_array($nextStage) && !empty($nextStage) || is_array($previousStage) && !empty($previousStage),
806
            'nextStage' => $nextStage['title'],
807
            'nextStageId' => $nextStage['uid'],
808
            'prevStage' => $previousStage['title'],
809
            'prevStageId' => $previousStage['uid'],
810
        ]);
811
        $renderedView = $view->render();
812
        return $renderedView;
813
    }
814
815
    /**
816
     * @return BackendUserAuthentication
817
     */
818
    protected function getBackendUser()
819
    {
820
        return $GLOBALS['BE_USER'];
821
    }
822
823
    /**
824
     * @return LanguageService
825
     */
826
    protected function getLanguageService()
827
    {
828
        return $GLOBALS['LANG'];
829
    }
830
831
    /**
832
     * Gets an error response to be shown in the grid component.
833
     *
834
     * @param string $errorLabel Name of the label in the locallang.xlf file
835
     * @param int $errorCode The error code to be used
836
     * @param bool $successFlagValue Value of the success flag to be delivered back (might be FALSE in most cases)
837
     * @return array
838
     */
839
    protected function getErrorResponse($errorLabel, $errorCode = 0, $successFlagValue = false)
840
    {
841
        $localLangFile = 'LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf';
842
        $response = [
843
            'error' => [
844
                'code' => $errorCode,
845
                'message' => $this->getLanguageService()->sL($localLangFile . ':' . $errorLabel)
846
            ],
847
            'success' => $successFlagValue
848
        ];
849
        return $response;
850
    }
851
852
    /**
853
     * Gets the current workspace ID.
854
     *
855
     * @return int The current workspace ID
856
     */
857
    protected function getCurrentWorkspace()
858
    {
859
        return $this->workspaceService->getCurrentWorkspace();
860
    }
861
}
862