Passed
Push — master ( 9447cf...76f63b )
by Thomas
06:46
created

ActionsGridFieldItemRequest   F

Complexity

Total Complexity 84

Size/Duplication

Total Lines 584
Duplicated Lines 0 %

Importance

Changes 7
Bugs 0 Features 1
Metric Value
eloc 241
c 7
b 0
f 1
dl 0
loc 584
rs 2
wmc 84

17 Methods

Rating   Name   Duplication   Size   Complexity  
B moveCancelAndDelete() 0 38 7
A doSaveAndClose() 0 7 1
A getBtnClassForRecord() 0 6 2
A doSaveAndNext() 0 19 3
A getCustomPreviousRecordID() 0 6 2
A doSaveAndPrev() 0 19 3
A doCustomLink() 0 4 1
A doCustomAction() 0 4 1
A getToplevelController() 0 10 4
A getBackLink() 0 17 6
F forwardActionToRecord() 0 100 22
B redirectAfterAction() 0 30 7
A getCustomNextRecordID() 0 6 2
A addSaveAndClose() 0 28 5
B updateItemEditForm() 0 58 9
B addSaveNextAndPrevious() 0 42 7
A getAvailableActions() 0 7 2

How to fix   Complexity   

Complex Class

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

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

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

1
<?php
2
3
namespace LeKoala\CmsActions;
4
5
use Exception;
6
use SilverStripe\Forms\Form;
7
use SilverStripe\ORM\DataObject;
8
use SilverStripe\Forms\FieldList;
9
use SilverStripe\Control\Director;
10
use SilverStripe\Forms\FormAction;
11
use SilverStripe\Admin\LeftAndMain;
12
use SilverStripe\ORM\DataExtension;
13
use SilverStripe\Control\Controller;
14
use SilverStripe\Control\HTTPRequest;
15
use SilverStripe\Control\HTTPResponse;
16
use SilverStripe\Core\Config\Configurable;
17
use SilverStripe\ORM\ValidationResult;
18
use SilverStripe\Forms\GridField\GridFieldDetailForm_ItemRequest;
19
20
/**
21
 * Decorates GridDetailForm_ItemRequest to use new form actions and buttons.
22
 * This is also applied to LeftAndMain to allow actions on pages
23
 *
24
 * This is a lightweight version of BetterButtons that use default getCMSActions functionnality
25
 * on DataObjects
26
 *
27
 * @link https://github.com/unclecheese/silverstripe-gridfield-betterbuttons
28
 * @link https://github.com/unclecheese/silverstripe-gridfield-betterbuttons/blob/master/src/Extensions/GridFieldBetterButtonsItemRequest.php
29
 * @property \SilverStripe\Forms\GridField\GridFieldDetailForm_ItemRequest|\SilverStripe\Admin\LeftAndMain $owner
30
 */
31
class ActionsGridFieldItemRequest extends DataExtension
32
{
33
    use Configurable;
34
35
    /**
36
     * @config
37
     * @var boolean
38
     */
39
    private static $enable_save_prev_next = true;
40
41
    /**
42
     * @config
43
     * @var boolean
44
     */
45
    private static $enable_save_close = true;
46
47
    /**
48
     * @config
49
     * @var boolean
50
     */
51
    private static $enable_delete_right = true;
52
53
    /**
54
     * @config
55
     * @var boolean
56
     */
57
    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...
58
59
    /**
60
     * @var array Allowed controller actions
61
     */
62
    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...
63
        'doSaveAndClose',
64
        'doSaveAndNext',
65
        'doSaveAndPrev',
66
        'doCustomAction', // For CustomAction
67
        'doCustomLink', // For CustomLink
68
    );
69
70
    /**
71
     * @return array
72
     */
73
    protected function getAvailableActions($actions)
74
    {
75
        $list = [];
76
        foreach ($actions as $action) {
77
            $list[] = $action->getName();
78
        }
79
        return $list;
80
    }
81
82
    /**
83
     * Updates the detail form to include new form actions and buttons
84
     *
85
     * Reorganize things a bit
86
     *
87
     * @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...
88
     */
89
    public function updateItemEditForm($form)
