Passed
Push — master ( 659ead...bfe61f )
by Thomas
11:30
created

ActionsGridFieldItemRequest::updateItemEditForm()   F

Complexity

Conditions 12
Paths 770

Size

Total Lines 91
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 7
Bugs 3 Features 0
Metric Value
cc 12
eloc 41
c 7
b 3
f 0
nc 770
nop 1
dl 0
loc 91
rs 3.1194

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace LeKoala\CmsActions;
4
5
use Exception;
6
use SilverStripe\Admin\LeftAndMain;
7
use SilverStripe\Control\Controller;
8
use SilverStripe\Control\Director;
9
use SilverStripe\Control\HTTPRequest;
10
use SilverStripe\Control\HTTPResponse;
11
use SilverStripe\Control\HTTPResponse_Exception;
12
use SilverStripe\Core\Config\Configurable;
13
use SilverStripe\Core\Extensible;
14
use SilverStripe\Forms\CompositeField;
15
use SilverStripe\Forms\FieldList;
16
use SilverStripe\Forms\Form;
17
use SilverStripe\Forms\FormAction;
18
use SilverStripe\Forms\GridField\GridFieldDetailForm_ItemRequest;
19
use SilverStripe\Forms\Tab;
20
use SilverStripe\Forms\TabSet;
21
use SilverStripe\ORM\DataExtension;
22
use SilverStripe\ORM\DataObject;
23
use SilverStripe\ORM\FieldType\DBHTMLText;
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
27
/**
28
 * Decorates GridDetailForm_ItemRequest to use new form actions and buttons.
29
 *
30
 * This is also applied to LeftAndMain to allow actions on pages
31
 * Warning: LeftAndMain doesn't call updateItemEditForm
32
 *
33
 * This is a lightweight version of BetterButtons that use default getCMSActions functionnality
34
 * on DataObjects
35
 *
36
 * @link https://github.com/unclecheese/silverstripe-gridfield-betterbuttons
37
 * @link https://github.com/unclecheese/silverstripe-gridfield-betterbuttons/blob/master/src/Extensions/GridFieldBetterButtonsItemRequest.php
38
 * @property LeftAndMain|GridFieldDetailForm_ItemRequest|ActionsGridFieldItemRequest $owner
39
 */
