Passed
Push — master ( 70f6ec...ce79a5 )
by Thomas
02:21
created

ActionsGridFieldItemRequest::addGridState()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 1
Metric Value
cc 3
eloc 7
c 2
b 1
f 1
nc 3
nop 2
dl 0
loc 12
rs 10
1
<?php
2
3
namespace LeKoala\CmsActions;
4
5
use Exception;
6
use PhpOffice\PhpSpreadsheet\Calculation\MathTrig\Exp;
0 ignored issues
show
Bug introduced by
The type PhpOffice\PhpSpreadsheet\Calculation\MathTrig\Exp 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...
7
use ReflectionMethod;
8
use SilverStripe\Forms\Tab;
9
use SilverStripe\Forms\Form;
10
use SilverStripe\Forms\TabSet;
11
use SilverStripe\ORM\DataObject;
12
use SilverStripe\Core\Extensible;
13
use SilverStripe\Forms\FieldList;
14
use SilverStripe\Forms\FormField;
15
use SilverStripe\Control\Director;
16
use SilverStripe\Forms\FormAction;
17
use SilverStripe\Admin\LeftAndMain;
18
use SilverStripe\Forms\HiddenField;
19
use SilverStripe\ORM\DataExtension;
20
use SilverStripe\Control\Controller;
21
use SilverStripe\Control\HTTPRequest;
22
use SilverStripe\Control\HTTPResponse;
23
use SilverStripe\Forms\CompositeField;
24
use SilverStripe\ORM\ValidationResult;
25
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...
26
use SilverStripe\Core\Config\Configurable;
27
use SilverStripe\ORM\FieldType\DBHTMLText;
28
use SilverStripe\Control\HTTPResponse_Exception;
29
use SilverStripe\Forms\GridField\GridFieldDetailForm_ItemRequest;
30
use ReflectionObject;
31
use SilverStripe\Admin\ModelAdmin;
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 LeftAndMain&GridFieldDetailForm_ItemRequest&ActionsGridFieldItemRequest $owner
45
 */
46
class ActionsGridFieldItemRequest extends DataExtension
0 ignored issues
show
Deprecated Code introduced by
The class SilverStripe\ORM\DataExtension has been deprecated: 5.3.0 Subclass SilverStripe\Core\Extension\Extension instead ( Ignorable by Annotation )

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