90
    {
91
        $itemRequest = $this->owner;
92
        $record = $itemRequest->record;
93
        if (!$record) {
94
            $record = $form->getRecord();
95
        }
96
        if (!$record) {
97
            return;
98
        }
99
100
        // We get the actions as defined on our record
101
        $CMSActions = $record->getCMSActions();
102
103
        // We can the actions from the GridFieldDetailForm_ItemRequest
104
        // It sets the Save and Delete buttons + Right Group
105
        $actions = $form->Actions();
106
107
        // The default button group that contains the Save or Create action
108
        // @link https://docs.silverstripe.org/en/4/developer_guides/customising_the_admin_interface/how_tos/extend_cms_interface/#extending-the-cms-actions
109
        $MajorActions = $actions->fieldByName('MajorActions');
110
111
        // If it doesn't exist, push to default group
112
        if (!$MajorActions) {
113
            $MajorActions = $actions;
0 ignored issues
show
Unused Code introduced by
The assignment to $MajorActions is dead and can be removed.
Loading history...
114
        }
115
116
        // Push our actions that are otherwise ignored by SilverStripe
117
        foreach ($CMSActions as $action) {
118
            $actions->push($action);
119
        }
120
121
        // Add extension hook
122
        $record->extend('onBeforeUpdateCMSActions', $actions);
123
124
        // We have a 4.4 setup, before that there was no RightGroup
125
        $RightGroup = $actions->fieldByName('RightGroup');
126
127
        // Insert again to make sure our actions are properly placed after apply changes
128
        if ($RightGroup) {
129
            $actions->remove($RightGroup);
130
            $actions->push($RightGroup);
131
        }
132
133
        if (self::config()->enable_save_close) {
134
            $this->addSaveAndClose($actions, $record);
135
        }
136
137
        if (self::config()->enable_save_prev_next) {
138
            $this->addSaveNextAndPrevious($actions, $record);
139
        }
140
141
        if (self::config()->enable_delete_right) {
142
            $this->moveCancelAndDelete($actions, $record);
143
        }
144
145
        // Add extension hook
146
        $record->extend('onAfterUpdateCMSActions', $actions);
147
    }
148
149
    /**
150
     * @param FieldList $actions
151
     * @param DataObject $record
152
     * @return void
153
     */
154
    public function moveCancelAndDelete(FieldList $actions, DataObject $record)
155
    {
156
        // We have a 4.4 setup, before that there was no RightGroup
157
        $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...
158
159
        // Move delete at the end
160
        $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...
161
        if ($deleteAction) {
0 ignored issues
show
introduced by
$deleteAction is of type null, thus it always evaluated to false.
Loading history...
162
            // Move at the end of the stack
163
            $actions->remove($deleteAction);
164
            $actions->push($deleteAction);
165
166
            if ($RightGroup) {
167
                // Stack position is enough to have it on the left
168
            } else {
169
                // Only necessary pre 4.4
170
                $deleteAction->addExtraClass('align-right');
171
            }
172
            // Set custom titlte
173
            if ($record->hasMethod('getDeleteButtonTitle')) {
174
                $deleteAction->setTitle($record->getDeleteButtonTitle());
175
            }
176
        }
177
        // Move cancel at the end
178
        $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...
179
        if ($cancelButton) {
0 ignored issues
show
introduced by
$cancelButton is of type null, thus it always evaluated to false.
Loading history...
180
            // Move at the end of the stack
181
            $actions->remove($cancelButton);
182
            $actions->push($cancelButton);
183
            if ($RightGroup) {
184
                // Stack position is enough to have it on the left
185
            } else {
186
                // Only necessary pre 4.4
187
                $cancelButton->addExtraClass('align-right');
188
            }
189
            // Set custom titlte
190
            if ($record->hasMethod('getCancelButtonTitle')) {
191
                $cancelButton->setTitle($record->getCancelButtonTitle());
192
            }
193
        }
194
    }
195
196
    public function getCustomPreviousRecordID(DataObject $record)
197
    {
198
        if ($record->hasMethod('PrevRecord')) {
199
            return $record->PrevRecord()->ID ?? 0;
200
        }
201
        $this->owner->getPreviousRecordID();
202
    }
