createDropUpContainer()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 9
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 14
rs 9.9666
1
<?php
2
3
namespace LeKoala\CmsActions;
4
5
use Exception;
6
use ReflectionMethod;
7
use SilverStripe\Forms\Tab;
8
use SilverStripe\Forms\Form;
9
use SilverStripe\Forms\TabSet;
10
use SilverStripe\ORM\DataObject;
11
use SilverStripe\Core\Extensible;
12
use SilverStripe\Forms\FieldList;
13
use SilverStripe\Forms\FormField;
14
use SilverStripe\Control\Director;
15
use SilverStripe\Forms\FormAction;
16
use SilverStripe\Admin\LeftAndMain;
17
use SilverStripe\Forms\HiddenField;
18
use SilverStripe\Control\Controller;
19
use SilverStripe\Control\HTTPRequest;
20
use SilverStripe\Control\HTTPResponse;
21
use SilverStripe\Forms\CompositeField;
22
use SilverStripe\ORM\ValidationResult;
23
use SilverStripe\SiteConfig\SiteConfig;
0 ignored issues
show
Bug introduced by
The type SilverStripe\SiteConfig\SiteConfig was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
24
use SilverStripe\Core\Config\Configurable;
25
use SilverStripe\ORM\FieldType\DBHTMLText;
26
use SilverStripe\Control\HTTPResponse_Exception;
27
use SilverStripe\Forms\GridField\GridFieldDetailForm_ItemRequest;
28
use ReflectionObject;
29
use SilverStripe\Admin\ModelAdmin;
30
use SilverStripe\Core\Extension;
31
use SilverStripe\View\ViewableData;
32
33
/**
34
 * Decorates GridDetailForm_ItemRequest to use new form actions and buttons.
35
 *
36
 * This is also applied to LeftAndMain to allow actions on pages
37
 * Warning: LeftAndMain doesn't call updateItemEditForm
38
 *
39
 * This is a lightweight version of BetterButtons that use default getCMSActions functionnality
40
 * on DataObjects
41
 *
42
 * @link https://github.com/unclecheese/silverstripe-gridfield-betterbuttons
43
 * @link https://github.com/unclecheese/silverstripe-gridfield-betterbuttons/blob/master/src/Extensions/GridFieldBetterButtonsItemRequest.php
44
 * @property \SilverStripe\Admin\LeftAndMain|\SilverStripe\Forms\GridField\GridFieldDetailForm_ItemRequest|ActionsGridFieldItemRequest $owner
45
 * @extends \SilverStripe\Core\Extension<object>
46
 */
