Passed
Push — master ( 4e7a51...0ba264 )
by Thomas
02:41
created

ActionsGridFieldItemRequest::updateItemEditForm()   D

Complexity

Conditions 11
Paths 386

Size

Total Lines 80
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 2 Features 0
Metric Value
cc 11
eloc 33
c 3
b 2
f 0
nc 386
nop 1
dl 0
loc 80
rs 4.1583

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\Forms\Tab;
7
use SilverStripe\Forms\Form;
8
use SilverStripe\Forms\TabSet;
9
use SilverStripe\ORM\DataObject;
10
use SilverStripe\Forms\FieldList;
11
use SilverStripe\Control\Director;
12
use SilverStripe\Forms\FormAction;
13
use SilverStripe\Admin\LeftAndMain;
14
use SilverStripe\ORM\DataExtension;
15
use SilverStripe\Control\Controller;
16
use SilverStripe\Control\HTTPRequest;
17
use SilverStripe\Versioned\Versioned;
18
use SilverStripe\Control\HTTPResponse;
19
use SilverStripe\ORM\ValidationResult;
20
use SilverStripe\Core\Config\Configurable;
21
use SilverStripe\Forms\GridField\GridFieldDetailForm_ItemRequest;
22
23
/**
24
 * Decorates GridDetailForm_ItemRequest to use new form actions and buttons.
25
 * This is also applied to LeftAndMain to allow actions on pages
26
 * Warning: LeftAndMain doesn't call updateItemEditForm
27
 *
28
 * This is a lightweight version of BetterButtons that use default getCMSActions functionnality
29
 * on DataObjects
30
 *
31
 * @link https://github.com/unclecheese/silverstripe-gridfield-betterbuttons
32
 * @link https://github.com/unclecheese/silverstripe-gridfield-betterbuttons/blob/master/src/Extensions/GridFieldBetterButtonsItemRequest.php
33
 * @property \SilverStripe\Forms\GridField\GridFieldDetailForm_ItemRequest|\SilverStripe\Admin\LeftAndMain $owner
34
 */