203
204
    public function getCustomNextRecordID(DataObject $record)
205
    {
206
        if ($record->hasMethod('NextRecord')) {
207
            return $record->NextRecord()->ID ?? 0;
208
        }
209
        $this->owner->getNextRecordID();
210
    }
211
212
    /**
213
     * @param FieldList $actions
214
     * @param DataObject $record
215
     * @return void
216
     */
217
    public function addSaveNextAndPrevious(FieldList $actions, DataObject $record)
218
    {
219
        if (!$record->canEdit()) {
220
            return;
221
        }
222
        if (!$record->ID) {
223
            return;
224
        }
225
226
        $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...
227
228
        // If it doesn't exist, push to default group
229
        if (!$MajorActions) {
0 ignored issues
show
introduced by
$MajorActions is of type null, thus it always evaluated to false.
Loading history...
230
            $MajorActions = $actions;
231
        }
232
233
        // TODO: check why with paginator, after the first page, getPreviousRecordID/getNextRecordID tend to not work properly
234
        $getPreviousRecordID = $this->getCustomPreviousRecordID($record);
235
        $getNextRecordID = $this->getCustomNextRecordID($record);
236
237
        // Coupling for HasPrevNextUtils
238
        if (Controller::has_curr()) {
239
            $request =  Controller::curr()->getRequest();
240
            $routeParams = $request->routeParams();
241
            $routeParams['PreviousRecordID'] = $getPreviousRecordID;
242
            $routeParams['NextRecordID'] = $getNextRecordID;
243
            $request->setRouteParams($routeParams);
244
        }
245
246
        if ($getPreviousRecordID) {
247
            $doSaveAndPrev = new FormAction('doSaveAndPrev', _t('ActionsGridFieldItemRequest.SAVEANDPREVIOUS', 'Save and Previous'));
248
            $doSaveAndPrev->addExtraClass($this->getBtnClassForRecord($record));
249
            $doSaveAndPrev->addExtraClass('font-icon-angle-double-left');
250
            $doSaveAndPrev->setUseButtonTag(true);
251
            $MajorActions->push($doSaveAndPrev);
252
        }
253
        if ($getNextRecordID) {
254
            $doSaveAndNext = new FormAction('doSaveAndNext', _t('ActionsGridFieldItemRequest.SAVEANDNEXT', 'Save and Next'));
255
            $doSaveAndNext->addExtraClass($this->getBtnClassForRecord($record));
256
            $doSaveAndNext->addExtraClass('font-icon-angle-double-right');
257
            $doSaveAndNext->setUseButtonTag(true);
258
            $MajorActions->push($doSaveAndNext);
259
        }
260
    }
261
262
    /**
263
     * @param FieldList $actions
264
     * @param DataObject $record
265
     * @return void
266
     */
267
    public function addSaveAndClose(FieldList $actions, DataObject $record)
268
    {
269
        if (!$record->canEdit()) {
270
            return;
271
        }
272
273
        $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...
274
275
        // If it doesn't exist, push to default group
276
        if (!$MajorActions) {
0 ignored issues
show
introduced by
$MajorActions is of type null, thus it always evaluated to false.
Loading history...
277
            $MajorActions = $actions;
278
        }
279
280
        if ($record->ID) {
281
            $label = _t('ActionsGridFieldItemRequest.SAVEANDCLOSE', 'Save and Close');
282
        } else {
283
            $label = _t('ActionsGridFieldItemRequest.CREATEANDCLOSE', 'Create and Close');
284
        }
285
        $saveAndClose = new FormAction('doSaveAndClose', $label);
286
        $saveAndClose->addExtraClass($this->getBtnClassForRecord($record));
287
        $saveAndClose->setAttribute('data-text-alternate', $label);
288
        if ($record->ID) {
289
            $saveAndClose->setAttribute('data-btn-alternate-add', 'btn-primary');
290
            $saveAndClose->setAttribute('data-btn-alternate-remove', 'btn-outline-primary');
291
        }
292
        $saveAndClose->addExtraClass('font-icon-level-up');
293
        $saveAndClose->setUseButtonTag(true);
294
        $MajorActions->push($saveAndClose);
295
    }