40
class ActionsGridFieldItemRequest extends DataExtension
41
{
42
    use Configurable;
43
    use Extensible;
44
45
    /**
46
     * @config
47
     * @var boolean
48
     */
49
    private static $enable_save_prev_next = true;
50
51
    /**
52
     * @config
53
     * @var boolean
54
     */
55
    private static $enable_save_close = true;
56
57
    /**
58
     * @config
59
     * @var boolean
60
     */
61
    private static $enable_delete_right = true;
62
63
    /**
64
     * @config
65
     * @var boolean
66
     */
67
    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...
68
69
    /**
70
     * @var array Allowed controller actions
71
     */
72
    private static $allowed_actions = [
0 ignored issues
show
introduced by
The private property $allowed_actions is not used, and could be removed.
Loading history...
73
        'doSaveAndClose',
74
        'doSaveAndNext',
75
        'doSaveAndPrev',
76
        'doCustomAction', // For CustomAction
77
        'doCustomLink', // For CustomLink
78
    ];
79
80
    /**
81
     * @return array
82
     */
83
    protected function getAvailableActions($actions)
84
    {
85
        $list = [];
86
        foreach ($actions as $action) {
87
            if (is_a($action, CompositeField::class)) {
88
                $list = array_merge($list, $this->getAvailableActions($action->FieldList()));
89
            } else {
90
                $list[] = $action->getName();
91
            }
92
        }
93
94
        return $list;
95
    }
96
97
    /**
98
     * Updates the detail form to include new form actions and buttons
99
     *
100
     * This is called by GridFieldDetailForm_ItemRequest
101
     *
102
     * @param Form $form The ItemEditForm object
103
     */
104
    public function updateItemEditForm($form)
105
    {
106
        $itemRequest = $this->owner;
107
108
        /** @var DataObject $record */
109
        $record = $itemRequest->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...
110
        if (!$record) {
0 ignored issues
show
introduced by
$record is of type SilverStripe\ORM\DataObject, thus it always evaluated to true.
Loading history...
111
            $record = $form->getRecord();
112
        }
113
        if (!$record) {
0 ignored issues
show
introduced by
$record is of type SilverStripe\ORM\DataObject, thus it always evaluated to true.
Loading history...
114
            return;
115
        }
116
117
        // We get the actions as defined on our record
118
        /** @var FieldList $CMSActions */
119
        $CMSActions = $record->getCMSActions();
120
121
        // address Silverstripe bug when SiteTree buttons are broken
122
        // @link https://github.com/silverstripe/silverstripe-cms/issues/2702
123
        $CMSActions->setForm($form);
124
125
        // We can the actions from the GridFieldDetailForm_ItemRequest
126
        // It sets the Save and Delete buttons + Right Group
127
        $actions = $form->Actions();
128
129
        // The default button group that contains the Save or Create action
130
        // @link https://docs.silverstripe.org/en/4/developer_guides/customising_the_admin_interface/how_tos/extend_cms_interface/#extending-the-cms-actions
131
        $MajorActions = $actions->fieldByName('MajorActions');
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $MajorActions is correct as $actions->fieldByName('MajorActions') targeting SilverStripe\Forms\FieldList::fieldByName() 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...
132
133
        // If it doesn't exist, push to default group
134
        if (!$MajorActions) {
0 ignored issues
show
introduced by
$MajorActions is of type null, thus it always evaluated to false.
Loading history...
135
            $MajorActions = $actions;
0 ignored issues
show
Unused Code introduced by
The assignment to $MajorActions is dead and can be removed.
Loading history...
136
        }
137
138
        // Push our actions that are otherwise ignored by SilverStripe
139
        foreach ($CMSActions as $action) {
140
            // Avoid duplicated actions (eg: when added by SilverStripe\Versioned\VersionedGridFieldItemRequest)
141
            if ($actions->fieldByName($action->getName())) {
142
                continue;
143
            }
144
            $actions->push($action);
145
        }
146
147
        // We create a Drop-Up menu afterwards because it may already exist in the $CMSActions
148
        // and we don't want to duplicate it
149
        $this->processDropUpMenu($actions);
150
151
        // Add extension hook
152
        $this->extend('onBeforeUpdateCMSActions', $actions, $record);
153
        $record->extend('onBeforeUpdateCMSActions', $actions);
154
155
        $ActionMenus = $actions->fieldByName('ActionMenus');
156
        // Re-insert ActionMenus to make sure they always follow the buttons
157
        if ($ActionMenus) {
158
            $actions->remove($ActionMenus);
159
            $actions->push($ActionMenus);
160
        }
161
162
        // We have a 4.4 setup, before that there was no RightGroup
163
        $RightGroup = $actions->fieldByName('RightGroup');
164
165
        // Insert again to make sure our actions are properly placed after apply changes
166
        if ($RightGroup) {
167
            $actions->remove($RightGroup);
168
            $actions->push($RightGroup);
169
        }
170
171
        $opts = [
172
            'save_close'     => self::config()->enable_save_close,
173
            'save_prev_next' => self::config()->enable_save_prev_next,
174
            'delete_right'   => self::config()->enable_delete_right,
175
        ];
176
        if ($record->hasMethod('getCMSActionsOptions')) {
177
            $opts = array_merge($opts, $record->getCMSActionsOptions());
178
        }
179
180
        if ($opts['save_close']) {
181
            $this->addSaveAndClose($actions, $record);
182
        }
183
184
        if ($opts['save_prev_next']) {
185
            $this->addSaveNextAndPrevious($actions, $record);
186
        }
187
188
        if ($opts['delete_right']) {
189
            $this->moveCancelAndDelete($actions, $record);
190
        }
191
192
        // Add extension hook
193
        $this->extend('onAfterUpdateCMSActions', $actions, $record);
194
        $record->extend('onAfterUpdateCMSActions', $actions);
195
    }
196
197
198
    /**
199
     * Collect all Drop-Up actions into a menu.
200
     * @param FieldList $actions
201
     * @return void
202
     */
203
    protected function processDropUpMenu($actions)
204
    {
205
        // The Drop-up container may already exist
206
        $dropUpContainer = $actions->fieldByName('ActionMenus.MoreOptions');
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $dropUpContainer is correct as $actions->fieldByName('ActionMenus.MoreOptions') targeting SilverStripe\Forms\FieldList::fieldByName() 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...
207
        foreach ($actions as $action) {
208
            if ($action->hasMethod('getDropUp') && $action->getDropUp()) {
209
                if (!$dropUpContainer) {
210
                    $dropUpContainer = $this->createDropUpContainer($actions);
211
                }
212
                $action->getContainerFieldList()->removeByName($action->getName());
213
                $dropUpContainer->push($action);
214
            }
215
        }
216
    }
217
218
    /**
219
     * Prepares a Drop-Up menu
220
     * @param FieldList $actions
221
     * @return Tab
222
     */
223
    protected function createDropUpContainer($actions)
224
    {
225
        $rootTabSet = new TabSet('ActionMenus');
226
        $dropUpContainer = new Tab(
227
            'MoreOptions',
228
            _t(__CLASS__ . '.MoreOptions', 'More options', 'Expands a view for more buttons')
229
        );
230
        $dropUpContainer->addExtraClass('popover-actions-simulate');
231
        $rootTabSet->push($dropUpContainer);
232
        $rootTabSet->addExtraClass('ss-ui-action-tabset action-menus noborder');
233
234
        $actions->insertBefore('RightGroup', $rootTabSet);
235
236
        return $dropUpContainer;
237
    }
238
239
    /**
240
     * Check if a record can be edited/created/exists
241
     * @param DataObject $record
242
     * @return bool
243
     */
244
    protected function checkCan($record)
245
    {
246
        if (!$record->canEdit() || (!$record->ID && !$record->canCreate())) {
247
            return false;
248
        }
249
250
        return true;
251
    }
252
253
    /**
254
     * @param FieldList $actions
255
     * @param DataObject $record
256
     * @return void
257
     */
258
    public function moveCancelAndDelete(FieldList $actions, DataObject $record)
259
    {
260
        // We have a 4.4 setup, before that there was no RightGroup
261
        $RightGroup = $actions->fieldByName('RightGroup');
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $RightGroup is correct as $actions->fieldByName('RightGroup') targeting SilverStripe\Forms\FieldList::fieldByName() 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...
262
263
        // Move delete at the end
264
        $deleteAction = $actions->fieldByName('action_doDelete');
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $deleteAction is correct as $actions->fieldByName('action_doDelete') targeting SilverStripe\Forms\FieldList::fieldByName() 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...
265
        if ($deleteAction) {
0 ignored issues
show
introduced by
$deleteAction is of type null, thus it always evaluated to false.
Loading history...
266
            // Move at the end of the stack
267
            $actions->remove($deleteAction);
268
            $actions->push($deleteAction);
269
270
            if (!$RightGroup) {
271
                // Only necessary pre 4.4
272
                $deleteAction->addExtraClass('align-right');
273
            }
274
            // Set custom title
275
            if ($record->hasMethod('getDeleteButtonTitle')) {
276
                $deleteAction->setTitle($record->getDeleteButtonTitle());
277
            }
278
        }
279
        // Move cancel at the end
280
        $cancelButton = $actions->fieldByName('cancelbutton');
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $cancelButton is correct as $actions->fieldByName('cancelbutton') targeting SilverStripe\Forms\FieldList::fieldByName() 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...
281
        if ($cancelButton) {
0 ignored issues
show
introduced by
$cancelButton is of type null, thus it always evaluated to false.
Loading history...
282
            // Move at the end of the stack
283
            $actions->remove($cancelButton);
284
            $actions->push($cancelButton);
285
            if (!$RightGroup) {
286
                // Only necessary pre 4.4
287
                $cancelButton->addExtraClass('align-right');
288
            }
289
            // Set custom titlte
290
            if ($record->hasMethod('getCancelButtonTitle')) {
291
                $cancelButton->setTitle($record->getCancelButtonTitle());
292
            }
293
        }
294
    }
295
296
    /**
297
     * @param DataObject $record
298
     * @return int
299
     */
300
    public function getCustomPreviousRecordID(DataObject $record)
301
    {
302
        if ($record->hasMethod('PrevRecord')) {
303
            return $record->PrevRecord()->ID ?? 0;
304
        }
305
306
        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

306
        return $this->owner->/** @scrutinizer ignore-call */ getPreviousRecordID();
Loading history...
307
    }
308
309
    /**
310
     * @param DataObject $record
311
     * @return int
312
     */
313
    public function getCustomNextRecordID(DataObject $record)
314
    {
315
        if ($record->hasMethod('NextRecord')) {
316
            return $record->NextRecord()->ID ?? 0;
317
        }
318
319
        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

319
        return $this->owner->/** @scrutinizer ignore-call */ getNextRecordID();
Loading history...
320
    }
321
322
    /**
323
     * @param FieldList $actions
324
     * @param DataObject $record
325
     * @return void
326
     */
327
    public function addSaveNextAndPrevious(FieldList $actions, DataObject $record)
328
    {
329
        if (!$record->canEdit() || !$record->ID) {
330
            return;
331
        }
332
333
        $MajorActions = $actions->fieldByName('MajorActions');
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $MajorActions is correct as $actions->fieldByName('MajorActions') targeting SilverStripe\Forms\FieldList::fieldByName() 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...
334
335
        // If it doesn't exist, push to default group
336
        if (!$MajorActions) {
0 ignored issues
show
introduced by
$MajorActions is of type null, thus it always evaluated to false.
Loading history...
337
            $MajorActions = $actions;
338
        }
339
340
        // TODO: check why with paginator, after the first page, getPreviousRecordID/getNextRecordID tend to not work properly
341
        $getPreviousRecordID = $this->getCustomPreviousRecordID($record);
342
        $getNextRecordID = $this->getCustomNextRecordID($record);
343
344
        // Coupling for HasPrevNextUtils
345
        if (Controller::has_curr()) {
346
            /** @var HTTPRequest $request */
347
            $request = Controller::curr()->getRequest();
348
            $routeParams = $request->routeParams();
349
            $routeParams['PreviousRecordID'] = $getPreviousRecordID;
350
            $routeParams['NextRecordID'] = $getNextRecordID;
351
            $request->setRouteParams($routeParams);
352
        }
353
354
        if ($getPreviousRecordID) {
355
            $doSaveAndPrev = new FormAction('doSaveAndPrev', _t('ActionsGridFieldItemRequest.SAVEANDPREVIOUS', 'Save and Previous'));
356
            $doSaveAndPrev->addExtraClass($this->getBtnClassForRecord($record));
357
            $doSaveAndPrev->addExtraClass('font-icon-angle-double-left btn-mobile-collapse');
358
            $doSaveAndPrev->setUseButtonTag(true);
359
            $MajorActions->push($doSaveAndPrev);
360
        }
361
        if ($getNextRecordID) {
362
            $doSaveAndNext = new FormAction('doSaveAndNext', _t('ActionsGridFieldItemRequest.SAVEANDNEXT', 'Save and Next'));
363
            $doSaveAndNext->addExtraClass($this->getBtnClassForRecord($record));
364
            $doSaveAndNext->addExtraClass('font-icon-angle-double-right btn-mobile-collapse');
365
            $doSaveAndNext->setUseButtonTag(true);
366
            $MajorActions->push($doSaveAndNext);
367
        }
368
    }
369
370
    /**
371
     * @param FieldList $actions
372
     * @param DataObject $record
373
     * @return void
374
     */
375
    public function addSaveAndClose(FieldList $actions, DataObject $record)
376
    {
377
        if (!$this->checkCan($record)) {
378
            return;
379
        }
380
381
        $MajorActions = $actions->fieldByName('MajorActions');
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $MajorActions is correct as $actions->fieldByName('MajorActions') targeting SilverStripe\Forms\FieldList::fieldByName() 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...
382
383
        // If it doesn't exist, push to default group
384
        if (!$MajorActions) {
0 ignored issues
show
introduced by
$MajorActions is of type null, thus it always evaluated to false.
Loading history...
385
            $MajorActions = $actions;
386
        }
387
388
        if ($record->ID) {
389
            $label = _t('ActionsGridFieldItemRequest.SAVEANDCLOSE', 'Save and Close');
390
        } else {
391
            $label = _t('ActionsGridFieldItemRequest.CREATEANDCLOSE', 'Create and Close');
392
        }
393
        $saveAndClose = new FormAction('doSaveAndClose', $label);
394
        $saveAndClose->addExtraClass($this->getBtnClassForRecord($record));
395
        $saveAndClose->setAttribute('data-text-alternate', $label);
396
        if ($record->ID) {
397
            $saveAndClose->setAttribute('data-btn-alternate-add', 'btn-primary');
398
            $saveAndClose->setAttribute('data-btn-alternate-remove', 'btn-outline-primary');
399
        }
400
        $saveAndClose->addExtraClass('font-icon-level-up btn-mobile-collapse');
401
        $saveAndClose->setUseButtonTag(true);
402
        $MajorActions->push($saveAndClose);
403
    }
404
405
    /**
406
     * New and existing records have different classes
407
     *
408
     * @param DataObject $record
409
     * @return string
410
     */
411
    protected function getBtnClassForRecord(DataObject $record)
412
    {
413
        if ($record->ID) {
414
            return 'btn-outline-primary';
415
        }
416
417
        return 'btn-primary';
418
    }
419
420
    /**
421
     * @param $action
422
     * @param $definedActions
423
     * @return mixed|CompositeField|null
424
     */
425
    protected static function findAction($action, $definedActions)
426
    {
427
        $result = null;
428
429
        foreach ($definedActions as $definedAction) {
430
            if (is_a($definedAction, CompositeField::class)) {
431
                $result = self::findAction($action, $definedAction->FieldList());
432
                if ($result) {
433
                    break;
434
                }
435
            }
436
437
            $definedActionName = $definedAction->getName();
438
439
            if ($definedAction->hasMethod('actionName')) {
440
                $definedActionName = $definedAction->actionName();
441
            }
442
            if ($definedActionName === $action) {
443
                $result = $definedAction;
444
                break;
445
            }
446
        }
447
448
        return $result;
449
    }
450
451
    /**
452
     * Forward a given action to a DataObject
453
     *
454
     * Action must be declared in getCMSActions to be called
455
     *
456
     * @param string $action
457
     * @param array $data
458
     * @param Form $form
459
     * @return HTTPResponse|DBHTMLText|string
460
     * @throws HTTPResponse_Exception
461
     */
462
    protected function forwardActionToRecord($action, $data = [], $form = null)
463
    {
464
        $controller = $this->getToplevelController();
465
466
        // We have an item request or a controller that can provide a record
467
        $record = null;
468
        if ($this->owner->hasMethod('ItemEditForm')) {
469
            // It's a request handler. Don't check for a specific class as it may be subclassed
470
            $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...
471
        } elseif ($controller->hasMethod('save_siteconfig')) {
472
            // Check for any type of siteconfig controller
473
            $record = SiteConfig::current_site_config();
474
        } elseif (!empty($data['ClassName']) && !empty($data['ID'])) {
475
            $record = DataObject::get_by_id($data['ClassName'], $data['ID']);
476
        } elseif ($controller->hasMethod("getRecord")) {
477
            $record = $controller->getRecord();
478
        }
479
480
        if (!$record) {
481
            throw new Exception("No record to handle the action $action on " . get_class($controller));
482
        }
483
        $definedActions = $record->getCMSActions();
484
        // Check if the action is indeed available
485
        $clickedAction = null;
486
        if (!empty($definedActions)) {
487
            $clickedAction = self::findAction($action, $definedActions);
488
        }
489
        if (!$clickedAction) {
0 ignored issues
show
introduced by
$clickedAction is of type mixed|null, thus it always evaluated to false.
Loading history...
490
            $class = get_class($record);
491
            $availableActions = implode(',', $this->getAvailableActions($definedActions));
492
            if (!$availableActions) {
493
                $availableActions = "(no available actions, please check getCMSActions)";
494
            }
495
496
            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

496
            return $this->owner->/** @scrutinizer ignore-call */ httpError(403, sprintf(
Loading history...
497
                'Action not available on %s. It must be one of : %s',
498
                $class,
499
                $availableActions
500
            ));
501
        }
502
        $message = null;
503
        $error = false;
504
505
        // Check record BEFORE the action
506
        // It can be deleted by the action, and it will return to the list
507
        $isNewRecord = $record->ID === 0;
508
509
        try {
510
            $result = $record->$action($data, $form, $controller);
511
512
            // We have a response
513
            if ($result instanceof HTTPResponse) {
514
                return $result;
515
            }
516
517
            if ($result === false) {
518
                // Result returned an error (false)
519
                $error = true;
520
                $message = _t(
521
                    'ActionsGridFieldItemRequest.FAILED',
522
                    'Action {action} failed on {name}',
523
                    ['action' => $clickedAction->getTitle(), 'name' => $record->i18n_singular_name()]
524
                );
525
            } elseif (is_string($result)) {
526
                // Result is a message
527
                $message = $result;
528
            }
529
        } catch (Exception $ex) {
530
            $error = true;
531
            $message = $ex->getMessage();
532
        }
533
534
        // Build default message
535
        if (!$message) {
536
            $message = _t(
537
                'ActionsGridFieldItemRequest.DONE',
538
                'Action {action} was done on {name}',
539
                ['action' => $clickedAction->getTitle(), 'name' => $record->i18n_singular_name()]
540
            );
541
        }
542
        $status = 'good';
543
        if ($error) {
544
            $status = 'bad';
545
        }
546
547
        // Progressive actions return array with json data
548
        if (method_exists($clickedAction, 'getProgressive') && $clickedAction->getProgressive()) {
549
            $response = $controller->getResponse();
550
            $response->addHeader('Content-Type', 'application/json');
551
            if ($result) {
552
                $response->setBody(json_encode($result));
553
            }
554
555
            return $response;
556
        }
557
558
        // We don't have a form, simply return the result
559
        if (!$form) {
560
            if ($error) {
561
                return $this->owner->httpError(403, $message);
562
            }
563
564
            return $message;
565
        }
566
567
        if (Director::is_ajax()) {
568
            $controller->getResponse()->addHeader('X-Status', rawurlencode($message));
569
            if (method_exists($clickedAction, 'getShouldRefresh') && $clickedAction->getShouldRefresh()) {
570
                $controller->getResponse()->addHeader('X-Reload', "true");
571
            }
572
            // 4xx status makes a red box
573
            if ($error) {
574
                $controller->getResponse()->setStatusCode(400);
575
            }
576
        } else {
577
            // If the controller support sessionMessage, use it instead of form
578
            if ($controller->hasMethod('sessionMessage')) {
579
                $controller->sessionMessage($message, $status, ValidationResult::CAST_HTML);
580
            } else {
581
                $form->sessionMessage($message, $status, ValidationResult::CAST_HTML);
582
            }
583
        }
584
585
        // Custom redirect
586
        if (method_exists($clickedAction, 'getRedirectURL') && $clickedAction->getRedirectURL()) {
587
            $controller->getResponse()->addHeader('X-Reload', "true"); // we probably need a full ui refresh
588
            return $controller->redirect($clickedAction->getRedirectURL());
589
        }
590
591
        // Redirect after action
592
        return $this->redirectAfterAction($isNewRecord, $record);
593
    }
594
595
    /**
596
     * Handles custom links
597
     *
598
     * Use CustomLink with default behaviour to trigger this
599
     *
600
     * See:
601
     * DefaultLink::getModelLink
602
     * GridFieldCustomLink::getLink
603
     *
604
     * @param HTTPRequest $request
605
     * @return HTTPResponse|DBHTMLText|string
606
     * @throws Exception
607
     */
608
    public function doCustomLink(HTTPRequest $request)
609
    {
610
        $action = $request->getVar('CustomLink');
611
        return $this->forwardActionToRecord($action);
612
    }
613
614
    /**
615
     * Handles custom actions
616
     *
617
     * Use CustomAction class to trigger this
618
     *
619
     * Nested actions are submitted like this
620
     * [action_doCustomAction] => Array
621
     * (
622
     *   [doTestAction] => 1
623
     * )
624
     *
625
     * @param array $data The form data
626
     * @param Form $form The form object
627
     * @return HTTPResponse|DBHTMLText|string
628
     * @throws Exception
629
     */
630
    public function doCustomAction($data, $form)
631
    {
632
        $action = key($data['action_doCustomAction']);
633
634
        return $this->forwardActionToRecord($action, $data, $form);
635
    }
636
637
    /**
638
     * Saves the form and goes back to list view
639
     *
640
     * @param array $data The form data
641
     * @param Form $form The form object
642
     */
643
    public function doSaveAndClose($data, $form)
644
    {
645
        $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

645
        $this->owner->/** @scrutinizer ignore-call */ 
646
                      doSave($data, $form);
Loading history...
646
        // Redirect after save
647
        $controller = $this->getToplevelController();
648
649
        $link = $this->getBackLink();
650
651
        $link = $this->addGridState($link, $data);
652
653
        $controller->getResponse()->addHeader("X-Pjax", "Content");
654
        // Prevent Already directed to errors
655
        $controller->getResponse()->addHeader("Location", $link);
656
657
        return $controller->redirect($link);
658
    }
659
660
    /**
661
     * Saves the form and goes back to the next item
662
     *
663
     * @param array $data The form data
664
     * @param Form $form The form object
665
     */
666
    public function doSaveAndNext($data, $form)
667
    {
668
        $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...
669
        $this->owner->doSave($data, $form);
670
        // Redirect after save
671
        $controller = $this->getToplevelController();
672
        $controller->getResponse()->addHeader("X-Pjax", "Content");
673
674
        $getNextRecordID = $this->getCustomNextRecordID($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...getCustomNextRecordID() 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

674
        $getNextRecordID = $this->getCustomNextRecordID(/** @scrutinizer ignore-type */ $record);
Loading history...
675
        $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

675
        $class = get_class(/** @scrutinizer ignore-type */ $record);
Loading history...
676
        $next = $class::get()->byID($getNextRecordID);
677
678
        $link = $this->owner->getEditLink($getNextRecordID);
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

678
        /** @scrutinizer ignore-call */ 
679
        $link = $this->owner->getEditLink($getNextRecordID);
Loading history...
679
680
        $link = $this->addGridState($link, $data);
681
682
        // Link to a specific tab if set, see cms-actions.js
683
        if ($next && !empty($data['_activetab'])) {
684
            $link .= sprintf('#%s', $data['_activetab']);
685
        }
686
687
        return $controller->redirect($link);
688
    }
689
690
    /**
691
     * Saves the form and goes to the previous item
692
     *
693
     * @param array $data The form data
694
     * @param Form $form The form object
695
     */
696
    public function doSaveAndPrev($data, $form)
697
    {
698
        $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...
699
        $this->owner->doSave($data, $form);
700
        // Redirect after save
701
        $controller = $this->getToplevelController();
702
        $controller->getResponse()->addHeader("X-Pjax", "Content");
703
704
        $getPreviousRecordID = $this->getCustomPreviousRecordID($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...ustomPreviousRecordID() 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

704
        $getPreviousRecordID = $this->getCustomPreviousRecordID(/** @scrutinizer ignore-type */ $record);
Loading history...
705
        $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

705
        $class = get_class(/** @scrutinizer ignore-type */ $record);
Loading history...
706
        $prev = $class::get()->byID($getPreviousRecordID);
707
708
        $link = $this->owner->getEditLink($getPreviousRecordID);
709
710
        // Link to a specific tab if set, see cms-actions.js
711
        if ($prev && !empty($data['_activetab'])) {
712
            $link .= sprintf('#%s', $data['_activetab']);
713
        }
714
715
        return $controller->redirect($link);
716
    }
717
718
    protected function addGridState($url, $data)
719
    {
720
        $BackURL = $data['BackURL'] ?? null;
721
        if ($BackURL) {
722
            $query = parse_url($BackURL, PHP_URL_QUERY);
723
            if ($query) {
724
                $url .= '?' . $query;
725
            }
726
        }
727
        return $url;
728
    }
729
730
    /**
731
     * Gets the top level controller.
732
     *
733
     * @return Controller
734
     * @todo  This had to be directly copied from {@link GridFieldDetailForm_ItemRequest}
735
     * because it is a protected method and not visible to a decorator!
736
     */
737
    protected function getToplevelController()
738
    {
739
        if ($this->isLeftAndMain($this->owner)) {
740
            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...
741
        }
742
        if (!$this->owner->hasMethod("getController")) {
743
            return Controller::curr();
744
        }
745
        $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

745
        /** @scrutinizer ignore-call */ 
746
        $controller = $this->owner->getController();
Loading history...
746
        while ($controller instanceof GridFieldDetailForm_ItemRequest) {
747
            $controller = $controller->getController();
748
        }
749
750
        return $controller;
751
    }
752
753
    protected function isLeftAndMain($controller)
754
    {
755
        return is_subclass_of($controller, LeftAndMain::class);
756
    }
757
758
    /**
759
     * Gets the back link
760
     *
761
     * @return string
762
     * @todo This had to be directly copied from {@link GridFieldDetailForm_ItemRequest}
763
     * because it is a protected method and not visible to a decorator!
764
     */
765
    public function getBackLink()
766
    {
767
        $backlink = '';
768
        $toplevelController = $this->getToplevelController();
769
        // Check for LeftAndMain and alike controllers with a Backlink or Breadcrumbs methods
770
        if ($toplevelController->hasMethod('Backlink')) {
771
            $backlink = $toplevelController->Backlink();
772
        } elseif ($this->owner->getController()->hasMethod('Breadcrumbs')) {
773
            $parents = $this->owner->getController()->Breadcrumbs(false)->items;
774
            $backlink = array_pop($parents)->Link;
775
        }
776
        if (!$backlink) {
777
            $backlink = $toplevelController->Link();
778
        }
779
780
        return $backlink;
781
    }
782
783
    /**
784
     * Response object for this request after a successful save
785
     *
786
     * @param bool $isNewRecord True if this record was just created
787
     * @param DataObject $record
788
     * @return HTTPResponse|DBHTMLText|string
789
     * @todo  This had to be directly copied from {@link GridFieldDetailForm_ItemRequest}
790
     * because it is a protected method and not visible to a decorator!
791
     */
792
    protected function redirectAfterAction($isNewRecord, $record = null)
793
    {
794
        $controller = $this->getToplevelController();
795
796
        if ($this->isLeftAndMain($controller)) {
797
            // CMSMain => redirect to show
798
            if ($this->owner->hasMethod("LinkPageEdit")) {
799
                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

799
                return $controller->redirect($this->owner->/** @scrutinizer ignore-call */ LinkPageEdit($record->ID));
Loading history...
800
            }
801
        }
802
803
        if ($isNewRecord) {
804
            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

804
            return $controller->redirect($this->owner->/** @scrutinizer ignore-call */ Link());
Loading history...
805
        }
806
        if ($this->owner->gridField && $this->owner->gridField->getList()->byID($this->owner->record->ID)) {
0 ignored issues
show
Bug introduced by
The method byID() does not exist on SilverStripe\ORM\SS_List. It seems like you code against a sub-type of said class. However, the method does not exist in SilverStripe\ORM\Sortable or SilverStripe\ORM\Limitable. Are you sure you never get one of those? ( Ignorable by Annotation )

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

806
        if ($this->owner->gridField && $this->owner->gridField->getList()->/** @scrutinizer ignore-call */ byID($this->owner->record->ID)) {
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...
Bug Best Practice introduced by
The property record does not exist on LeKoala\CmsActions\ActionsGridFieldItemRequest. Did you maybe forget to declare it?
Loading history...
807
            // Return new view, as we can't do a "virtual redirect" via the CMS Ajax
808
            // to the same URL (it assumes that its content is already current, and doesn't reload)
809
            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

809
            return $this->owner->/** @scrutinizer ignore-call */ edit($controller->getRequest());
Loading history...
810
        }
811
        // Changes to the record properties might've excluded the record from
812
        // a filtered list, so return back to the main view if it can't be found
813
        $noActionURL = $url = $controller->getRequest()->getURL();
814
815
        // The controller may not have these
816
        if ($controller->hasMethod('getAction')) {
817
            $action = $controller->getAction();
818
            // Handle GridField detail form editing
819
            if (strpos($url, 'ItemEditForm') !== false) {
820
                $action = 'ItemEditForm';
821
            }
822
            if ($action) {
823
                $noActionURL = $controller->removeAction($url, $action);
824
            }
825
        } else {
826
            // Simple fallback (last index of)
827
            $pos = strrpos($url ?? '', 'ItemEditForm');
828
            $noActionURL = substr($url ?? '', 0, $pos);
829
        }
830
831
        $controller->getRequest()->addHeader('X-Pjax', 'Content');
832
833
        return $controller->redirect($noActionURL, 302);
834
    }
835
}
836