Passed
Push — master ( 5990f5...4e7a51 )
by Thomas
02:53 queued 12s
created

ActionsGridFieldItemRequest::processDropUpMenu()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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

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

586
        $class = get_class(/** @scrutinizer ignore-type */ $record);
Loading history...
587
        $next = $class::get()->byID($getNextRecordID);
588
589
        $link = $this->owner->getEditLink($getNextRecordID);
590
591
        // Link to a specific tab if set, see cms-actions.js
592
        if ($next && !empty($data['_activetab'])) {
593
            $link .= '#' . $data['_activetab'];
594
        }
595
        return $controller->redirect($link);
596
    }
597
598
    /**
599
     * Saves the form and goes to the previous item
600
     *
601
     * @param array The form data
602
     * @param Form The form object
603
     */
604
    public function doSaveAndPrev($data, $form)
605
    {
606
        $record = $this->owner->record;
607
        $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...
608
        // Redirect after save
609
        $controller = $this->getToplevelController();
610
        $controller->getResponse()->addHeader("X-Pjax", "Content");
611
612
        $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

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

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

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