296
297
    /**
298
     * New and existing records have different classes
299
     *
300
     * @param DataObject $record
301
     * @return string
302
     */
303
    protected function getBtnClassForRecord(DataObject $record)
304
    {
305
        if ($record->ID) {
306
            return 'btn-outline-primary';
307
        }
308
        return 'btn-primary';
309
    }
310
311
    /**
312
     * Forward a given action to a DataObject
313
     *
314
     * Action must be declared in getCMSActions to be called
315
     *
316
     * @param string $action
317
     * @param array $data
318
     * @param Form $form
319
     * @return HTTPResponse|DBHTMLText
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...
320
     */
321
    protected function forwardActionToRecord($action, $data = [], $form = null)
322
    {
323
        $controller = $this->getToplevelController();
324
        if ($controller instanceof LeftAndMain) {
325
            if (empty($data['ClassName']) || empty($data['ID'])) {
326
                throw new Exception("Submitted data does not contain and ID and a ClassName");
327
            }
328
            $record = DataObject::get_by_id($data['ClassName'], $data['ID']);
329
        } else {
330
            $record = $this->owner->record;
331
        }
332
        if (!$record) {
333
            throw new Exception("No record to handle the action $action");
334
        }
335
        $definedActions = $record->getCMSActions();
336
        // Check if the action is indeed available
337
        $clickedAction = null;
338
        if (!empty($definedActions)) {
339
            foreach ($definedActions as $definedAction) {
340
                $definedActionName = $definedAction->getName();
341
                if ($definedAction->hasMethod('actionName')) {
342
                    $definedActionName = $definedAction->actionName();
343
                }
344
                if ($definedActionName == $action) {
345
                    $clickedAction = $definedAction;
346
                }
347
            }
348
        }
349
        if (!$clickedAction) {
350
            $class = get_class($record);
351
            return $this->owner->httpError(403, 'Action not available on ' . $class . '. It must be one of :' . implode(',', $this->getAvailableActions($definedActions)));
352
        }
353
        $message = null;
354
        $error = false;
355
356
        // Check record BEFORE the action
357
        // It can be deleted by the action and it will return to the list
358
        $isNewRecord = $record->ID == 0;
359
360
        try {
361
            $result = $record->$action($data, $form, $controller);
362
363
            // We have a response
364
            if ($result && $result instanceof HTTPResponse) {
365
                return $result;
366
            }
367
368
            if ($result === false) {
369
                // Result returned an error (false)
370
                $error = true;
371
                $message = _t(
372
                    'ActionsGridFieldItemRequest.FAILED',
373
                    'Action {action} failed on {name}',
374
                    array(
375
                        'action' => $clickedAction->getTitle(),
376
                        'name' => $record->i18n_singular_name(),
377
                    )
378
                );
379
            } elseif (is_string($result)) {
380
                // Result is a message
381
                $message = $result;
382
            }
383
        } catch (Exception $ex) {
384
            $error = true;
385
            $message = $ex->getMessage();
386
        }
387
388
        // Build default message
389
        if (!$message) {
390
            $message = _t(
391
                'ActionsGridFieldItemRequest.DONE',
392
                'Action {action} was done on {name}',
393
                array(
394
                    'action' => $clickedAction->getTitle(),
395
                    'name' => $record->i18n_singular_name(),
396
                )
397
            );
398
        }
399
        $status = 'good';
400
        if ($error) {
401
            $status = 'bad';
402
        }
403
        // We don't have a form, simply return the result
404
        if (!$form) {
405
            if ($error) {
406
                return $this->owner->httpError(403, $message);
407
            }
408
            return $message;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $message returns the type string which is incompatible with the documented return type LeKoala\CmsActions\DBHTM...pe\Control\HTTPResponse.
Loading history...
409
        }
410
        if (Director::is_ajax()) {
411
            $controller = $this->getToplevelController();
412
            $controller->getResponse()->addHeader('X-Status', rawurlencode($message));
413
            if (method_exists($clickedAction, 'getShouldRefresh') && $clickedAction->getShouldRefresh()) {
414
                $controller->getResponse()->addHeader('X-Reload', true);
0 ignored issues
show
Bug introduced by
true of type true is incompatible with the type string expected by parameter $value of SilverStripe\Control\HTTPResponse::addHeader(). ( Ignorable by Annotation )

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

414
                $controller->getResponse()->addHeader('X-Reload', /** @scrutinizer ignore-type */ true);
Loading history...
415
            }
416
        } else {
417
            $form->sessionMessage($message, $status, ValidationResult::CAST_HTML);
418
        }
419
        // Redirect after action
420
        return $this->redirectAfterAction($isNewRecord, $record);
421
    }