46
class ActionsGridFieldItemRequest extends /** @scrutinizer ignore-deprecated */ DataExtension
Loading history...
47
{
48
    use Configurable;
49
    use Extensible;
50
51
    /**
52
     * @config
53
     * @var boolean
54
     */
55
    private static $enable_save_prev_next = true;
56
57
    /**
58
     * @config
59
     * @var boolean
60
     */
61
    private static $enable_save_close = true;
62
63
    /**
64
     * @config
65
     * @var boolean
66
     */
67
    private static $enable_delete_right = true;
68
69
    /**
70
     * @config
71
     * @var boolean
72
     */
73
    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...
74
75
    /**
76
     * @var array<string> Allowed controller actions
77
     */
78
    private static $allowed_actions = [
0 ignored issues
show
introduced by
The private property $allowed_actions is not used, and could be removed.
Loading history...
79
        'doSaveAndClose',
80
        'doSaveAndNext',
81
        'doSaveAndPrev',
82
        'doCustomAction', // For CustomAction
83
        'doCustomLink', // For CustomLink
84
    ];
85
86
    /**
87
     * @param FieldList $actions
88
     * @return array<string>
89
     */
90
    protected function getAvailableActions($actions)
91
    {
92
        $list = [];
93
        foreach ($actions as $action) {
94
            if (is_a($action, CompositeField::class)) {
95
                $list = array_merge($list, $this->getAvailableActions($action->FieldList()));
96
            } else {
97
                $list[] = $action->getName();
98
            }
99
        }
100
        return $list;
101
    }
102
103
    /**
104
     * This module does not interact with the /schema/SearchForm endpoint
105
     * and therefore all requests for these urls don't need any special treatement
106
     *
107
     * @return bool
108
     */
109
    protected function isSearchFormRequest(): bool
110
    {
111
        if (!Controller::has_curr()) {
112
            return false;
113
        }
114
        $curr =  Controller::curr();
115
        if ($curr) {
0 ignored issues
show
introduced by
$curr is of type SilverStripe\Control\Controller, thus it always evaluated to true.
Loading history...
116
            return str_contains($curr->getRequest()->getURL(), '/schema/SearchForm');
117
        }
118
        return false;
119
    }
120
121
    /**
122
     * Called by CMSMain, typically in the CMS or in the SiteConfig admin
123
     * CMSMain already uses getCMSActions so we are good to go with anything defined there
124
     *
125
     * @param Form $form
126
     * @return void
127
     */
128
    public function updateEditForm(Form $form)
129
    {
130
        // Ignore search form requests
131
        if ($this->isSearchFormRequest()) {
132
            return;
133
        }
134
135
        $actions = $form->Actions();
136
137
        // We create a Drop-Up menu afterwards because it may already exist in the $CMSActions
138
        // and we don't want to duplicate it
139
        $this->processDropUpMenu($actions);
140
    }
141
142
    /**
143
     * @return FieldList|false
144
     */
145
    public function recordCmsUtils()
146
    {
147
        /** @var \SilverStripe\Versioned\VersionedGridFieldItemRequest|\SilverStripe\Admin\LeftAndMain $owner */
148
        $owner = $this->owner;
149
150
        // 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
151
        // maybe we could simply do:
152
        // $record = DataObject::singleton($controller->getModelClass());
153
        $reflectionMethod = new ReflectionMethod($owner, 'getRecord');
154
        $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

154
        $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

154
        $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...
155
        if ($record && $record->hasMethod('getCMSUtils')) {
156
            //@phpstan-ignore-next-line
157
            $utils = $record->getCMSUtils();
158
            $this->extend('onCMSUtils', $utils, $record);
159
            $record->extend('onCMSUtils', $utils);
160
            return $utils;
161
        }
162
        return false;
163
    }
164
165
    /**
166
     * @param Form $form
167
     * @return void
168
     */
169
    public function updateItemEditForm($form)
170
    {
171
        $record = $this->owner->getRecord();
0 ignored issues
show
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

171
        /** @scrutinizer ignore-call */ 
172
        $record = $this->owner->getRecord();
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

171
        /** @scrutinizer ignore-call */ 
172
        $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...
172
        if (!$record) {
173
            return;
174
        }
175
176
        // We get the actions as defined on our record
177
        /** @var FieldList $CMSActions */
178
        $CMSActions = $record->getCMSActions();
179
180
        $FormActions = $form->Actions();
181
182
        // Push our actions that are otherwise ignored by SilverStripe
183
        foreach ($CMSActions as $CMSAction) {
184
            $action = $FormActions->fieldByName($CMSAction->getName());
185
186
            if ($action) {
187
                // If it has been made readonly, revert
188
                if ($CMSAction->isReadonly() != $action->isReadonly()) {
189
                    $FormActions->replaceField($action->getName(), $action->setReadonly($CMSAction->isReadonly()));
190
                }
191
            }
192
        }
193
    }
194
195
    /**
196
     * Called by GridField_ItemRequest
197
     * We add our custom save&close, save&next and other tweaks
198
     * Actions can be made readonly after this extension point
199
     * @param FieldList $actions
200
     * @return void
201
     */
202
    public function updateFormActions($actions)
203
    {
204
        // Ignore search form requests
205
        if ($this->isSearchFormRequest()) {
206
            return;
207
        }
208
209
        $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

209
        /** @scrutinizer ignore-call */ 
210
        $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...
210
211
        // We get the actions as defined on our record
212
        /** @var FieldList $CMSActions */
213
        $CMSActions = $record->getCMSActions();
214
215
        // The default button group that contains the Save or Create action
216
        // @link https://docs.silverstripe.org/en/4/developer_guides/customising_the_admin_interface/how_tos/extend_cms_interface/#extending-the-cms-actions
217
        $MajorActions = $actions->fieldByName('MajorActions');
218
219
        // If it doesn't exist, push to default group
220
        if (!$MajorActions) {
221
            $MajorActions = $actions;
0 ignored issues
show
Unused Code introduced by
The assignment to $MajorActions is dead and can be removed.
Loading history...
222
        }
223
224
        // Push our actions that are otherwise ignored by SilverStripe
225
        foreach ($CMSActions as $action) {
226
            // Avoid duplicated actions (eg: when added by SilverStripe\Versioned\VersionedGridFieldItemRequest)
227
            if ($actions->fieldByName($action->getName())) {
228
                continue;
229
            }
230
            $actions->push($action);
231
        }
232
233
        // We create a Drop-Up menu afterwards because it may already exist in the $CMSActions
234
        // and we don't want to duplicate it
235
        $this->processDropUpMenu($actions);
236
237
        // Add extension hook
238
        $this->extend('onBeforeUpdateCMSActions', $actions, $record);
239
        $record->extend('onBeforeUpdateCMSActions', $actions);
240
241
        $ActionMenus = $actions->fieldByName('ActionMenus');
242
        // Re-insert ActionMenus to make sure they always follow the buttons
243
        if ($ActionMenus) {
244
            $actions->remove($ActionMenus);
245
            $actions->push($ActionMenus);
246
        }
247
248
        // We have a 4.4 setup, before that there was no RightGroup
249
        $RightGroup = $this->getRightGroupActions($actions);
250
251
        // Insert again to make sure our actions are properly placed after apply changes
252
        if ($RightGroup) {
0 ignored issues
show
introduced by
$RightGroup is of type SilverStripe\Forms\CompositeField, thus it always evaluated to true.
Loading history...
253
            $actions->remove($RightGroup);
254
            $actions->push($RightGroup);
255
        }
256
257
        $opts = [
258
            'save_close'     => self::config()->enable_save_close,
259
            'save_prev_next' => self::config()->enable_save_prev_next,
260
            'delete_right'   => self::config()->enable_delete_right,
261
        ];
262
        if ($record->hasMethod('getCMSActionsOptions')) {
263
            $opts = array_merge($opts, $record->getCMSActionsOptions());
264
        }
265
266
        if ($opts['save_close']) {
267
            $this->addSaveAndClose($actions, $record);
0 ignored issues
show
Bug introduced by
It seems like $record can also be of type integer and null; however, parameter $record of LeKoala\CmsActions\Actio...uest::addSaveAndClose() does only seem to accept SilverStripe\ORM\DataObject, 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

267
            $this->addSaveAndClose($actions, /** @scrutinizer ignore-type */ $record);
Loading history...
268
        }
269
270
        if ($opts['save_prev_next']) {
271
            $this->addSaveNextAndPrevious($actions, $record);
0 ignored issues
show
Bug introduced by
It seems like $record can also be of type integer and null; however, parameter $record of LeKoala\CmsActions\Actio...ddSaveNextAndPrevious() does only seem to accept SilverStripe\ORM\DataObject, 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

271
            $this->addSaveNextAndPrevious($actions, /** @scrutinizer ignore-type */ $record);
Loading history...
272
        }
273
274
        if ($opts['delete_right']) {
275
            $this->moveCancelAndDelete($actions, $record);
0 ignored issues
show
Bug introduced by
It seems like $record can also be of type integer and null; however, parameter $record of LeKoala\CmsActions\Actio...::moveCancelAndDelete() does only seem to accept SilverStripe\ORM\DataObject, 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

275
            $this->moveCancelAndDelete($actions, /** @scrutinizer ignore-type */ $record);
Loading history...
276
        }
277
278
        // Fix gridstate being lost when running custom actions
279
        if (method_exists($this->owner, 'getStateManager')) {
280
            $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

280
            /** @scrutinizer ignore-call */ 
281
            $request = $this->owner->getRequest();
Loading history...
281
            $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

281
            /** @scrutinizer ignore-call */ 
282
            $stateManager = $this->owner->getStateManager();
Loading history...
282
            $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

282
            /** @scrutinizer ignore-call */ 
283
            $gridField = $this->owner->getGridField();
Loading history...
283
            $state = $stateManager->getStateFromRequest($gridField, $request);
284
            $actions->push(new HiddenField($stateManager->getStateKey($gridField), null, $state));
285
        }
286
287
        // Add extension hook
288
        $this->extend('onAfterUpdateCMSActions', $actions, $record);
289
        $record->extend('onAfterUpdateCMSActions', $actions);
290
    }
291
292
    /**
293
     * Collect all Drop-Up actions into a menu.
294
     * @param FieldList $actions
295
     * @return void
296
     */
297
    protected function processDropUpMenu($actions)
298
    {
299
        // The Drop-up container may already exist
300
        /** @var ?Tab $dropUpContainer */
301
        $dropUpContainer = $actions->fieldByName('ActionMenus.MoreOptions');
302
        foreach ($actions as $action) {
303
            //@phpstan-ignore-next-line
304
            if ($action->hasMethod('getDropUp') && $action->getDropUp()) {
305
                if (!$dropUpContainer) {
306
                    $dropUpContainer = $this->createDropUpContainer($actions);
307
                }
308
                $action->getContainerFieldList()->removeByName($action->getName());
309
                $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

309
                $dropUpContainer->push(/** @scrutinizer ignore-type */ $action);
Loading history...
310
            }
311
        }
312
    }
313
314
    /**
315
     * Prepares a Drop-Up menu
316
     * @param FieldList $actions
317
     * @return Tab
318
     */
319
    protected function createDropUpContainer($actions)
320
    {
321
        $rootTabSet = new TabSet('ActionMenus');
322
        $dropUpContainer = new Tab(
323
            'MoreOptions',
324
            _t(__CLASS__ . '.MoreOptions', 'More options', 'Expands a view for more buttons')
325
        );
326
        $dropUpContainer->addExtraClass('popover-actions-simulate');
327
        $rootTabSet->push($dropUpContainer);
328
        $rootTabSet->addExtraClass('ss-ui-action-tabset action-menus noborder');
329
330
        $actions->insertBefore('RightGroup', $rootTabSet);
331
332
        return $dropUpContainer;
333
    }
334
335
    /**
336
     * Check if a record can be edited/created/exists
337
     * @param DataObject $record
338
     * @return bool
339
     */
340
    protected function checkCan($record)
341
    {
342
        if (!$record->canEdit() || (!$record->ID && !$record->canCreate())) {
343
            return false;
344
        }
345
346
        return true;
347
    }
348
349
    /**
350
     * @param FieldList $actions
351
     * @param DataObject $record
352
     * @return void
353
     */
354
    public function moveCancelAndDelete(FieldList $actions, DataObject $record)
355
    {
356
        // We have a 4.4 setup, before that there was no RightGroup
357
        $RightGroup = $actions->fieldByName('RightGroup');
358
359
        // Move delete at the end
360
        $deleteAction = $actions->fieldByName('action_doDelete');
361
        if ($deleteAction) {
362
            // Move at the end of the stack
363
            $actions->remove($deleteAction);
364
            $actions->push($deleteAction);
365
366
            if (!$RightGroup) {
367
                // Only necessary pre 4.4
368
                $deleteAction->addExtraClass('align-right');
369
            }
370
            // Set custom title
371
            if ($record->hasMethod('getDeleteButtonTitle')) {
372
                //@phpstan-ignore-next-line
373
                $deleteAction->setTitle($record->getDeleteButtonTitle());
374
            }
375
        }
376
        // Move cancel at the end
377
        $cancelButton = $actions->fieldByName('cancelbutton');
378
        if ($cancelButton) {
379
            // Move at the end of the stack
380
            $actions->remove($cancelButton);
381
            $actions->push($cancelButton);
382
            if (!$RightGroup) {
383
                // Only necessary pre 4.4
384
                $cancelButton->addExtraClass('align-right');
385
            }
386
            // Set custom titlte
387
            if ($record->hasMethod('getCancelButtonTitle')) {
388
                //@phpstan-ignore-next-line
389
                $cancelButton->setTitle($record->getCancelButtonTitle());
390
            }
391
        }
392
    }
393
394
    /**
395
     * @param DataObject $record
396
     * @return bool
397
     */
398
    public function useCustomPrevNext(DataObject $record): bool
399
    {
400
        if (self::config()->enable_custom_prevnext) {
401
            return $record->hasMethod('PrevRecord') && $record->hasMethod('NextRecord');
402
        }
403
        return false;
404
    }
405
406
    /**
407
     * @param DataObject $record
408
     * @return int
409
     */
410
    public function getCustomPreviousRecordID(DataObject $record)
411
    {
412
        // This will overwrite state provided record
413
        if ($this->useCustomPrevNext($record)) {
414
            //@phpstan-ignore-next-line
415
            return $record->PrevRecord()->ID ?? 0;
416
        }
417
        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

417
        return $this->owner->/** @scrutinizer ignore-call */ getPreviousRecordID();
Loading history...
418
    }
419
420
    /**
421
     * @param DataObject $record
422
     * @return int
423
     */
424
    public function getCustomNextRecordID(DataObject $record)
425
    {
426
427
        // This will overwrite state provided record
428
        if ($this->useCustomPrevNext($record)) {
429
            //@phpstan-ignore-next-line
430
            return $record->NextRecord()->ID ?? 0;
431
        }
432
        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

432
        return $this->owner->/** @scrutinizer ignore-call */ getNextRecordID();
Loading history...
433
    }
434
435
    /**
436
     * @param FieldList $actions
437
     * @return CompositeField|FieldList
438
     */
439
    protected function getMajorActions(FieldList $actions)
440
    {
441
        /** @var ?CompositeField $MajorActions */
442
        $MajorActions = $actions->fieldByName('MajorActions');
443
444
        // If it doesn't exist, push to default group
445
        if (!$MajorActions) {
0 ignored issues
show
introduced by
$MajorActions is of type SilverStripe\Forms\CompositeField, thus it always evaluated to true.
Loading history...
446
            $MajorActions = $actions;
447
        }
448
        return $MajorActions;
449
    }
450
451
    /**
452
     * @param FieldList $actions
453
     * @return CompositeField
454
     */
455
    protected function getRightGroupActions(FieldList $actions)
456
    {
457
        /** @var ?CompositeField $RightGroup */
458
        $RightGroup = $actions->fieldByName('RightGroup');
459
        return $RightGroup;
460
    }
461
462
    /**
463
     * @param FieldList $actions
464
     * @param DataObject $record
465
     * @return void
466
     */
467
    public function addSaveNextAndPrevious(FieldList $actions, DataObject $record)
468
    {
469
        if (!$record->canEdit() || !$record->ID) {
470
            return;
471
        }
472
473
        $MajorActions = $this->getMajorActions($actions);
474
475
        // @link https://github.com/silverstripe/silverstripe-framework/issues/10742
476
        $getPreviousRecordID = $this->getCustomPreviousRecordID($record);
477
        $getNextRecordID = $this->getCustomNextRecordID($record);
478
        $isCustom  = $this->useCustomPrevNext($record);
479
480
        // Coupling for HasPrevNextUtils
481
        if (Controller::has_curr()) {
482
            $prevLink = $nextLink = null;
483
            if (!$isCustom && $this->owner instanceof GridFieldDetailForm_ItemRequest) {
484
                if ($getPreviousRecordID) {
485
                    $prevLink = $this->getPublicEditLinkForAdjacentRecord(-1);
486
                }
487
                if ($getNextRecordID) {
488
                    $nextLink = $this->getPublicEditLinkForAdjacentRecord(+1);
489
                }
490
            }
491
492
            /** @var HTTPRequest $request */
493
            $request = Controller::curr()->getRequest();
494
            $routeParams = $request->routeParams();
495
            $recordClass = get_class($record);
496
            $routeParams['cmsactions'][$recordClass]['PreviousRecordID'] = $getPreviousRecordID;
497
            $routeParams['cmsactions'][$recordClass]['NextRecordID'] = $getNextRecordID;
498
            $routeParams['cmsactions'][$recordClass]['PrevRecordLink'] = $prevLink;
499
            $routeParams['cmsactions'][$recordClass]['NextRecordLink'] = $nextLink;
500
            $request->setRouteParams($routeParams);
501
        }
502
503
        if ($getPreviousRecordID) {
504
            $doSaveAndPrev = new FormAction('doSaveAndPrev', _t('ActionsGridFieldItemRequest.SAVEANDPREVIOUS', 'Save and Previous'));
505
            $doSaveAndPrev->addExtraClass($this->getBtnClassForRecord($record));
506
            $doSaveAndPrev->addExtraClass('font-icon-angle-double-left btn-mobile-collapse');
507
            $doSaveAndPrev->setUseButtonTag(true);
508
            $MajorActions->push($doSaveAndPrev);
509
        }
510
        if ($getNextRecordID) {
511
            $doSaveAndNext = new FormAction('doSaveAndNext', _t('ActionsGridFieldItemRequest.SAVEANDNEXT', 'Save and Next'));
512
            $doSaveAndNext->addExtraClass($this->getBtnClassForRecord($record));
513
            $doSaveAndNext->addExtraClass('font-icon-angle-double-right btn-mobile-collapse');
514
            $doSaveAndNext->setUseButtonTag(true);
515
            $MajorActions->push($doSaveAndNext);
516
        }
517
    }
518
519
    public function getPublicEditLinkForAdjacentRecord(int $offset): ?string
520
    {
521
        $this->owner->getStateManager();
522
        $reflObject = new ReflectionObject($this->owner);
523
        $reflMethod = $reflObject->getMethod('getEditLinkForAdjacentRecord');
524
        $reflMethod->setAccessible(true);
525
526
        try {
527
            return $reflMethod->invoke($this->owner, $offset);
528
        } catch (Exception $e) {
529
            return null;
530
        }
531
    }
532
533
    /**
534
     * @param FieldList $actions
535
     * @param DataObject $record
536
     * @return void
537
     */
538
    public function addSaveAndClose(FieldList $actions, DataObject $record)
539
    {
540
        if (!$this->checkCan($record)) {
541
            return;
542
        }
543
544
        $MajorActions = $this->getMajorActions($actions);
545
546
        if ($record->ID) {
547
            $label = _t('ActionsGridFieldItemRequest.SAVEANDCLOSE', 'Save and Close');
548
        } else {
549
            $label = _t('ActionsGridFieldItemRequest.CREATEANDCLOSE', 'Create and Close');
550
        }
551
        $saveAndClose = new FormAction('doSaveAndClose', $label);
552
        $saveAndClose->addExtraClass($this->getBtnClassForRecord($record));
553
        $saveAndClose->setAttribute('data-text-alternate', $label);
554
        if ($record->ID) {
555
            $saveAndClose->setAttribute('data-btn-alternate-add', 'btn-primary');
556
            $saveAndClose->setAttribute('data-btn-alternate-remove', 'btn-outline-primary');
557
        }
558
        $saveAndClose->addExtraClass('font-icon-level-up btn-mobile-collapse');
559
        $saveAndClose->setUseButtonTag(true);
560
        $MajorActions->push($saveAndClose);
561
    }
562
563
    /**
564
     * New and existing records have different classes
565
     *
566
     * @param DataObject $record
567
     * @return string
568
     */
569
    protected function getBtnClassForRecord(DataObject $record)
570
    {
571
        if ($record->ID) {
572
            return 'btn-outline-primary';
573
        }
574
        return 'btn-primary';
575
    }
576
577
    /**
578
     * @param string $action
579
     * @param array<FormField>|FieldList $definedActions
580
     * @return FormField|null
581
     */
582
    protected static function findAction($action, $definedActions)
583
    {
584
        $result = null;
585
586
        foreach ($definedActions as $definedAction) {
587
            if (is_a($definedAction, CompositeField::class)) {
588
                $result = self::findAction($action, $definedAction->FieldList());
589
                if ($result) {
590
                    break;
591
                }
592
            }
593
594
            $definedActionName = $definedAction->getName();
595
596
            if ($definedAction->hasMethod('actionName')) {
597
                //@phpstan-ignore-next-line
598
                $definedActionName = $definedAction->actionName();
599
            }
600
            if ($definedActionName === $action) {
601
                $result = $definedAction;
602
                break;
603
            }
604
        }
605
606
        return $result;
607
    }
608
609
    /**
610
     * Forward a given action to a DataObject
611
     *
612
     * Action must be declared in getCMSActions to be called
613
     *
614
     * @param string $action
615
     * @param array<string,mixed> $data
616
     * @param Form $form
617
     * @return HTTPResponse|DBHTMLText|string
618
     * @throws HTTPResponse_Exception
619
     */
620
    protected function forwardActionToRecord($action, $data = [], $form = null)
621
    {
622
        $controller = $this->getToplevelController();
623
624
        // We have an item request or a controller that can provide a record
625
        $record = null;
626
        if ($this->owner->hasMethod('ItemEditForm')) {
627
            // It's a request handler. Don't check for a specific class as it may be subclassed
628
            $record = $this->owner->record;
629
        } elseif ($controller->hasMethod('save_siteconfig')) {
630
            // Check for any type of siteconfig controller
631
            $record = SiteConfig::current_site_config();
632
        } elseif (!empty($data['ClassName']) && !empty($data['ID'])) {
633
            $record = DataObject::get_by_id($data['ClassName'], $data['ID']);
634
        } elseif ($controller->hasMethod("getRecord")) {
635
            // LeftAndMain requires an id
636
            if ($controller instanceof LeftAndMain && !empty($data['ID'])) {
637
                $record = $controller->getRecord($data['ID']);
638
            } elseif ($controller instanceof ModelAdmin) {
639
                // Otherwise fallback to singleton
640
                $record = DataObject::singleton($controller->getModelClass());
641
            }
642
        }
643
644
        if (!$record) {
645
            throw new Exception("No record to handle the action $action on " . get_class($controller));
646
        }
647
        $definedActions = $record->getCMSActions();
648
        // Check if the action is indeed available
649
        $clickedAction = null;
650
        if (!empty($definedActions)) {
651
            $clickedAction = self::findAction($action, $definedActions);
652
        }
653
        if (!$clickedAction) {
654
            $class = get_class($record);
655
            $availableActions = implode(',', $this->getAvailableActions($definedActions));
656
            if (!$availableActions) {
657
                $availableActions = "(no available actions, please check getCMSActions)";
658
            }
659
660
            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

660
            return $this->owner->/** @scrutinizer ignore-call */ httpError(403, sprintf(
Loading history...
661
                'Action not available on %s. It must be one of : %s',
662
                $class,
663
                $availableActions
664
            ));
665
        }
666
667
        if ($clickedAction->isReadonly() || $clickedAction->isDisabled()) {
668
            return $this->owner->httpError(403, sprintf(
669
                'Action %s is disabled',
670
                $clickedAction->getName(),
671
            ));
672
        }
673
674
        $message = null;
675
        $error = false;
676
677
        // Check record BEFORE the action
678
        // It can be deleted by the action, and it will return to the list
679
        $isNewRecord = $record->ID === 0;
680
681
        $actionTitle = $clickedAction->getName();
682
        if (method_exists($clickedAction, 'getTitle')) {
683
            $actionTitle = $clickedAction->getTitle();
684
        }
685
686
        try {
687
            $result = $record->$action($data, $form, $controller);
688
689
            // We have a response
690
            if ($result instanceof HTTPResponse) {
691
                return $result;
692
            }
693
694
            if ($result === false) {
695
                // Result returned an error (false)
696
                $error = true;
697
                $message = _t(
698
                    'ActionsGridFieldItemRequest.FAILED',
699
                    'Action {action} failed on {name}',
700
                    [
701
                        'action' => $actionTitle,
702
                        'name' => $record->i18n_singular_name(),
703
                    ]
704
                );
705
            } elseif (is_string($result)) {
706
                // Result is a message
707
                $message = $result;
708
            }
709
        } catch (Exception $ex) {
710
            $result = null;
711
            $error = true;
712
            $message = $ex->getMessage();
713
        }
714
715
        // Build default message
716
        if (!$message) {
717
            $message = _t(
718
                'ActionsGridFieldItemRequest.DONE',
719
                'Action {action} was done on {name}',
720
                [
721
                    'action' => $actionTitle,
722
                    'name' => $record->i18n_singular_name(),
723
                ]
724
            );
725
        }
726
        $status = 'good';
727
        if ($error) {
728
            $status = 'bad';
729
        }
730
731
        // Progressive actions return array with json data
732
        //@phpstan-ignore-next-line
733
        if (method_exists($clickedAction, 'getProgressive') && $clickedAction->getProgressive()) {
734
            $response = $controller->getResponse();
735
            $response->addHeader('Content-Type', 'application/json');
736
            if ($result) {
737
                $encodedResult = json_encode($result);
738
                if (!$encodedResult) {
739
                    $encodedResult = json_last_error_msg();
740
                }
741
                $response->setBody($encodedResult);
742
            }
743
744
            return $response;
745
        }
746
747
        // We don't have a form, simply return the result
748
        if (!$form) {
749
            if ($error) {
750
                return $this->owner->httpError(403, $message);
751
            }
752
753
            return $message;
754
        }
755
756
        if (Director::is_ajax()) {
757
            $controller->getResponse()->addHeader('X-Status', rawurlencode($message));
758
759
            //@phpstan-ignore-next-line
760
            if (method_exists($clickedAction, 'getShouldRefresh') && $clickedAction->getShouldRefresh()) {
761
                self::addXReload($controller);
762
            }
763
            // 4xx status makes a red box
764
            if ($error) {
765
                $controller->getResponse()->setStatusCode(400);
766
            }
767
        } else {
768
            // If the controller support sessionMessage, use it instead of form
769
            if ($controller->hasMethod('sessionMessage')) {
770
                //@phpstan-ignore-next-line
771
                $controller->sessionMessage($message, $status, ValidationResult::CAST_HTML);
772
            } else {
773
                $form->sessionMessage($message, $status, ValidationResult::CAST_HTML);
774
            }
775
        }
776
777
        // Custom redirect
778
        //@phpstan-ignore-next-line
779
        if (method_exists($clickedAction, 'getRedirectURL') && $clickedAction->getRedirectURL()) {
780
            // we probably need a full ui refresh
781
            self::addXReload($clickedAction->getRedirectURL());
782
            //@phpstan-ignore-next-line
783
            return $controller->redirect($clickedAction->getRedirectURL());
784
        }
785
786
        // Redirect after action
787
        return $this->redirectAfterAction($isNewRecord, $record);
788
    }
789
790
    /**
791
     * Requires a ControllerURL as well, see
792
     * https://github.com/silverstripe/silverstripe-admin/blob/a3aa41cea4c4df82050eef65ad5efcfae7bfde69/client/src/legacy/LeftAndMain.js#L773-L780
793
     *
794
     * @param Controller $controller
795
     * @param string|null $url
796
     * @return void
797
     */
798
    public static function addXReload(Controller $controller, ?string $url = null): void
799
    {
800
        if (!$url) {
801
            $url = $controller->getReferer();
802
        }
803
        $controller->getResponse()->addHeader('X-ControllerURL', $url);
804
        $controller->getResponse()->addHeader('X-Reload', "true");
805
    }
806
807
    /**
808
     * Handles custom links
809
     *
810
     * Use CustomLink with default behaviour to trigger this
811
     *
812
     * See:
813
     * DefaultLink::getModelLink
814
     * GridFieldCustomLink::getLink
815
     *
816
     * @param HTTPRequest $request
817
     * @return HTTPResponse|DBHTMLText|string
818
     * @throws Exception
819
     */
820
    public function doCustomLink(HTTPRequest $request)
821
    {
822
        $action = $request->getVar('CustomLink');
823
        return $this->forwardActionToRecord($action);
824
    }
825
826
    /**
827
     * Handles custom actions
828
     *
829
     * Use CustomAction class to trigger this
830
     *
831
     * Nested actions are submitted like this
832
     * [action_doCustomAction] => Array
833
     * (
834
     *   [doTestAction] => 1
835
     * )
836
     *
837
     * @param array<string,mixed> $data The form data
838
     * @param Form $form The form object
839
     * @return HTTPResponse|DBHTMLText|string
840
     * @throws Exception
841
     */
842
    public function doCustomAction($data, $form)
843
    {
844
        $action = key($data['action_doCustomAction']);
845
        return $this->forwardActionToRecord($action, $data, $form);
846
    }
847
848
    /**
849
     * Saves the form and goes back to list view
850
     *
851
     * @param array<string,mixed> $data The form data
852
     * @param Form $form The form object
853
     * @return HTTPResponse
854
     */
855
    public function doSaveAndClose($data, $form)
856
    {
857
        $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

857
        $this->owner->/** @scrutinizer ignore-call */ 
858
                      doSave($data, $form);
Loading history...
858
        // Redirect after save
859
        $controller = $this->getToplevelController();
860
861
        $link = $this->getBackLink();
862
863
        // Doesn't seem to be needed anymore
864
        // $link = $this->addGridState($link, $data);
865
866
        $controller->getResponse()->addHeader("X-Pjax", "Content");
867
868
        return $controller->redirect($link);
869
    }
870
871
    /**
872
     * @param string $dir prev|next
873
     * @param array<string,mixed> $data The form data
874
     * @param Form|null $form
875
     * @return HTTPResponse
876
     */
877
    protected function doSaveAndAdjacent(string $dir, array $data, ?Form $form)
878
    {
879
        $record = $this->owner->record;
880
        $this->owner->doSave($data, $form);
881
        // Redirect after save
882
        $controller = $this->getToplevelController();
883
        $controller->getResponse()->addHeader("X-Pjax", "Content");
884
885
        $class = get_class($record);
0 ignored issues
show
Bug introduced by
It seems like $record can also be of type null; however, parameter $object of get_class() does only seem to accept object, 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

885
        $class = get_class(/** @scrutinizer ignore-type */ $record);
Loading history...
886
        if (!$class) {
887
            throw new Exception("Could not get class");
888
        }
889
890
        if (!in_array($dir, ['prev', 'next'])) {
891
            throw new Exception("Invalid dir $dir");
892
        }
893
894
        $method = match ($dir) {
895
            'prev' => 'getCustomPreviousRecordID',
896
            'next' => 'getCustomNextRecordID',
897
        };
898
899
        $offset = match ($dir) {
900
            'prev' => -1,
901
            'next' => +1,
902
        };
903
904
        $adjRecordID = $this->$method($record);
905
906
        /** @var ?DataObject $adj */
907
        $adj = $class::get()->byID($adjRecordID);
908
909
        $useCustom = $this->useCustomPrevNext($record);
0 ignored issues
show
Bug introduced by
It seems like $record can also be of type null; however, parameter $record of LeKoala\CmsActions\Actio...st::useCustomPrevNext() does only seem to accept SilverStripe\ORM\DataObject, 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

909
        $useCustom = $this->useCustomPrevNext(/** @scrutinizer ignore-type */ $record);
Loading history...
910
        $link = $this->getPublicEditLinkForAdjacentRecord($offset);
911
        if (!$link || $useCustom) {
912
            $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

912
            /** @scrutinizer ignore-call */ 
913
            $link = $this->owner->getEditLink($adjRecordID);
Loading history...
913
            $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

913
            $link = /** @scrutinizer ignore-deprecated */ $this->addGridState($link, $data);
Loading history...
914
        }
915
916
        // Link to a specific tab if set, see cms-actions.js
917
        if ($adj && !empty($data['_activetab'])) {
918
            $link .= sprintf('#%s', $data['_activetab']);
919
        }
920
921
        return $controller->redirect($link);
922
    }
923
924
    /**
925
     * Saves the form and goes back to the next item
926
     *
927
     * @param array<string,mixed> $data The form data
928
     * @param Form $form The form object
929
     * @return HTTPResponse
930
     */
931
    public function doSaveAndNext($data, $form)
932
    {
933
        return $this->doSaveAndAdjacent('next', $data, $form);
934
    }
935
936
    /**
937
     * Saves the form and goes to the previous item
938
     *
939
     * @param array<string,mixed> $data The form data
940
     * @param Form $form The form object
941
     * @return HTTPResponse
942
     */
943
    public function doSaveAndPrev($data, $form)
944
    {
945
        return $this->doSaveAndAdjacent('prev', $data, $form);
946
    }
947
948
    /**
949
     * Check if we can remove this safely
950
     * @deprecated
951
     * @param string $url
952
     * @param array<mixed> $data
953
     * @return string
954
     */
955
    protected function addGridState($url, $data)
956
    {
957
        // This should not be necessary at all if the state is correctly passed along
958
        $BackURL = $data['BackURL'] ?? null;
959
        if ($BackURL) {
960
            $query = parse_url($BackURL, PHP_URL_QUERY);
961
            if ($query) {
962
                $url = strtok($url, '?');
963
                $url .= '?' . $query;
964
            }
965
        }
966
        return $url;
967
    }
968
969
    /**
970
     * Gets the top level controller.
971
     *
972
     * @return Controller
973
     * @todo  This had to be directly copied from {@link GridFieldDetailForm_ItemRequest}
974
     * because it is a protected method and not visible to a decorator!
975
     */
976
    protected function getToplevelController()
977
    {
978
        if ($this->isLeftAndMain($this->owner)) {
979
            return $this->owner;
980
        }
981
        if (!$this->owner->hasMethod("getController")) {
982
            return Controller::curr();
983
        }
984
        $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

984
        /** @scrutinizer ignore-call */ 
985
        $controller = $this->owner->getController();
Loading history...
985
        while ($controller instanceof GridFieldDetailForm_ItemRequest) {
986
            $controller = $controller->getController();
987
        }
988
989
        return $controller;
990
    }
991
992
    /**
993
     * @param Controller $controller
994
     * @return boolean
995
     */
996
    protected function isLeftAndMain($controller)
997
    {
998
        return is_subclass_of($controller, LeftAndMain::class);
999
    }
1000
1001
    /**
1002
     * Gets the back link
1003
     *
1004
     * @return string
1005
     */
1006
    public function getBackLink()
1007
    {
1008
        $backlink = '';
1009
        $toplevelController = $this->getToplevelController();
1010
        // Check for LeftAndMain and alike controllers with a Backlink or Breadcrumbs methods
1011
        if ($toplevelController->hasMethod('Backlink')) {
1012
            //@phpstan-ignore-next-line
1013
            $backlink = $toplevelController->Backlink();
1014
        } elseif ($this->owner->getController()->hasMethod('Breadcrumbs')) {
1015
            $parents = $this->owner->getController()->Breadcrumbs(false)->items;
1016
            $backlink = array_pop($parents)->Link;
1017
        }
1018
        if (!$backlink) {
1019
            $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...
1020
        }
1021
1022
        return $backlink;
1023
    }
1024
1025
    /**
1026
     * Response object for this request after a successful save
1027
     *
1028
     * @param bool $isNewRecord True if this record was just created
1029
     * @param DataObject $record
1030
     * @return HTTPResponse|DBHTMLText|string
1031
     * @todo  This had to be directly copied from {@link GridFieldDetailForm_ItemRequest}
1032
     * because it is a protected method and not visible to a decorator!
1033
     */
1034
    protected function redirectAfterAction($isNewRecord, $record = null)
1035
    {
1036
        $controller = $this->getToplevelController();
1037
1038
        if ($this->isLeftAndMain($controller)) {
1039
            // CMSMain => redirect to show
1040
            if ($this->owner->hasMethod("LinkPageEdit")) {
1041
                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

1041
                return $controller->redirect($this->owner->/** @scrutinizer ignore-call */ LinkPageEdit($record->ID));
Loading history...
1042
            }
1043
        }
1044
1045
        if ($isNewRecord) {
1046
            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

1046
            return $controller->redirect($this->owner->/** @scrutinizer ignore-call */ Link());
Loading history...
1047
        }
1048
        if ($this->owner->gridField && $this->owner->gridField->getList()->byID($this->owner->record->ID)) {
1049
            // Return new view, as we can't do a "virtual redirect" via the CMS Ajax
1050
            // to the same URL (it assumes that its content is already current, and doesn't reload)
1051
            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

1051
            return $this->owner->/** @scrutinizer ignore-call */ edit($controller->getRequest());
Loading history...
1052
        }
1053
        // Changes to the record properties might've excluded the record from
1054
        // a filtered list, so return back to the main view if it can't be found
1055
        $noActionURL = $url = $controller->getRequest()->getURL();
1056
        if (!$url) {
1057
            $url = '';
1058
        }
1059
1060
        // The controller may not have these
1061
        if ($controller->hasMethod('getAction')) {
1062
            $action = $controller->getAction();
1063
            // Handle GridField detail form editing
1064
            if (strpos($url, 'ItemEditForm') !== false) {
1065
                $action = 'ItemEditForm';
1066
            }
1067
            if ($action) {
1068
                $noActionURL = $controller->removeAction($url, $action);
1069
            }
1070
        } else {
1071
            // Simple fallback (last index of)
1072
            $pos = strrpos($url, 'ItemEditForm');
1073
            if (is_int($pos)) {
0 ignored issues
show
introduced by
The condition is_int($pos) is always true.
Loading history...
1074
                $noActionURL = substr($url, 0, $pos);
1075
            }
1076
        }
1077
1078
        $controller->getRequest()->addHeader('X-Pjax', 'Content');
1079
        return $controller->redirect($noActionURL, 302);
1080
    }
1081
}
1082