47
class ActionsGridFieldItemRequest extends Extension
48
{
49
    use Configurable;
50
    use Extensible;
51
52
    /**
53
     * @config
54
     * @var boolean
55
     */
56
    private static $enable_save_prev_next = true;
57
58
    /**
59
     * @config
60
     * @var boolean
61
     */
62
    private static $enable_save_close = true;
63
64
    /**
65
     * @config
66
     * @var boolean
67
     */
68
    private static $enable_delete_right = true;
69
70
    /**
71
     * @config
72
     * @var boolean
73
     */
74
    private static $enable_utils_prev_next = false;
0 ignored issues
show
introduced by
The private property $enable_utils_prev_next is not used, and could be removed.
Loading history...
75
76
    /**
77
     * @var array<string> Allowed controller actions
78
     */
79
    private static $allowed_actions = [
0 ignored issues
show
introduced by
The private property $allowed_actions is not used, and could be removed.
Loading history...
80
        'doSaveAndClose',
81
        'doSaveAndNext',
82
        'doSaveAndPrev',
83
        'doCustomAction', // For CustomAction
84
        'doCustomLink', // For CustomLink
85
    ];
86
87
    /**
88
     * @param FieldList $actions
89
     * @return array<string>
90
     */
91
    protected function getAvailableActions($actions)
92
    {
93
        $list = [];
94
        foreach ($actions as $action) {
95
            if (is_a($action, CompositeField::class)) {
96
                $list = array_merge($list, $this->getAvailableActions($action->FieldList()));
97
            } else {
98
                $list[] = $action->getName();
99
            }
100
        }
101
        return $list;
102
    }
103
104
    /**
105
     * This module does not interact with the /schema/SearchForm endpoint
106
     * and therefore all requests for these urls don't need any special treatement
107
     *
108
     * @return bool
109
     */
110
    protected function isSearchFormRequest(): bool
111
    {
112
        if (!Controller::has_curr()) {
0 ignored issues
show
Deprecated Code introduced by
The function SilverStripe\Control\Controller::has_curr() has been deprecated: 5.4.0 Will be removed without equivalent functionality to replace it in a future major release ( Ignorable by Annotation )

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

112
        if (!/** @scrutinizer ignore-deprecated */ Controller::has_curr()) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
113
            return false;
114
        }
115
        $curr =  Controller::curr();
116
        if ($curr) {
0 ignored issues
show
introduced by
$curr is of type SilverStripe\Control\Controller, thus it always evaluated to true.
Loading history...
117
            return str_contains($curr->getRequest()->getURL(), '/schema/SearchForm');
118
        }
119
        return false;
120
    }
121
122
    /**
123
     * Called by CMSMain, typically in the CMS or in the SiteConfig admin
124
     * CMSMain already uses getCMSActions so we are good to go with anything defined there
125
     *
126
     * @param Form $form
127
     * @return void
128
     */
129
    public function updateEditForm(Form $form)
130
    {
131
        // Ignore search form requests
132
        if ($this->isSearchFormRequest()) {
133
            return;
134
        }
135
136
        $actions = $form->Actions();
137
138
        // We create a Drop-Up menu afterwards because it may already exist in the $CMSActions
139
        // and we don't want to duplicate it
140
        $this->processDropUpMenu($actions);
141
    }
142
143
    /**
144
     * @return FieldList|false
145
     */
146
    public function recordCmsUtils()
147
    {
148
        /** @var \SilverStripe\Versioned\VersionedGridFieldItemRequest|\SilverStripe\Admin\LeftAndMain $owner */
149
        $owner = $this->owner;
150
151
        // At this stage, the get record could be from a gridfield item request, or from a more general left and main which requires an id
152
        // maybe we could simply do:
153
        // $record = DataObject::singleton($controller->getModelClass());
154
        $reflectionMethod = new ReflectionMethod($owner, 'getRecord');
155
        $record = count($reflectionMethod->getParameters()) > 0 ? $owner->getRecord(0) : $owner->getRecord();
0 ignored issues
show
Unused Code introduced by
The call to SilverStripe\Forms\GridF...temRequest::getRecord() has too many arguments starting with 0. ( Ignorable by Annotation )

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

155
        $record = count($reflectionMethod->getParameters()) > 0 ? $owner->/** @scrutinizer ignore-call */ getRecord(0) : $owner->getRecord();

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Bug introduced by
The call to SilverStripe\Admin\LeftAndMain::getRecord() has too few arguments starting with id. ( Ignorable by Annotation )

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

155
        $record = count($reflectionMethod->getParameters()) > 0 ? $owner->getRecord(0) : $owner->/** @scrutinizer ignore-call */ getRecord();

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
156
        if ($record && $record->hasMethod('getCMSUtils')) {
157
            //@phpstan-ignore-next-line
158
            $utils = $record->getCMSUtils();
159
            $this->extend('onCMSUtils', $utils, $record);
160
            $record->extend('onCMSUtils', $utils);
161
            return $utils;
162
        }
163
        return false;
164
    }
165
166
    /**
167
     * @param Form $form
168
     * @return void
169
     */
170
    public function updateItemEditForm($form)
171
    {
172
        /** @var ?DataObject $record */
173
        $record = $this->owner->getRecord();
0 ignored issues
show
Bug introduced by
The call to SilverStripe\Admin\LeftAndMain::getRecord() has too few arguments starting with id. ( Ignorable by Annotation )

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

173
        /** @scrutinizer ignore-call */ 
174
        $record = $this->owner->getRecord();

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Bug introduced by
The method getRecord() does not exist on LeKoala\CmsActions\ActionsGridFieldItemRequest. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

173
        /** @scrutinizer ignore-call */ 
174
        $record = $this->owner->getRecord();
Loading history...
174
        if (!$record) {
0 ignored issues
show
introduced by
$record is of type SilverStripe\ORM\DataObject, thus it always evaluated to true.
Loading history...
175
            return;
176
        }
177
178
        // We get the actions as defined on our record
179
        $CMSActions = $this->getCmsActionsFromRecord($record);
180
181
        $FormActions = $form->Actions();
182
183
        // Push our actions that are otherwise ignored by SilverStripe
184
        if ($CMSActions) {
0 ignored issues
show
introduced by
$CMSActions is of type SilverStripe\Forms\FieldList, thus it always evaluated to true.
Loading history...
185
            foreach ($CMSActions as $CMSAction) {
186
                $action = $FormActions->fieldByName($CMSAction->getName());
187
188
                if ($action) {
189
                    // If it has been made readonly, revert
190
                    if ($CMSAction->isReadonly() != $action->isReadonly()) {
191
                        $FormActions->replaceField($action->getName(), $action->setReadonly($CMSAction->isReadonly()));
192
                    }
193
                }
194
            }
195
        }
196
    }
197
198
    /**
199
     * Called by GridField_ItemRequest
200
     * We add our custom save&close, save&next and other tweaks
201
     * Actions can be made readonly after this extension point
202
     * @param FieldList $actions
203
     * @return void
204
     */
205
    public function updateFormActions($actions)
206
    {
207
        // Ignore search form requests
208
        if ($this->isSearchFormRequest()) {
209
            return;
210
        }
211
212
        /** @var DataObject|ViewableData|null $record */
213
        $record = $this->owner->getRecord();
0 ignored issues
show
Bug introduced by
The call to SilverStripe\Admin\LeftAndMain::getRecord() has too few arguments starting with id. ( Ignorable by Annotation )

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

213
        /** @scrutinizer ignore-call */ 
214
        $record = $this->owner->getRecord();

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
214
        if (!$record) {
215
            return;
216
        }
217
218
        // We get the actions as defined on our record
219
        $CMSActions = $this->getCmsActionsFromRecord($record);
220
221
        // The default button group that contains the Save or Create action
222
        // @link https://docs.silverstripe.org/en/4/developer_guides/customising_the_admin_interface/how_tos/extend_cms_interface/#extending-the-cms-actions
223
        $MajorActions = $actions->fieldByName('MajorActions');
224
225
        // If it doesn't exist, push to default group
226
        if (!$MajorActions) {
227
            $MajorActions = $actions;
0 ignored issues
show
Unused Code introduced by
The assignment to $MajorActions is dead and can be removed.
Loading history...
228
        }
229
230
        // Push our actions that are otherwise ignored by SilverStripe
231
        if ($CMSActions) {
232
            foreach ($CMSActions as $action) {
233
                // Avoid duplicated actions (eg: when added by SilverStripe\Versioned\VersionedGridFieldItemRequest)
234
                if ($actions->fieldByName($action->getName())) {
235
                    continue;
236
                }
237
                $actions->push($action);
238
            }
239
        }
240
241
        // We create a Drop-Up menu afterwards because it may already exist in the $CMSActions
242
        // and we don't want to duplicate it
243
        $this->processDropUpMenu($actions);
244
245
        // Add extension hook
246
        $this->extend('onBeforeUpdateCMSActions', $actions, $record);
247
        $record->extend('onBeforeUpdateCMSActions', $actions);
248
249
        $ActionMenus = $actions->fieldByName('ActionMenus');
250
        // Re-insert ActionMenus to make sure they always follow the buttons
251
        if ($ActionMenus) {
252
            $actions->remove($ActionMenus);
253
            $actions->push($ActionMenus);
254
        }
255
256
        // We have a 4.4 setup, before that there was no RightGroup
257
        $RightGroup = $this->getRightGroupActions($actions);
258
259
        // Insert again to make sure our actions are properly placed after apply changes
260
        if ($RightGroup) {
0 ignored issues
show
introduced by
$RightGroup is of type SilverStripe\Forms\CompositeField, thus it always evaluated to true.
Loading history...
261
            $actions->remove($RightGroup);
262
            $actions->push($RightGroup);
263
        }
264
265
        $opts = [
266
            'save_close'     => self::config()->enable_save_close,
267
            'save_prev_next' => self::config()->enable_save_prev_next,
268
            'delete_right'   => self::config()->enable_delete_right,
269
        ];
270
        if ($record->hasMethod('getCMSActionsOptions')) {
271
            $opts = array_merge($opts, $record->getCMSActionsOptions());
272
        }
273
274
        if ($opts['save_close']) {
275
            $this->addSaveAndClose($actions, $record);
276
        }
277
278
        if ($opts['save_prev_next']) {
279
            $this->addSaveNextAndPrevious($actions, $record);
280
        }
281
282
        if ($opts['delete_right']) {
283
            $this->moveCancelAndDelete($actions, $record);
284
        }
285
286
        // Fix gridstate being lost when running custom actions
287
        if (method_exists($this->owner, 'getStateManager')) {
288
            $request = $this->owner->getRequest();
0 ignored issues
show
Bug introduced by
The method getRequest() does not exist on LeKoala\CmsActions\ActionsGridFieldItemRequest. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

288
            /** @scrutinizer ignore-call */ 
289
            $request = $this->owner->getRequest();
Loading history...
289
            $stateManager = $this->owner->getStateManager();
0 ignored issues
show
Bug introduced by
The method getStateManager() does not exist on LeKoala\CmsActions\ActionsGridFieldItemRequest. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

289
            /** @scrutinizer ignore-call */ 
290
            $stateManager = $this->owner->getStateManager();
Loading history...
290
            $gridField = $this->owner->getGridField();
0 ignored issues
show
Bug introduced by
The method getGridField() does not exist on LeKoala\CmsActions\ActionsGridFieldItemRequest. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

290
            /** @scrutinizer ignore-call */ 
291
            $gridField = $this->owner->getGridField();
Loading history...
291
            $state = $stateManager->getStateFromRequest($gridField, $request);
292
            $actions->push(new HiddenField($stateManager->getStateKey($gridField), null, $state));
293
        }
294
295
        // Add extension hook
296
        $this->extend('onAfterUpdateCMSActions', $actions, $record);
297
        $record->extend('onAfterUpdateCMSActions', $actions);
298
    }
299
300
    /**
301
     * Collect all Drop-Up actions into a menu.
302
     * @param FieldList $actions
303
     * @return void
304
     */
305
    protected function processDropUpMenu($actions)
306
    {
307
        // The Drop-up container may already exist
308
        /** @var ?Tab $dropUpContainer */
309
        $dropUpContainer = $actions->fieldByName('ActionMenus.MoreOptions');
310
        foreach ($actions as $action) {
311
            //@phpstan-ignore-next-line
312
            if ($action->hasMethod('getDropUp') && $action->getDropUp()) {
313
                if (!$dropUpContainer) {
314
                    $dropUpContainer = $this->createDropUpContainer($actions);
315
                }
316
                $action->getContainerFieldList()->removeByName($action->getName());
317
                $dropUpContainer->push($action);
0 ignored issues
show
Bug introduced by
It seems like $action can also be of type SilverStripe\View\ArrayData; however, parameter $field of SilverStripe\Forms\CompositeField::push() does only seem to accept SilverStripe\Forms\FormField, 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

317
                $dropUpContainer->push(/** @scrutinizer ignore-type */ $action);
Loading history...
318
            }
319
        }
320
    }
321
322
    /**
323
     * Prepares a Drop-Up menu
324
     * @param FieldList $actions
325
     * @return Tab
326
     */
327
    protected function createDropUpContainer($actions)
328
    {
329
        $rootTabSet = new TabSet('ActionMenus');
330
        $dropUpContainer = new Tab(
331
            'MoreOptions',
332
            _t(__CLASS__ . '.MoreOptions', 'More options', 'Expands a view for more buttons')
333
        );
334
        $dropUpContainer->addExtraClass('popover-actions-simulate');
335
        $rootTabSet->push($dropUpContainer);
336
        $rootTabSet->addExtraClass('ss-ui-action-tabset action-menus noborder');
337
338
        $actions->insertBefore('RightGroup', $rootTabSet);
339
340
        return $dropUpContainer;
341
    }
342
343
    /**
344
     * Check if a record can be edited/created/exists
345
     * @param ViewableData $record
346
     * @param bool $editOnly
347
     * @return bool
348
     */
349
    protected function checkCan($record, $editOnly = false)
350
    {
351
        // For ViewableData, we assume all methods should be implemented
352
        // @link https://docs.silverstripe.org/en/5/developer_guides/forms/using_gridfield_with_arbitrary_data/#custom-edit
353
        if (!method_exists($record, 'canEdit') || !method_exists($record, 'canCreate')) {
354
            return false;
355
        }
356
        //@phpstan-ignore-next-line
357
        if (!$record->ID && ($editOnly || !$record->canCreate())) {
358
            return false;
359
        }
360
        if (!$record->canEdit()) {
361
            return false;
362
        }
363
364
        return true;
365
    }
366
367
    /**
368
     * @param ViewableData $record
369
     * @return ?FieldList
370
     */
371
    protected function getCmsActionsFromRecord(ViewableData $record)
372
    {
373
        if ($record instanceof DataObject) {
374
            return $record->getCMSActions();
375
        }
376
        if (method_exists($record, 'getCMSActions')) {
377
            return $record->getCMSActions();
378
        }
379
        return null;
380
    }
381
382
    /**
383
     * @param FieldList $actions
384
     * @param ViewableData $record
385
     * @return void
386
     */
387
    public function moveCancelAndDelete(FieldList $actions, ViewableData $record)
388
    {
389
        // We have a 4.4 setup, before that there was no RightGroup
390
        $RightGroup = $actions->fieldByName('RightGroup');
391
392
        // Move delete at the end
393
        $deleteAction = $actions->fieldByName('action_doDelete');
394
        if ($deleteAction) {
395
            // Move at the end of the stack
396
            $actions->remove($deleteAction);
397
            $actions->push($deleteAction);
398
399
            if (!$RightGroup) {
400
                // Only necessary pre 4.4
401
                $deleteAction->addExtraClass('align-right');
402
            }
403
            // Set custom title
404
            if ($record->hasMethod('getDeleteButtonTitle')) {
405
                //@phpstan-ignore-next-line
406
                $deleteAction->setTitle($record->getDeleteButtonTitle());
407
            }
408
        }
409
        // Move cancel at the end
410
        $cancelButton = $actions->fieldByName('cancelbutton');
411
        if ($cancelButton) {
412
            // Move at the end of the stack
413
            $actions->remove($cancelButton);
414
            $actions->push($cancelButton);
415
            if (!$RightGroup) {
416
                // Only necessary pre 4.4
417
                $cancelButton->addExtraClass('align-right');
418
            }
419
            // Set custom titlte
420
            if ($record->hasMethod('getCancelButtonTitle')) {
421
                //@phpstan-ignore-next-line
422
                $cancelButton->setTitle($record->getCancelButtonTitle());
423
            }
424
        }
425
    }
426
427
    /**
428
     * @param ViewableData $record
429
     * @return bool
430
     */
431
    public function useCustomPrevNext(ViewableData $record): bool
432
    {
433
        if (self::config()->enable_custom_prevnext) {
434
            return $record->hasMethod('PrevRecord') && $record->hasMethod('NextRecord');
435
        }
436
        return false;
437
    }
438
439
    /**
440
     * @param ViewableData $record
441
     * @return int
442
     */
443
    public function getCustomPreviousRecordID(ViewableData $record)
444
    {
445
        // This will overwrite state provided record
446
        if ($this->useCustomPrevNext($record)) {
447
            //@phpstan-ignore-next-line
448
            return $record->PrevRecord()->ID ?? 0;
449
        }
450
        return $this->owner->getPreviousRecordID();
0 ignored issues
show
Bug introduced by
The method getPreviousRecordID() does not exist on LeKoala\CmsActions\ActionsGridFieldItemRequest. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

450
        return $this->owner->/** @scrutinizer ignore-call */ getPreviousRecordID();
Loading history...
451
    }
452
453
    /**
454
     * @param ViewableData $record
455
     * @return int
456
     */
457
    public function getCustomNextRecordID(ViewableData $record)
458
    {
459
        // This will overwrite state provided record
460
        if ($this->useCustomPrevNext($record)) {
461
            //@phpstan-ignore-next-line
462
            return $record->NextRecord()->ID ?? 0;
463
        }
464
        return $this->owner->getNextRecordID();
0 ignored issues
show
Bug introduced by
The method getNextRecordID() does not exist on LeKoala\CmsActions\ActionsGridFieldItemRequest. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

464
        return $this->owner->/** @scrutinizer ignore-call */ getNextRecordID();
Loading history...
465
    }
466
467
    /**
468
     * @param FieldList $actions
469
     * @return CompositeField|FieldList
470
     */
471
    protected function getMajorActions(FieldList $actions)
472
    {
473
        /** @var ?CompositeField $MajorActions */
474
        $MajorActions = $actions->fieldByName('MajorActions');
475
476
        // If it doesn't exist, push to default group
477
        if (!$MajorActions) {
0 ignored issues
show
introduced by
$MajorActions is of type SilverStripe\Forms\CompositeField, thus it always evaluated to true.
Loading history...
478
            $MajorActions = $actions;
479
        }
480
        return $MajorActions;
481
    }
482
483
    /**
484
     * @param FieldList $actions
485
     * @return CompositeField
486
     */
487
    protected function getRightGroupActions(FieldList $actions)
488
    {
489
        /** @var ?CompositeField $RightGroup */
490
        $RightGroup = $actions->fieldByName('RightGroup');
491
        return $RightGroup;
492
    }
493
494
    /**
495
     * @param FieldList $actions
496
     * @param ViewableData $record
497
     * @return void
498
     */
499
    public function addSaveNextAndPrevious(FieldList $actions, ViewableData $record)
500
    {
501
        if ($this->checkCan($record, true)) {
502
            return;
503
        }
504
505
        $MajorActions = $this->getMajorActions($actions);
506
507
        // @link https://github.com/silverstripe/silverstripe-framework/issues/10742
508
        $getPreviousRecordID = $this->getCustomPreviousRecordID($record);
509
        $getNextRecordID = $this->getCustomNextRecordID($record);
510
        $isCustom  = $this->useCustomPrevNext($record);
511
512
        // Coupling for HasPrevNextUtils
513
        if (Controller::has_curr()) {
0 ignored issues
show
Deprecated Code introduced by
The function SilverStripe\Control\Controller::has_curr() has been deprecated: 5.4.0 Will be removed without equivalent functionality to replace it in a future major release ( Ignorable by Annotation )

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

513
        if (/** @scrutinizer ignore-deprecated */ Controller::has_curr()) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
514
            $prevLink = $nextLink = null;
515
            if (!$isCustom && $this->owner instanceof GridFieldDetailForm_ItemRequest) {
516
                if ($getPreviousRecordID) {
517
                    $prevLink = $this->getPublicEditLinkForAdjacentRecord(-1);
518
                }
519
                if ($getNextRecordID) {
520
                    $nextLink = $this->getPublicEditLinkForAdjacentRecord(+1);
521
                }
522
            }
523
524
            /** @var HTTPRequest $request */
525
            $request = Controller::curr()->getRequest();
526
            $routeParams = $request->routeParams();
527
            $recordClass = get_class($record);
528
            $routeParams['cmsactions'][$recordClass]['PreviousRecordID'] = $getPreviousRecordID;
529
            $routeParams['cmsactions'][$recordClass]['NextRecordID'] = $getNextRecordID;
530
            $routeParams['cmsactions'][$recordClass]['PrevRecordLink'] = $prevLink;
531
            $routeParams['cmsactions'][$recordClass]['NextRecordLink'] = $nextLink;
532
            $request->setRouteParams($routeParams);
533
        }
534
535
        if ($getPreviousRecordID) {
536
            $doSaveAndPrev = new FormAction('doSaveAndPrev', _t('ActionsGridFieldItemRequest.SAVEANDPREVIOUS', 'Save and Previous'));
537
            $doSaveAndPrev->addExtraClass($this->getBtnClassForRecord($record));
538
            $doSaveAndPrev->addExtraClass('font-icon-angle-double-left btn-mobile-collapse');
539
            $doSaveAndPrev->setUseButtonTag(true);
540
            $MajorActions->push($doSaveAndPrev);
541
        }
542
        if ($getNextRecordID) {
543
            $doSaveAndNext = new FormAction('doSaveAndNext', _t('ActionsGridFieldItemRequest.SAVEANDNEXT', 'Save and Next'));
544
            $doSaveAndNext->addExtraClass($this->getBtnClassForRecord($record));
545
            $doSaveAndNext->addExtraClass('font-icon-angle-double-right btn-mobile-collapse');
546
            $doSaveAndNext->setUseButtonTag(true);
547
            $MajorActions->push($doSaveAndNext);
548
        }
549
    }
550
551
    public function getPublicEditLinkForAdjacentRecord(int $offset): ?string
552
    {
553
        $this->owner->getStateManager();
554
        $reflObject = new ReflectionObject($this->owner);
555
        $reflMethod = $reflObject->getMethod('getEditLinkForAdjacentRecord');
556
        $reflMethod->setAccessible(true);
557
558
        try {
559
            return $reflMethod->invoke($this->owner, $offset);
560
        } catch (Exception $e) {
561
            return null;
562
        }
563
    }
564
565
    /**
566
     * @param FieldList $actions
567
     * @param ViewableData $record
568
     * @return void
569
     */
570
    public function addSaveAndClose(FieldList $actions, ViewableData $record)
571
    {
572
        if (!$this->checkCan($record)) {
573
            return;
574
        }
575
576
        $MajorActions = $this->getMajorActions($actions);
577
578
        //@phpstan-ignore-next-line
579
        if ($record->ID) {
580
            $label = _t('ActionsGridFieldItemRequest.SAVEANDCLOSE', 'Save and Close');
581
        } else {
582
            $label = _t('ActionsGridFieldItemRequest.CREATEANDCLOSE', 'Create and Close');
583
        }
584
        $saveAndClose = new FormAction('doSaveAndClose', $label);
585
        $saveAndClose->addExtraClass($this->getBtnClassForRecord($record));
586
        $saveAndClose->setAttribute('data-text-alternate', $label);
587
588
        if ($record->ID) {
589
            $saveAndClose->setAttribute('data-btn-alternate-add', 'btn-primary');
590
            $saveAndClose->setAttribute('data-btn-alternate-remove', 'btn-outline-primary');
591
        }
592
        $saveAndClose->addExtraClass('font-icon-level-up btn-mobile-collapse');
593
        $saveAndClose->setUseButtonTag(true);
594
        $MajorActions->push($saveAndClose);
595
    }
596
597
    /**
598
     * New and existing records have different classes
599
     *
600
     * @param ViewableData $record
601
     * @return string
602
     */
603
    protected function getBtnClassForRecord(ViewableData $record)
604
    {
605
        //@phpstan-ignore-next-line
606
        if ($record->ID) {
607
            return 'btn-outline-primary';
608
        }
609
        return 'btn-primary';
610
    }
611
612
    /**
613
     * @param string $action
614
     * @param array<FormField>|FieldList $definedActions
615
     * @return FormField|null
616
     */
617
    protected static function findAction($action, $definedActions)
618
    {
619
        $result = null;
620
621
        foreach ($definedActions as $definedAction) {
622
            if (is_a($definedAction, CompositeField::class)) {
623
                $result = self::findAction($action, $definedAction->FieldList());
624
                if ($result) {
625
                    break;
626
                }
627
            }
628
629
            $definedActionName = $definedAction->getName();
630
631
            if ($definedAction->hasMethod('actionName')) {
632
                //@phpstan-ignore-next-line
633
                $definedActionName = $definedAction->actionName();
634
            }
635
            if ($definedActionName === $action) {
636
                $result = $definedAction;
637
                break;
638
            }
639
        }
640
641
        return $result;
642
    }
643
644
    /**
645
     * Forward a given action to a DataObject
646
     *
647
     * Action must be declared in getCMSActions to be called
648
     *
649
     * @param string $action
650
     * @param array<string,mixed> $data
651
     * @param Form $form
652
     * @return HTTPResponse|DBHTMLText|string
653
     * @throws HTTPResponse_Exception
654
     */
655
    protected function forwardActionToRecord($action, $data = [], $form = null)
656
    {
657
        $controller = $this->getToplevelController();
658
659
        // We have an item request or a controller that can provide a record
660
        $record = null;
661
        if ($this->owner->hasMethod('ItemEditForm')) {
662
            // It's a request handler. Don't check for a specific class as it may be subclassed
663
            //@phpstan-ignore-next-line
664
            $record = $this->owner->record;
0 ignored issues
show
Bug Best Practice introduced by
The property record does not exist on LeKoala\CmsActions\ActionsGridFieldItemRequest. Did you maybe forget to declare it?
Loading history...
665
        } elseif ($controller->hasMethod('save_siteconfig')) {
666
            // Check for any type of siteconfig controller
667
            $record = SiteConfig::current_site_config();
668
        } elseif (!empty($data['ClassName']) && !empty($data['ID'])) {
669
            $record = DataObject::get_by_id($data['ClassName'], $data['ID']);
670
        } elseif ($controller->hasMethod("getRecord")) {
671
            // LeftAndMain requires an id
672
            if ($controller instanceof LeftAndMain && !empty($data['ID'])) {
673
                $record = $controller->getRecord($data['ID']);
674
            } elseif ($controller instanceof ModelAdmin) {
675
                // Otherwise fallback to singleton
676
                $record = DataObject::singleton($controller->getModelClass());
677
            }
678
        }
679
680
        if (!$record) {
681
            throw new Exception("No record to handle the action $action on " . get_class($controller));
682
        }
683
        $CMSActions = $this->getCmsActionsFromRecord($record);
684
685
        // Check if the action is indeed available
686
        $clickedAction = null;
687
        if (!empty($CMSActions)) {
688
            $clickedAction = self::findAction($action, $CMSActions);
689
        }
690
        if (!$clickedAction) {
691
            $class = get_class($record);
692
            $availableActions = null;
693
            if ($CMSActions) {
694
                $availableActions = implode(',', $this->getAvailableActions($CMSActions));
695
            }
696
            if (!$availableActions) {
697
                $availableActions = "(no available actions, please check getCMSActions)";
698
            }
699
700
            return $this->owner->httpError(403, sprintf(
0 ignored issues
show
Bug introduced by
The method httpError() does not exist on LeKoala\CmsActions\ActionsGridFieldItemRequest. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

700
            return $this->owner->/** @scrutinizer ignore-call */ httpError(403, sprintf(
Loading history...
701
                'Action not available on %s. It must be one of : %s',
702
                $class,
703
                $availableActions
704
            ));
705
        }
706
707
        if ($clickedAction->isReadonly() || $clickedAction->isDisabled()) {
708
            return $this->owner->httpError(403, sprintf(
709
                'Action %s is disabled',
710
                $clickedAction->getName(),
711
            ));
712
        }
713
714
        $message = null;
715
        $error = false;
716
717
        // Check record BEFORE the action
718
        // It can be deleted by the action, and it will return to the list
719
        $isNewRecord = isset($record->ID) && $record->ID === 0;
720
721
        $actionTitle = $clickedAction->getName();
722
        if (method_exists($clickedAction, 'getTitle')) {
723
            $actionTitle = $clickedAction->getTitle();
724
        }
725
726
        $recordName = $record instanceof DataObject ? $record->i18n_singular_name() : _t('ActionsGridFieldItemRequest.record', 'record');
727
728
        try {
729
            $result = $record->$action($data, $form, $controller);
730
731
            // We have a response
732
            if ($result instanceof HTTPResponse) {
733
                return $result;
734
            }
735
736
            if ($result === false) {
737
                // Result returned an error (false)
738
                $error = true;
739
                $message = _t(
740
                    'ActionsGridFieldItemRequest.FAILED',
741
                    'Action {action} failed on {name}',
742
                    [
743
                        'action' => $actionTitle,
744
                        'name' => $recordName,
745
                    ]
746
                );
747
            } elseif (is_string($result)) {
748
                // Result is a message
749
                $message = $result;
750
            }
751
        } catch (Exception $ex) {
752
            $result = null;
753
            $error = true;
754
            $message = $ex->getMessage();
755
        }
756
757
        // Build default message
758
        if (!$message) {
759
            $message = _t(
760
                'ActionsGridFieldItemRequest.DONE',
761
                'Action {action} was done on {name}',
762
                [
763
                    'action' => $actionTitle,
764
                    'name' => $recordName,
765
                ]
766
            );
767
        }
768
        $status = 'good';
769
        if ($error) {
770
            $status = 'bad';
771
        }
772
773
        // Progressive actions return array with json data
774
        if (method_exists($clickedAction, 'getProgressive') && $clickedAction->getProgressive()) {
775
            $response = $controller->getResponse();
776
            $response->addHeader('Content-Type', 'application/json');
777
            if ($result) {
778
                $encodedResult = json_encode($result);
779
                if (!$encodedResult) {
780
                    $encodedResult = json_last_error_msg();
781
                }
782
                $response->setBody($encodedResult);
783
            }
784
785
            return $response;
786
        }
787
788
        // We don't have a form, simply return the result
789
        if (!$form) {
790
            if ($error) {
791
                return $this->owner->httpError(403, $message);
792
            }
793
794
            return $message;
795
        }
796
797
        if (Director::is_ajax()) {
798
            $controller->getResponse()->addHeader('X-Status', rawurlencode($message));
799
800
            if (method_exists($clickedAction, 'getShouldRefresh') && $clickedAction->getShouldRefresh()) {
801
                self::addXReload($controller);
802
            }
803
            // 4xx status makes a red box
804
            if ($error) {
805
                $controller->getResponse()->setStatusCode(400);
806
            }
807
        } else {
808
            // If the controller support sessionMessage, use it instead of form
809
            if ($controller->hasMethod('sessionMessage')) {
810
                //@phpstan-ignore-next-line
811
                $controller->sessionMessage($message, $status, ValidationResult::CAST_HTML);
812
            } else {
813
                $form->sessionMessage($message, $status, ValidationResult::CAST_HTML);
814
            }
815
        }
816
817
        // Custom redirect
818
        /** @var CustomAction $clickedAction */
819
        if (method_exists($clickedAction, 'getRedirectURL') && $clickedAction->getRedirectURL()) {
820
            // we probably need a full ui refresh
821
            self::addXReload($controller, $clickedAction->getRedirectURL());
822
            return $controller->redirect($clickedAction->getRedirectURL());
823
        }
824
825
        // Redirect after action
826
        return $this->redirectAfterAction($isNewRecord, $record);
827
    }
828
829
    /**
830
     * Requires a ControllerURL as well, see
831
     * https://github.com/silverstripe/silverstripe-admin/blob/a3aa41cea4c4df82050eef65ad5efcfae7bfde69/client/src/legacy/LeftAndMain.js#L773-L780
832
     *
833
     * @param Controller $controller
834
     * @param string|null $url
835
     * @return void
836
     */
837
    public static function addXReload(Controller $controller, ?string $url = null): void
838
    {
839
        if (!$url) {
840
            $url = $controller->getReferer();
841
        }
842
        // Triggers a full reload. Without this header, it will use the pjax response
843
        if ($url) {
844
            $controller->getResponse()->addHeader('X-ControllerURL', $url);
845
        }
846
        $controller->getResponse()->addHeader('X-Reload', "true");
847
    }
848
849
    /**
850
     * Handles custom links
851
     *
852
     * Use CustomLink with default behaviour to trigger this
853
     *
854
     * See:
855
     * DefaultLink::getModelLink
856
     * GridFieldCustomLink::getLink
857
     *
858
     * @param HTTPRequest $request
859
     * @return HTTPResponse|DBHTMLText|string
860
     * @throws Exception
861
     */
862
    public function doCustomLink(HTTPRequest $request)
863
    {
864
        $action = $request->getVar('CustomLink');
865
        return $this->forwardActionToRecord($action);
866
    }
867
868
    /**
869
     * Handles custom actions
870
     *
871
     * Use CustomAction class to trigger this
872
     *
873
     * Nested actions are submitted like this
874
     * [action_doCustomAction] => Array
875
     * (
876
     *   [doTestAction] => 1
877
     * )
878
     *
879
     * @param array<string,mixed> $data The form data
880
     * @param Form $form The form object
881
     * @return HTTPResponse|DBHTMLText|string
882
     * @throws Exception
883
     */
884
    public function doCustomAction($data, $form)
885
    {
886
        $action = key($data['action_doCustomAction']);
887
        return $this->forwardActionToRecord($action, $data, $form);
888
    }
889
890
    /**
891
     * Saves the form and goes back to list view
892
     *
893
     * @param array<string,mixed> $data The form data
894
     * @param Form $form The form object
895
     * @return HTTPResponse
896
     */
897
    public function doSaveAndClose($data, $form)
898
    {
899
        $this->owner->doSave($data, $form);
0 ignored issues
show
Bug introduced by
The method doSave() does not exist on LeKoala\CmsActions\ActionsGridFieldItemRequest. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

899
        $this->owner->/** @scrutinizer ignore-call */ 
900
                      doSave($data, $form);
Loading history...
900
        // Redirect after save
901
        $controller = $this->getToplevelController();
902
903
        $link = $this->getBackLink();
904
905
        // Doesn't seem to be needed anymore
906
        // $link = $this->addGridState($link, $data);
907
908
        $controller->getResponse()->addHeader("X-Pjax", "Content");
909
910
        return $controller->redirect($link);
911
    }
912
913
    /**
914
     * @param string $dir prev|next
915
     * @param array<string,mixed> $data The form data
916
     * @param Form|null $form
917
     * @return HTTPResponse
918
     */
919
    protected function doSaveAndAdjacent(string $dir, array $data, ?Form $form)
920
    {
921
        //@phpstan-ignore-next-line
922
        $record = $this->owner->record;
0 ignored issues
show
Bug Best Practice introduced by
The property record does not exist on LeKoala\CmsActions\ActionsGridFieldItemRequest. Did you maybe forget to declare it?
Loading history...
923
        $this->owner->doSave($data, $form);
924
        // Redirect after save
925
        $controller = $this->getToplevelController();
926
        $controller->getResponse()->addHeader("X-Pjax", "Content");
927
928
        if (!($record instanceof DataObject)) {
929
            throw new Exception("Works only with DataObject");
930
        }
931
932
        $class = get_class($record);
933
        if (!$class) {
934
            throw new Exception("Could not get class");
935
        }
936
937
        if (!in_array($dir, ['prev', 'next'])) {
938
            throw new Exception("Invalid dir $dir");
939
        }
940
941
        $method = match ($dir) {
942
            'prev' => 'getCustomPreviousRecordID',
943
            'next' => 'getCustomNextRecordID',
944
        };
945
946
        $offset = match ($dir) {
947
            'prev' => -1,
948
            'next' => +1,
949
        };
950
951
        $adjRecordID = $this->$method($record);
952
953
        /** @var ?DataObject $adj */
954
        $adj = $class::get()->byID($adjRecordID);
955
956
        $useCustom = $this->useCustomPrevNext($record);
957
        $link = $this->getPublicEditLinkForAdjacentRecord($offset);
958
        if (!$link || $useCustom) {
959
            $link = $this->owner->getEditLink($adjRecordID);
0 ignored issues
show
Bug introduced by
The method getEditLink() does not exist on LeKoala\CmsActions\ActionsGridFieldItemRequest. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

959
            /** @scrutinizer ignore-call */ 
960
            $link = $this->owner->getEditLink($adjRecordID);
Loading history...
960
            $link = $this->addGridState($link, $data);
0 ignored issues
show
Deprecated Code introduced by
The function LeKoala\CmsActions\Actio...Request::addGridState() has been deprecated. ( Ignorable by Annotation )

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

960
            $link = /** @scrutinizer ignore-deprecated */ $this->addGridState($link, $data);
Loading history...
961
        }
962
963
        // Link to a specific tab if set, see cms-actions.js
964
        if ($adj && !empty($data['_activetab'])) {
965
            $link .= sprintf('#%s', $data['_activetab']);
966
        }
967
968
        return $controller->redirect($link);
969
    }
970
971
    /**
972
     * Saves the form and goes back to the next item
973
     *
974
     * @param array<string,mixed> $data The form data
975
     * @param Form $form The form object
976
     * @return HTTPResponse
977
     */
978
    public function doSaveAndNext($data, $form)
979
    {
980
        return $this->doSaveAndAdjacent('next', $data, $form);
981
    }
982
983
    /**
984
     * Saves the form and goes to the previous item
985
     *
986
     * @param array<string,mixed> $data The form data
987
     * @param Form $form The form object
988
     * @return HTTPResponse
989
     */
990
    public function doSaveAndPrev($data, $form)
991
    {
992
        return $this->doSaveAndAdjacent('prev', $data, $form);
993
    }
994
995
    /**
996
     * Check if we can remove this safely
997
     * @deprecated
998
     * @param string $url
999
     * @param array<mixed> $data
1000
     * @return string
1001
     */
1002
    protected function addGridState($url, $data)
1003
    {
1004
        // This should not be necessary at all if the state is correctly passed along
1005
        $BackURL = $data['BackURL'] ?? null;
1006
        if ($BackURL) {
1007
            $query = parse_url($BackURL, PHP_URL_QUERY);
1008
            if ($query) {
1009
                $url = strtok($url, '?');
1010
                $url .= '?' . $query;
1011
            }
1012
        }
1013
        return $url;
1014
    }
1015
1016
    /**
1017
     * Gets the top level controller.
1018
     *
1019
     * @return Controller
1020
     * @todo  This had to be directly copied from {@link GridFieldDetailForm_ItemRequest}
1021
     * because it is a protected method and not visible to a decorator!
1022
     */
1023
    protected function getToplevelController()
1024
    {
1025
        if ($this->isLeftAndMain($this->owner)) {
0 ignored issues
show
Bug introduced by
It seems like $this->owner can also be of type LeKoala\CmsActions\ActionsGridFieldItemRequest and SilverStripe\Forms\GridF...dDetailForm_ItemRequest; however, parameter $controller of LeKoala\CmsActions\Actio...equest::isLeftAndMain() does only seem to accept SilverStripe\Control\Controller, 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

1025
        if ($this->isLeftAndMain(/** @scrutinizer ignore-type */ $this->owner)) {
Loading history...
1026
            return $this->owner;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->owner also could return the type LeKoala\CmsActions\Actio...dDetailForm_ItemRequest which is incompatible with the documented return type SilverStripe\Control\Controller.
Loading history...
1027
        }
1028
        if (!$this->owner->hasMethod("getController")) {
1029
            return Controller::curr();
1030
        }
1031
        $controller = $this->owner->getController();
0 ignored issues
show
Bug introduced by
The method getController() does not exist on LeKoala\CmsActions\ActionsGridFieldItemRequest. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

1031
        /** @scrutinizer ignore-call */ 
1032
        $controller = $this->owner->getController();
Loading history...
1032
        while ($controller instanceof GridFieldDetailForm_ItemRequest) {
1033
            $controller = $controller->getController();
1034
        }
1035
1036
        return $controller;
1037
    }
1038
1039
    /**
1040
     * @param Controller $controller
1041
     * @return boolean
1042
     */
1043
    protected function isLeftAndMain($controller)
1044
    {
1045
        return is_subclass_of($controller, LeftAndMain::class);
1046
    }
1047
1048
    /**
1049
     * Gets the back link
1050
     *
1051
     * @return string
1052
     */
1053
    public function getBackLink()
1054
    {
1055
        $backlink = '';
1056
        $toplevelController = $this->getToplevelController();
1057
        // Check for LeftAndMain and alike controllers with a Backlink or Breadcrumbs methods
1058
        if ($toplevelController->hasMethod('Backlink')) {
1059
            //@phpstan-ignore-next-line
1060
            $backlink = $toplevelController->Backlink();
1061
        } elseif ($this->owner->getController()->hasMethod('Breadcrumbs')) {
1062
            //@phpstan-ignore-next-line
1063
            $parents = $this->owner->getController()->Breadcrumbs(false)->items;
1064
            $backlink = array_pop($parents)->Link;
1065
        }
1066
        if (!$backlink) {
1067
            $backlink = $toplevelController->Link();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $backlink is correct as $toplevelController->Link() targeting SilverStripe\Control\RequestHandler::Link() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1068
        }
1069
1070
        return $backlink;
1071
    }
1072
1073
    /**
1074
     * Response object for this request after a successful save
1075
     *
1076
     * @param bool $isNewRecord True if this record was just created
1077
     * @param ViewableData $record
1078
     * @return HTTPResponse|DBHTMLText|string
1079
     * @todo  This had to be directly copied from {@link GridFieldDetailForm_ItemRequest}
1080
     * because it is a protected method and not visible to a decorator!
1081
     */
1082
    protected function redirectAfterAction($isNewRecord, $record = null)
1083
    {
1084
        $controller = $this->getToplevelController();
1085
1086
        if ($this->isLeftAndMain($controller)) {
1087
            // CMSMain => redirect to show
1088
            if ($this->owner->hasMethod("LinkPageEdit")) {
1089
                //@phpstan-ignore-next-line
1090
                return $controller->redirect($this->owner->LinkPageEdit($record->ID));
0 ignored issues
show
Bug introduced by
The method LinkPageEdit() does not exist on LeKoala\CmsActions\ActionsGridFieldItemRequest. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

1090
                return $controller->redirect($this->owner->/** @scrutinizer ignore-call */ LinkPageEdit($record->ID));
Loading history...
1091
            }
1092
        }
1093
1094
        if ($isNewRecord) {
1095
            return $controller->redirect($this->owner->Link());
0 ignored issues
show
Bug introduced by
The method Link() does not exist on LeKoala\CmsActions\ActionsGridFieldItemRequest. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

1095
            return $controller->redirect($this->owner->/** @scrutinizer ignore-call */ Link());
Loading history...
1096
        }
1097
        //@phpstan-ignore-next-line
1098
        if ($this->owner->gridField && $this->owner->gridField->getList()->byID($this->owner->record->ID)) {
0 ignored issues
show
Bug Best Practice introduced by
The property record does not exist on LeKoala\CmsActions\ActionsGridFieldItemRequest. Did you maybe forget to declare it?
Loading history...
Bug Best Practice introduced by
The property gridField does not exist on LeKoala\CmsActions\ActionsGridFieldItemRequest. Did you maybe forget to declare it?
Loading history...
1099
            // Return new view, as we can't do a "virtual redirect" via the CMS Ajax
1100
            // to the same URL (it assumes that its content is already current, and doesn't reload)
1101
            return $this->owner->edit($controller->getRequest());
0 ignored issues
show
Bug introduced by
The method edit() does not exist on LeKoala\CmsActions\ActionsGridFieldItemRequest. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

1101
            return $this->owner->/** @scrutinizer ignore-call */ edit($controller->getRequest());
Loading history...
1102
        }
1103
        // Changes to the record properties might've excluded the record from
1104
        // a filtered list, so return back to the main view if it can't be found
1105
        $noActionURL = $url = $controller->getRequest()->getURL();
1106
        if (!$url) {
1107
            $url = '';
1108
        }
1109
1110
        // The controller may not have these
1111
        if ($controller->hasMethod('getAction')) {
1112
            $action = $controller->getAction();
1113
            // Handle GridField detail form editing
1114
            if (strpos($url, 'ItemEditForm') !== false) {
1115
                $action = 'ItemEditForm';
1116
            }
1117
            if ($action) {
1118
                $noActionURL = $controller->removeAction($url, $action);
1119
            }
1120
        } else {
1121
            // Simple fallback (last index of)
1122
            $pos = strrpos($url, 'ItemEditForm');
1123
            if (is_int($pos)) {
0 ignored issues
show
introduced by
The condition is_int($pos) is always true.
Loading history...
1124
                $noActionURL = substr($url, 0, $pos);
1125
            }
1126
        }
1127
1128
        $controller->getRequest()->addHeader('X-Pjax', 'Content');
1129
        return $controller->redirect($noActionURL, 302);
1130
    }
1131
}
1132