422
423
    /**
424
     * Handles custom links
425
     *
426
     * Use CustomLink with default behaviour to trigger this
427
     *
428
     * See:
429
     * DefaultLink::getModelLink
430
     * GridFieldCustomLink::getLink
431
     *
432
     * @param HTTPRequest $request
433
     * @return HTTPResponse|DBHTMLText
434
     */
435
    public function doCustomLink(HTTPRequest $request)
436
    {
437
        $action = $request->getVar('CustomLink');
438
        return $this->forwardActionToRecord($action);
439
    }
440
441
    /**
442
     * Handles custom actions
443
     *
444
     * Use CustomAction class to trigger this
445
     *
446
     * Nested actions are submitted like this
447
     * [action_doCustomAction] => Array
448
     * (
449
     *   [doTestAction] => 1
450
     * )
451
     *
452
     * @param array The form data
453
     * @param Form The form object
454
     * @return HTTPResponse|DBHTMLText
455
     */
456
    public function doCustomAction($data, $form)
457
    {
458
        $action = key($data['action_doCustomAction']);
459
        return $this->forwardActionToRecord($action, $data, $form);
460
    }
461
462
    /**
463
     * Saves the form and goes back to list view
464
     *
465
     * @param array The form data
466
     * @param Form The form object
467
     */
468
    public function doSaveAndClose($data, $form)
469
    {
470
        $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...
471
        // Redirect after save
472
        $controller = $this->getToplevelController();
473
        $controller->getResponse()->addHeader("X-Pjax", "Content");
474
        return $controller->redirect($this->getBackLink());
475
    }
476
477
    /**
478
     * Saves the form and goes back to the next item
479
     *
480
     * @param array The form data
481
     * @param Form The form object
482
     */
483
    public function doSaveAndNext($data, $form)
484
    {
485
        $record = $this->owner->record;
486
        $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...
487
        // Redirect after save
488
        $controller = $this->getToplevelController();
489
        $controller->getResponse()->addHeader("X-Pjax", "Content");
490
491
        $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

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

492
        $class = get_class(/** @scrutinizer ignore-type */ $record);
Loading history...
493
        $next = $class::get()->byID($getNextRecordID);
494
495
        $link = $this->owner->getEditLink($getNextRecordID);
496
497
        // Link to a specific tab if set, see cms-actions.js
498
        if ($next && !empty($data['_activetab'])) {
499
            $link .= '#' . $data['_activetab'];
500
        }
501
        return $controller->redirect($link);
502
    }
503
504
    /**
505
     * Saves the form and goes to the previous item
506
     *
507
     * @param array The form data
508
     * @param Form The form object
509
     */
510
    public function doSaveAndPrev($data, $form)
511
    {
512
        $record = $this->owner->record;
513
        $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...
514
        // Redirect after save
515
        $controller = $this->getToplevelController();
516
        $controller->getResponse()->addHeader("X-Pjax", "Content");
517
518
        $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

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

519
        $class = get_class(/** @scrutinizer ignore-type */ $record);
Loading history...
520
        $prev = $class::get()->byID($getPreviousRecordID);
521
522
        $link = $this->owner->getEditLink($getPreviousRecordID);
523
524
        // Link to a specific tab if set, see cms-actions.js
525
        if ($prev && !empty($data['_activetab'])) {
526
            $link .= '#' . $data['_activetab'];
527
        }
528
        return $controller->redirect($link);
529
    }
530
531
    /**
532
     * Gets the top level controller.
533
     *
534
     * @return Controller
535
     * @todo  This had to be directly copied from {@link GridFieldDetailForm_ItemRequest}
536
     * because it is a protected method and not visible to a decorator!
537
     */