35
class ActionsGridFieldItemRequest extends DataExtension
36
{
37
    use Configurable;
38
39
    /**
40
     * @config
41
     * @var boolean
42
     */
43
    private static $enable_save_prev_next = true;
44
45
    /**
46
     * @config
47
     * @var boolean
48
     */
49
    private static $enable_save_close = true;
50
51
    /**
52
     * @config
53
     * @var boolean
54
     */
55
    private static $enable_delete_right = true;
56
57
    /**
58
     * @config
59
     * @var boolean
60
     */
61
    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...
62
63
    /**
64
     * @var array Allowed controller actions
65
     */
66
    private static $allowed_actions = array(
0 ignored issues
show
introduced by
The private property $allowed_actions is not used, and could be removed.
Loading history...
67
        'doSaveAndClose',
68
        'doSaveAndNext',
69
        'doSaveAndPrev',
70
        'doCustomAction', // For CustomAction
71
        'doCustomLink', // For CustomLink
72
    );
73
74
    /**
75
     * @return array
76
     */
77
    protected function getAvailableActions($actions)
78
    {
79
        $list = [];
80
        foreach ($actions as $action) {
81
            $list[] = $action->getName();
82
        }
83
        return $list;
84
    }
85
86
    /**
87
     * Updates the detail form to include new form actions and buttons
88
     *
89
     * This is called by GridFieldDetailForm_ItemRequest
90
     *
91
     * @param Form The ItemEditForm object
0 ignored issues
show
Bug introduced by
The type LeKoala\CmsActions\The 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...
92
     */
93
    public function updateItemEditForm($form)
94
    {
95
        $itemRequest = $this->owner;
96
97
        /** @var DataObject $record  */
98
        $record = $itemRequest->record;
99
        if (!$record) {
0 ignored issues
show
introduced by
$record is of type SilverStripe\ORM\DataObject, thus it always evaluated to true.
Loading history...
100
            $record = $form->getRecord();
101
        }
102
        if (!$record) {
0 ignored issues
show
introduced by
$record is of type SilverStripe\ORM\DataObject, thus it always evaluated to true.
Loading history...
103
            return;
104
        }
105
106
        // We get the actions as defined on our record
107
        /** @var FieldList $CMSActions  */
108
        $CMSActions = $record->getCMSActions();
109
110
        // address Silverstripe bug when SiteTree buttons are broken
111
        // @link https://github.com/silverstripe/silverstripe-cms/issues/2702
112
        $CMSActions->setForm($form);
113
114
        // We can the actions from the GridFieldDetailForm_ItemRequest
115
        // It sets the Save and Delete buttons + Right Group
116
        $actions = $form->Actions();
117
118
        // The default button group that contains the Save or Create action
119
        // @link https://docs.silverstripe.org/en/4/developer_guides/customising_the_admin_interface/how_tos/extend_cms_interface/#extending-the-cms-actions
120
        $MajorActions = $actions->fieldByName('MajorActions');
121
122
        // If it doesn't exist, push to default group
123
        if (!$MajorActions) {
124
            $MajorActions = $actions;
0 ignored issues
show
Unused Code introduced by
The assignment to $MajorActions is dead and can be removed.
Loading history...
125
        }
126
127
        // Push our actions that are otherwise ignored by SilverStripe
128
        foreach ($CMSActions as $action) {
129
            // Avoid duplicated actions (eg: when added by SilverStripe\Versioned\VersionedGridFieldItemRequest)
130
            if ($actions->fieldByName($action->getName())) {
131
                continue;
132
            }
133
            $actions->push($action);
134
        }
135
136
        // We create a Drop-Up menu afterwards because it may already exist in the $CMSActions
137
        // and we don't want to duplicate it
138
        $this->processDropUpMenu($actions);
139
140
        // Add extension hook
141
        $record->extend('onBeforeUpdateCMSActions', $actions);
142
143
        $ActionMenus = $actions->fieldByName('ActionMenus');
144
        // Re-insert ActionMenus to make sure they always follow the buttons
145
        if ($ActionMenus) {
146
            $actions->remove($ActionMenus);
147
            $actions->push($ActionMenus);
148
        }
149
150
        // We have a 4.4 setup, before that there was no RightGroup
151
        $RightGroup = $actions->fieldByName('RightGroup');
152
153
        // Insert again to make sure our actions are properly placed after apply changes
154
        if ($RightGroup) {
155
            $actions->remove($RightGroup);
156
            $actions->push($RightGroup);
157
        }
158
159
        if (self::config()->enable_save_close) {
160
            $this->addSaveAndClose($actions, $record);
161
        }
162
163
        if (self::config()->enable_save_prev_next) {
164
            $this->addSaveNextAndPrevious($actions, $record);
165
        }
166
167
        if (self::config()->enable_delete_right) {
168
            $this->moveCancelAndDelete($actions, $record);
169
        }
170
171
        // Add extension hook
172
        $record->extend('onAfterUpdateCMSActions', $actions);
173
    }
174
175
176
    /**
177
     * Collect all Drop-Up actions into a menu.
178
     * @param FieldList $actions
179
     * @return void
180
     */
181
    protected function processDropUpMenu($actions)
182
    {
183
        // The Drop-up container may already exist
184
        $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...
185
        foreach ($actions as $action) {
186
            if ($action->hasMethod('getDropUp') && $action->getDropUp()) {
187
                if (!$dropUpContainer) {
188
                    $dropUpContainer = $this->createDropUpContainer($actions);
189
                }
190
                $action->getContainerFieldList()->removeByName($action->getName());
191
                $dropUpContainer->push($action);
192
            }
193
        }
194
    }
195
196
    /**
197
     * Prepares a Drop-Up menu
198
     * @param FieldList $actions
199
     * @return Tab
200
     */
201
    protected function createDropUpContainer($actions)
202
    {
203
        $rootTabSet = new TabSet('ActionMenus');
204
        $dropUpContainer = new Tab(
205
            'MoreOptions',
206
            _t(__CLASS__ . '.MoreOptions', 'More options', 'Expands a view for more buttons')
207
        );
208
        $dropUpContainer->addExtraClass('popover-actions-simulate');
209
        $rootTabSet->push($dropUpContainer);
210
        $rootTabSet->addExtraClass('ss-ui-action-tabset action-menus noborder');
211
212
        $actions->insertBefore('RightGroup', $rootTabSet);
213
        return $dropUpContainer;
214
    }
215
216
    /**
217
     * @param FieldList $actions
218
     * @param DataObject $record
219
     * @return void
220
     */
221
    public function moveCancelAndDelete(FieldList $actions, DataObject $record)
222
    {
223
        // We have a 4.4 setup, before that there was no RightGroup
224
        $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...
225
226
        // Move delete at the end
227
        $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...
228
        if ($deleteAction) {
0 ignored issues
show
introduced by
$deleteAction is of type null, thus it always evaluated to false.
Loading history...
229
            // Move at the end of the stack
230
            $actions->remove($deleteAction);
231
            $actions->push($deleteAction);
232
233
            if ($RightGroup) {
234
                // Stack position is enough to have it on the left
235
            } else {
236
                // Only necessary pre 4.4
237
                $deleteAction->addExtraClass('align-right');
238
            }
239
            // Set custom title
240
            if ($record->hasMethod('getDeleteButtonTitle')) {
241
                $deleteAction->setTitle($record->getDeleteButtonTitle());
242
            }
243
        }
244
        // Move cancel at the end
245
        $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...
246
        if ($cancelButton) {
0 ignored issues
show
introduced by
$cancelButton is of type null, thus it always evaluated to false.
Loading history...
247
            // Move at the end of the stack
248
            $actions->remove($cancelButton);
249
            $actions->push($cancelButton);
250
            if ($RightGroup) {
251
                // Stack position is enough to have it on the left
252
            } else {
253
                // Only necessary pre 4.4
254
                $cancelButton->addExtraClass('align-right');
255
            }
256
            // Set custom titlte
257
            if ($record->hasMethod('getCancelButtonTitle')) {
258
                $cancelButton->setTitle($record->getCancelButtonTitle());
259
            }
260
        }
261
    }
262
263
    /**
264
     * @param DataObject $record
265
     * @return int
266
     */
267
    public function getCustomPreviousRecordID(DataObject $record)
268
    {
269
        if ($record->hasMethod('PrevRecord')) {
270
            return $record->PrevRecord()->ID ?? 0;
271
        }
272
        return $this->owner->getPreviousRecordID();
273
    }
274
275
    /**
276
     * @param DataObject $record
277
     * @return int
278
     */
279
    public function getCustomNextRecordID(DataObject $record)
280
    {
281
        if ($record->hasMethod('NextRecord')) {
282
            return $record->NextRecord()->ID ?? 0;
283
        }
284
        return $this->owner->getNextRecordID();
285
    }
286
287
    /**
288
     * @param FieldList $actions
289
     * @param DataObject $record
290
     * @return void
291
     */
292
    public function addSaveNextAndPrevious(FieldList $actions, DataObject $record)
293
    {
294
        if (!$record->canEdit()) {
295
            return;
296
        }
297
        if (!$record->ID) {
298
            return;
299
        }
300
301
        $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...
302
303
        // If it doesn't exist, push to default group
304
        if (!$MajorActions) {
0 ignored issues
show
introduced by
$MajorActions is of type null, thus it always evaluated to false.
Loading history...
305
            $MajorActions = $actions;
306
        }
307
308
        // TODO: check why with paginator, after the first page, getPreviousRecordID/getNextRecordID tend to not work properly
309
        $getPreviousRecordID = $this->getCustomPreviousRecordID($record);
310
        $getNextRecordID = $this->getCustomNextRecordID($record);
311
312
        // Coupling for HasPrevNextUtils
313
        if (Controller::has_curr()) {
314
            $request =  Controller::curr()->getRequest();
315
            $routeParams = $request->routeParams();
316
            $routeParams['PreviousRecordID'] = $getPreviousRecordID;
317
            $routeParams['NextRecordID'] = $getNextRecordID;
318
            $request->setRouteParams($routeParams);
319
        }
320
321
        if ($getPreviousRecordID) {
322
            $doSaveAndPrev = new FormAction('doSaveAndPrev', _t('ActionsGridFieldItemRequest.SAVEANDPREVIOUS', 'Save and Previous'));
323
            $doSaveAndPrev->addExtraClass($this->getBtnClassForRecord($record));
324
            $doSaveAndPrev->addExtraClass('font-icon-angle-double-left');
325
            $doSaveAndPrev->setUseButtonTag(true);
326
            $MajorActions->push($doSaveAndPrev);
327
        }
328
        if ($getNextRecordID) {
329
            $doSaveAndNext = new FormAction('doSaveAndNext', _t('ActionsGridFieldItemRequest.SAVEANDNEXT', 'Save and Next'));
330
            $doSaveAndNext->addExtraClass($this->getBtnClassForRecord($record));
331
            $doSaveAndNext->addExtraClass('font-icon-angle-double-right');
332
            $doSaveAndNext->setUseButtonTag(true);
333
            $MajorActions->push($doSaveAndNext);
334
        }
335
    }
336
337
    /**
338
     * @param FieldList $actions
339
     * @param DataObject $record
340
     * @return void
341
     */
342
    public function addSaveAndClose(FieldList $actions, DataObject $record)
343
    {
344
        if (!$record->canEdit()) {
345
            return;
346
        }
347
        if (!$record->ID && !$record->canCreate()) {
348
            return;
349
        }
350
351
        $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...
352
353
        // If it doesn't exist, push to default group
354
        if (!$MajorActions) {
0 ignored issues
show
introduced by
$MajorActions is of type null, thus it always evaluated to false.
Loading history...
355
            $MajorActions = $actions;
356
        }
357
358
        if ($record->ID) {
359
            $label = _t('ActionsGridFieldItemRequest.SAVEANDCLOSE', 'Save and Close');
360
        } else {
361
            $label = _t('ActionsGridFieldItemRequest.CREATEANDCLOSE', 'Create and Close');
362
        }
363
        $saveAndClose = new FormAction('doSaveAndClose', $label);
364
        $saveAndClose->addExtraClass($this->getBtnClassForRecord($record));
365
        $saveAndClose->setAttribute('data-text-alternate', $label);
366
        if ($record->ID) {
367
            $saveAndClose->setAttribute('data-btn-alternate-add', 'btn-primary');
368
            $saveAndClose->setAttribute('data-btn-alternate-remove', 'btn-outline-primary');
369
        }
370
        $saveAndClose->addExtraClass('font-icon-level-up');
371
        $saveAndClose->setUseButtonTag(true);
372
        $MajorActions->push($saveAndClose);
373
    }
374
375
    /**
376
     * New and existing records have different classes
377
     *
378
     * @param DataObject $record
379
     * @return string
380
     */
381
    protected function getBtnClassForRecord(DataObject $record)
382
    {
383
        if ($record->ID) {
384
            return 'btn-outline-primary';
385
        }
386
        return 'btn-primary';
387
    }
388
389
    /**
390
     * Forward a given action to a DataObject
391
     *
392
     * Action must be declared in getCMSActions to be called
393
     *
394
     * @param string $action
395
     * @param array $data
396
     * @param Form $form
397
     * @return HTTPResponse|DBHTMLText|string
0 ignored issues
show
Bug introduced by
The type LeKoala\CmsActions\DBHTMLText 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...
398
     */
399
    protected function forwardActionToRecord($action, $data = [], $form = null)
400
    {
401
        $controller = $this->getToplevelController();
402
403
        // We have an item request
404
        $record = null;
405
        if ($this->owner instanceof GridFieldDetailForm_ItemRequest) {
406
            $record = $this->owner->record;
407
        } elseif ($controller instanceof LeftAndMain) {
408
            if (empty($data['ClassName']) || empty($data['ID'])) {
409
                throw new Exception("Submitted data does not contain and ID and a ClassName");
410
            }
411
            $record = DataObject::get_by_id($data['ClassName'], $data['ID']);
412
        } elseif ($controller->hasMethod("getRecord")) {
413
            $record = $controller->getRecord();
414
        }
415
416
        if (!$record) {
417
            throw new Exception("No record to handle the action $action on " . get_class($controller));
418
        }
419
        $definedActions = $record->getCMSActions();
420
        // Check if the action is indeed available
421
        $clickedAction = null;
422
        if (!empty($definedActions)) {
423
            foreach ($definedActions as $definedAction) {
424
                $definedActionName = $definedAction->getName();
425
                if ($definedAction->hasMethod('actionName')) {
426
                    $definedActionName = $definedAction->actionName();
427
                }
428
                if ($definedActionName == $action) {
429
                    $clickedAction = $definedAction;
430
                }
431
            }
432
        }
433
        if (!$clickedAction) {
434
            $class = get_class($record);
435
            $availableActions = implode(',', $this->getAvailableActions($definedActions));
436
            if (!$availableActions) {
437
                $availableActions = "(no available actions, please check getCMSActions)";
438
            }
439
            return $this->owner->httpError(403, 'Action not available on ' . $class . '. It must be one of : ' . $availableActions);
440
        }
441
        $message = null;
442
        $error = false;
443
444
        // Check record BEFORE the action
445
        // It can be deleted by the action and it will return to the list
446
        $isNewRecord = $record->ID == 0;
447
448
        try {
449
            $result = $record->$action($data, $form, $controller);
450
451
            // We have a response
452
            if ($result && $result instanceof HTTPResponse) {
453
                return $result;
454
            }
455
456
            if ($result === false) {
457
                // Result returned an error (false)
458
                $error = true;
459
                $message = _t(
460
                    'ActionsGridFieldItemRequest.FAILED',
461
                    'Action {action} failed on {name}',
462
                    array(
463
                        'action' => $clickedAction->getTitle(),
464
                        'name' => $record->i18n_singular_name(),
465
                    )
466
                );
467
            } elseif (is_string($result)) {
468
                // Result is a message
469
                $message = $result;
470
            }
471
        } catch (Exception $ex) {
472
            $error = true;
473
            $message = $ex->getMessage();
474
        }
475
476
        // Build default message
477
        if (!$message) {
478
            $message = _t(
479
                'ActionsGridFieldItemRequest.DONE',
480
                'Action {action} was done on {name}',
481
                array(
482
                    'action' => $clickedAction->getTitle(),
483
                    'name' => $record->i18n_singular_name(),
484
                )
485
            );
486
        }
487
        $status = 'good';
488
        if ($error) {
489
            $status = 'bad';
490
        }
491
492
        // Progressive actions return array with json data
493
        if (method_exists($clickedAction, 'getProgressive') && $clickedAction->getProgressive()) {
494
            $response = $controller->getResponse();
495
            $response->addHeader('Content-Type', 'application/json');
496
            if ($result) {
497
                $response->setBody(json_encode($result));
498
            }
499
            return $response;
500
        }
501
502
        // We don't have a form, simply return the result
503
        if (!$form) {
504
            if ($error) {
505
                return $this->owner->httpError(403, $message);
506
            }
507
            return $message;
508
        }
509
        if (Director::is_ajax()) {
510
            $controller = $this->getToplevelController();
511
            $controller->getResponse()->addHeader('X-Status', rawurlencode($message));
512
            if (method_exists($clickedAction, 'getShouldRefresh') && $clickedAction->getShouldRefresh()) {
513
                $controller->getResponse()->addHeader('X-Reload', "true");
514
            }
515
            // 4xx status makes a red box
516
            if ($error) {
517
                $controller->getResponse()->setStatusCode(400);
518
            }
519
        } else {
520
            $form->sessionMessage($message, $status, ValidationResult::CAST_HTML);
521
        }
522
        // Redirect after action
523
        return $this->redirectAfterAction($isNewRecord, $record);
524
    }
525
526
    /**
527
     * Handles custom links
528
     *
529
     * Use CustomLink with default behaviour to trigger this
530
     *
531
     * See:
532
     * DefaultLink::getModelLink
533
     * GridFieldCustomLink::getLink
534
     *
535
     * @param HTTPRequest $request
536
     * @return HTTPResponse|DBHTMLText|string
537
     */
538
    public function doCustomLink(HTTPRequest $request)
539
    {
540
        $action = $request->getVar('CustomLink');
541
        return $this->forwardActionToRecord($action);
542
    }
543
544
    /**
545
     * Handles custom actions
546
     *
547
     * Use CustomAction class to trigger this
548
     *
549
     * Nested actions are submitted like this
550
     * [action_doCustomAction] => Array
551
     * (
552
     *   [doTestAction] => 1
553
     * )
554
     *
555
     * @param array The form data
556
     * @param Form The form object
557
     * @return HTTPResponse|DBHTMLText|string
558
     */
559
    public function doCustomAction($data, $form)
560
    {
561
        $action = key($data['action_doCustomAction']);
562
        return $this->forwardActionToRecord($action, $data, $form);
563
    }
564
565
    /**
566
     * Saves the form and goes back to list view
567
     *
568
     * @param array The form data
569
     * @param Form The form object
570
     */
571
    public function doSaveAndClose($data, $form)
572
    {
573
        $result = $this->owner->doSave($data, $form);
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
574
        // Redirect after save
575
        $controller = $this->getToplevelController();
576
        $controller->getResponse()->addHeader("X-Pjax", "Content");
577
        return $controller->redirect($this->getBackLink());
578
    }
579
580
    /**
581
     * Saves the form and goes back to the next item
582
     *
583
     * @param array The form data
584
     * @param Form The form object
585
     */
586
    public function doSaveAndNext($data, $form)
587
    {
588
        $record = $this->owner->record;
589
        $result = $this->owner->doSave($data, $form);
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
590
        // Redirect after save
591
        $controller = $this->getToplevelController();
592
        $controller->getResponse()->addHeader("X-Pjax", "Content");
593
594
        $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

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

595
        $class = get_class(/** @scrutinizer ignore-type */ $record);
Loading history...
596
        $next = $class::get()->byID($getNextRecordID);
597
598
        $link = $this->owner->getEditLink($getNextRecordID);
599
600
        // Link to a specific tab if set, see cms-actions.js
601
        if ($next && !empty($data['_activetab'])) {
602
            $link .= '#' . $data['_activetab'];
603
        }
604
        return $controller->redirect($link);
605
    }
606
607
    /**
608
     * Saves the form and goes to the previous item
609
     *
610
     * @param array The form data
611
     * @param Form The form object
612
     */
613
    public function doSaveAndPrev($data, $form)
614
    {
615
        $record = $this->owner->record;
616
        $result = $this->owner->doSave($data, $form);
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
617
        // Redirect after save
618
        $controller = $this->getToplevelController();
619
        $controller->getResponse()->addHeader("X-Pjax", "Content");
620
621
        $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

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

622
        $class = get_class(/** @scrutinizer ignore-type */ $record);
Loading history...
623
        $prev = $class::get()->byID($getPreviousRecordID);
624
625
        $link = $this->owner->getEditLink($getPreviousRecordID);
626
627
        // Link to a specific tab if set, see cms-actions.js
628
        if ($prev && !empty($data['_activetab'])) {
629
            $link .= '#' . $data['_activetab'];
630
        }
631
        return $controller->redirect($link);
632
    }
633
634
    /**
635
     * Gets the top level controller.
636
     *
637
     * @return Controller
638
     * @todo  This had to be directly copied from {@link GridFieldDetailForm_ItemRequest}
639
     * because it is a protected method and not visible to a decorator!
640
     */
641
    protected function getToplevelController()
642
    {
643
        if ($this->owner instanceof LeftAndMain) {
644
            return $this->owner;
645
        }
646
        if (!$this->owner->hasMethod("getController")) {
647
            return Controller::curr();
648
        }
649
        $c = $this->owner->getController();
650
        while ($c && $c instanceof GridFieldDetailForm_ItemRequest) {
651
            $c = $c->getController();
652
        }
653
        return $c;
654
    }
655
656
    /**
657
     * Gets the back link
658
     *
659
     * @return string
660
     * @todo This had to be directly copied from {@link GridFieldDetailForm_ItemRequest}
661
     * because it is a protected method and not visible to a decorator!
662
     */
663
    public function getBackLink()
664
    {
665
        // TODO Coupling with CMS
666
        $backlink = '';
667
        $toplevelController = $this->getToplevelController();
668
        if ($toplevelController && $toplevelController instanceof LeftAndMain) {
669
            if ($toplevelController->hasMethod('Backlink')) {
670
                $backlink = $toplevelController->Backlink();
671
            } elseif ($this->owner->getController()->hasMethod('Breadcrumbs')) {
672
                $parents = $this->owner->getController()->Breadcrumbs(false)->items;
673
                $backlink = array_pop($parents)->Link;
674
            }
675
        }
676
        if (!$backlink) {
677
            $backlink = $toplevelController->Link();
678
        }
679
        return $backlink;
680
    }
681
682
    /**
683
     * Response object for this request after a successful save
684
     *
685
     * @param bool $isNewRecord True if this record was just created
686
     * @param DataObject $record
687
     * @return HTTPResponse|DBHTMLText|string
688
     * @todo  This had to be directly copied from {@link GridFieldDetailForm_ItemRequest}
689
     * because it is a protected method and not visible to a decorator!
690
     */
691
    protected function redirectAfterAction($isNewRecord, $record = null)
692
    {
693
        $controller = $this->getToplevelController();
694
695
        if ($controller instanceof LeftAndMain) {
696
            // CMSMain => redirect to show
697
            if ($this->owner->hasMethod("LinkPageEdit")) {
698
                return $controller->redirect($this->owner->LinkPageEdit($record->ID));
699
            }
700
            // Fallback
701
            return $controller->redirect($this->owner->Link());
702
        }
703
704
        if ($isNewRecord) {
705
            return $controller->redirect($this->owner->Link());
706
        } elseif ($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

706
        } elseif ($this->owner->gridField && $this->owner->gridField->getList()->/** @scrutinizer ignore-call */ byID($this->owner->record->ID)) {
Loading history...
707
            // Return new view, as we can't do a "virtual redirect" via the CMS Ajax
708
            // to the same URL (it assumes that its content is already current, and doesn't reload)
709
            return $this->owner->edit($controller->getRequest());
710
        } else {
711
            // Changes to the record properties might've excluded the record from
712
            // a filtered list, so return back to the main view if it can't be found
713
            $url = $controller->getRequest()->getURL();
714
            $action = $controller->getAction();
715
            $noActionURL = $url;
716
            if ($action) {
717
                $noActionURL = $controller->removeAction($url, $action);
718
            }
719
            $controller->getRequest()->addHeader('X-Pjax', 'Content');
720
            return $controller->redirect($noActionURL, 302);
721
        }
722
    }
723
}
724