538
    protected function getToplevelController()
539
    {
540
        if (!$this->owner->hasMethod("getController")) {
541
            return Controller::curr();
542
        }
543
        $c = $this->owner->getController();
544
        while ($c && $c instanceof GridFieldDetailForm_ItemRequest) {
545
            $c = $c->getController();
546
        }
547
        return $c;
548
    }
549
550
    /**
551
     * Gets the back link
552
     *
553
     * @return string
554
     * @todo This had to be directly copied from {@link GridFieldDetailForm_ItemRequest}
555
     * because it is a protected method and not visible to a decorator!
556
     */
557
    public function getBackLink()
558
    {
559
        // TODO Coupling with CMS
560
        $backlink = '';
561
        $toplevelController = $this->getToplevelController();
562
        if ($toplevelController && $toplevelController instanceof LeftAndMain) {
563
            if ($toplevelController->hasMethod('Backlink')) {
564
                $backlink = $toplevelController->Backlink();
565
            } elseif ($this->owner->getController()->hasMethod('Breadcrumbs')) {
566
                $parents = $this->owner->getController()->Breadcrumbs(false)->items;
567
                $backlink = array_pop($parents)->Link;
568
            }
569
        }
570
        if (!$backlink) {
571
            $backlink = $toplevelController->Link();
572
        }
573
        return $backlink;
574
    }
575
576
    /**
577
     * Response object for this request after a successful save
578
     *
579
     * @param bool $isNewRecord True if this record was just created
580
     * @param DataObject $record
581
     * @return HTTPResponse|DBHTMLText
582
     * @todo  This had to be directly copied from {@link GridFieldDetailForm_ItemRequest}
583
     * because it is a protected method and not visible to a decorator!
584
     */
585
    protected function redirectAfterAction($isNewRecord, $record = null)
586
    {
587
        $controller = $this->getToplevelController();
588
589
        if ($controller instanceof LeftAndMain) {
590
            // CMSMain => redirect to show
591
            if ($this->owner->hasMethod("LinkPageEdit")) {
592
                return $controller->redirect($this->owner->LinkPageEdit($record->ID));
0 ignored issues
show
Bug Best Practice introduced by
The expression return $controller->redi...kPageEdit($record->ID)) also could return the type string which is incompatible with the documented return type LeKoala\CmsActions\DBHTM...pe\Control\HTTPResponse.
Loading history...
593
            }
594
            // Fallback
595
            return $controller->redirect($this->owner->Link());
0 ignored issues
show
Bug Best Practice introduced by
The expression return $controller->redi...t($this->owner->Link()) also could return the type string which is incompatible with the documented return type LeKoala\CmsActions\DBHTM...pe\Control\HTTPResponse.
Loading history...
596
        }
597
598
        if ($isNewRecord) {
599
            return $controller->redirect($this->owner->Link());
600
        } 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

600
        } elseif ($this->owner->gridField && $this->owner->gridField->getList()->/** @scrutinizer ignore-call */ byID($this->owner->record->ID)) {
Loading history...
601
            // Return new view, as we can't do a "virtual redirect" via the CMS Ajax
602
            // to the same URL (it assumes that its content is already current, and doesn't reload)
603
            return $this->owner->edit($controller->getRequest());
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->owner->edi...ntroller->getRequest()) also could return the type SilverStripe\ORM\FieldTy...ViewableData_Customised which is incompatible with the documented return type LeKoala\CmsActions\DBHTM...pe\Control\HTTPResponse.
Loading history...
604
        } else {
605
            // Changes to the record properties might've excluded the record from
606
            // a filtered list, so return back to the main view if it can't be found
607
            $url = $controller->getRequest()->getURL();
608
            $action = $controller->getAction();
609
            $noActionURL = $url;
610
            if ($action) {
611
                $noActionURL = $controller->removeAction($url, $action);
612
            }
613
            $controller->getRequest()->addHeader('X-Pjax', 'Content');
614
            return $controller->redirect($noActionURL, 302);
615
        }
616
    }
617
}
618