Issues (48)

src/GridFieldTableButton.php (2 issues)

1
<?php
2
3
namespace LeKoala\CmsActions;
4
5
use SilverStripe\Control\HTTPResponse;
6
use ReflectionClass;
7
use SilverStripe\Control\Controller;
8
use SilverStripe\Control\Director;
9
use SilverStripe\Forms\GridField\GridField;
10
use SilverStripe\Forms\GridField\GridField_ActionProvider;
11
use SilverStripe\Forms\GridField\GridField_HTMLProvider;
12
use SilverStripe\Forms\GridField\GridField_URLHandler;
13
use SilverStripe\Core\Injector\Injectable;
14
15
/**
16
 * Provide a simple way to declare buttons that affects a whole GridField
17
 *
18
 * This implements a URL Handler that can be called by the button
19
 */
20
abstract class GridFieldTableButton implements GridField_HTMLProvider, GridField_ActionProvider, GridField_URLHandler
21
{
22
    use ProgressiveAction;
23
    use Injectable;
24
25
    /**
26
     * Fragment to write the button to
27
     * @var string
28
     */
29
    protected $targetFragment;
30
31
    /**
32
     * @var boolean
33
     */
34
    protected $noAjax = true;
35
36
    /**
37
     * @var boolean
38
     */
39
    protected $allowEmptyResponse = false;
40
41
    /**
42
     * @var string
43
     */
44
    protected $buttonLabel;
45
46
    /**
47
     * @var string
48
     */
49
    protected $fontIcon;
50
51
    /**
52
     * @var int
53
     */
54
    protected $parentID;
55
56
    /**
57
     * @var string
58
     */
59
    protected $confirm;
60
61
    /**
62
     * @var string
63
     */
64
    protected $prompt;
65
66
    /**
67
     * @var string
68
     */
69
    protected $promptDefault;
70
71
    /**
72
     * @var array<string,mixed>
73
     */
74
    protected $attributes = [];
75
76
    public bool $submitData = false;
77
78
    /**
79
     * @param string $targetFragment The HTML fragment to write the button into
80
     * @param string $buttonLabel
81
     */
82
    public function __construct($targetFragment = "buttons-before-right", $buttonLabel = null)
83
    {
84
        $this->targetFragment = $targetFragment;
85
        if ($buttonLabel) {
86
            $this->buttonLabel = $buttonLabel;
87
        }
88
    }
89
90
    /**
91
     * @return string
92
     */
93
    public function getActionName()
94
    {
95
        $class = (new ReflectionClass(get_called_class()))->getShortName();
96
97
        // ! without lowercase, it does not work
98
        return strtolower(str_replace('Button', '', $class));
99
    }
100
101
    /**
102
     * @return string
103
     */
104
    public function getButtonLabel()
105
    {
106
        return $this->buttonLabel;
107
    }
108
109
    /**
110
     * Place the export button in a <p> tag below the field
111
     * @return array<string,mixed>
112
     */
113
    public function getHTMLFragments($gridField)
114
    {
115
        $action = $this->getActionName();
116
117
        $button = CustomGridField_FormAction::create($gridField, $action, $this->getButtonLabel(), $action, []);
118
        if ($this->submitData) {
119
            $button->submitData = true;
120
        }
121
        $button->addExtraClass('btn btn-secondary action_' . $action);
122
        if ($this->noAjax) {
123
            $button->addExtraClass('no-ajax');
124
        }
125
        if ($this->fontIcon) {
126
            $button->addExtraClass('font-icon-' . $this->fontIcon);
127
        }
128
        //TODO: replace prompt and confirm with inline js
129
        if ($this->prompt) {
130
            $button->setAttribute('data-prompt', $this->prompt);
131
            $promptDefault = $this->getPromptDefault();
132
            if ($promptDefault) {
133
                $button->setAttribute('data-prompt-default', $promptDefault);
134
            }
135
        }
136
        if ($this->progressive) {
137
            $button->setProgressive(true);
138
        }
139
        if ($this->confirm) {
140
            $button->setAttribute('data-confirm', $this->confirm);
141
        }
142
        foreach ($this->attributes as $attributeName => $attributeValue) {
143
            $button->setAttribute($attributeName, $attributeValue);
144
        }
145
        $button->setForm($gridField->getForm());
146
147
        return [$this->targetFragment => $button->Field()];
148
    }
149
150
    /**
151
     * @param string $name
152
     * @param string $value
153
     * @return $this
154
     */
155
    public function setAttribute($name, $value)
156
    {
157
        $this->attributes[$name] = $value;
158
159
        return $this;
160
    }
161
162
    /**
163
     * @param string $name
164
     * @return string
165
     */
166
    public function getAttribute($name)
167
    {
168
        return $this->attributes[$name] ?? null;
169
    }
170
171
    /**
172
     * @param GridField $gridField
173
     * @return array<string>
174
     */
175
    public function getActions($gridField)
176
    {
177
        // $gridField is not used but required by parent class
178
        return [$this->getActionName()];
179
    }
180
181
    /**
182
     * @param GridField $gridField
183
     * @param string $actionName
184
     * @param array<mixed> $arguments
185
     * @param array<mixed> $data
186
     * @return array<mixed>|HTTPResponse|void
187
     */
188
    public function handleAction(GridField $gridField, $actionName, $arguments, $data)
189
    {
190
        if (in_array($actionName, $this->getActions($gridField))) {
191
            $controller = Controller::curr();
192
193
            if ($this->progressive) {
194
                // Otherwise we would need some kind of UI
195
                if (!Director::is_ajax()) {
196
                    return $controller->redirectBack();
197
                }
198
            }
199
200
            // Data should contain $_POST vars
201
            $result = $this->handle($gridField, $controller, $arguments, $data);
0 ignored issues
show
The call to LeKoala\CmsActions\GridFieldTableButton::handle() has too many arguments starting with $arguments. ( Ignorable by Annotation )

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

201
            /** @scrutinizer ignore-call */ 
202
            $result = $this->handle($gridField, $controller, $arguments, $data);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
It seems like $controller can also be of type null; however, parameter $controller of LeKoala\CmsActions\GridFieldTableButton::handle() does only seem to accept SilverStripe\Control\Controller, 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

201
            $result = $this->handle($gridField, /** @scrutinizer ignore-type */ $controller, $arguments, $data);
Loading history...
202
            if ((!$result || is_string($result)) && $this->progressive) {
203
                // simply increment counter and let's hope last action will return something
204
                $step = (int)$controller->getRequest()->postVar("progress_step");
205
                $total = (int)$controller->getRequest()->postVar("progress_total");
206
                $result = [
207
                    'progress_step'  => $step + 1,
208
                    'progress_total' => $total,
209
                    'message'        => $result,
210
                ];
211
            }
212
            if ($result) {
213
                // Send a json response this will be handled by cms-actions.js
214
                if ($this->progressive) {
215
                    $response = $controller->getResponse();
216
                    $response->addHeader('Content-Type', 'application/json');
217
                    $encodedResult = json_encode($result);
218
                    if ($encodedResult === false) {
219
                        $encodedResult = json_last_error_msg();
220
                    }
221
                    $response->setBody($encodedResult);
222
223
                    return $response;
224
                }
225
226
                return $result;
227
            }
228
229
            // This can be helpful if you want to refresh the whole form for PJAX requests
230
            if ($this->allowEmptyResponse) {
231
                return;
232
            }
233
234
            // Do something!
235
            if ($this->noAjax || !Director::is_ajax()) {
236
                return $controller->redirectBack();
237
            } else {
238
                $response = $controller->getResponse();
239
                $response->setBody($gridField->forTemplate());
240
241
                // Add default message if none set
242
                if (!$response->getHeader('X-Status')) {
243
                    $response->addHeader('X-Status', 'Action completed');
244
                }
245
                return $response;
246
            }
247
        }
248
    }
249
250
    /**
251
     * it is also a URL
252
     * @return array<string,string>
253
     */
254
    public function getURLHandlers($gridField)
255
    {
256
        return [$this->getActionName() => 'handle'];
257
    }
258
259
    /**
260
     * TODO: update the actual method with the new arguments
261
     * @param GridField $gridField
262
     * @param Controller $controller
263
     * @param array<mixed> $arguments
264
     * @param array<mixed> $data
265
     * @return mixed
266
     */
267
    abstract public function handle(GridField $gridField, Controller $controller);
268
269
    /**
270
     * Get the value of fontIcon
271
     *
272
     * @return string
273
     */
274
    public function getFontIcon()
275
    {
276
        return $this->fontIcon;
277
    }
278
279
    /**
280
     * Set the value of fontIcon
281
     *
282
     * @param string $fontIcon
283
     *
284
     * @return $this
285
     */
286
    public function setFontIcon($fontIcon)
287
    {
288
        $this->fontIcon = $fontIcon;
289
290
        return $this;
291
    }
292
293
294
    /**
295
     * Get the parent record id
296
     *
297
     * @return int
298
     */
299
    public function getParentID()
300
    {
301
        return $this->parentID;
302
    }
303
304
    /**
305
     * Set the parent record id
306
     *
307
     * @param int $id
308
     * @return $this
309
     */
310
    public function setParentID($id)
311
    {
312
        $this->parentID = $id;
313
314
        return $this;
315
    }
316
317
    /**
318
     * Get the value of confirm
319
     *
320
     * @return string
321
     */
322
    public function getConfirm()
323
    {
324
        return $this->confirm;
325
    }
326
327
    /**
328
     * Set the value of confirm
329
     *
330
     * @param string $confirm
331
     * @return $this
332
     */
333
    public function setConfirm($confirm)
334
    {
335
        $this->confirm = $confirm;
336
337
        return $this;
338
    }
339
340
    /**
341
     * Get the value of prompt
342
     *
343
     * @return string
344
     */
345
    public function getPrompt()
346
    {
347
        return $this->prompt;
348
    }
349
350
    /**
351
     * Set the value of prompt
352
     *
353
     * @param string $prompt
354
     * @return $this
355
     */
356
    public function setPrompt($prompt)
357
    {
358
        $this->prompt = $prompt;
359
360
        return $this;
361
    }
362
363
    /**
364
     * Get the value of promptDefault
365
     *
366
     * @return string
367
     */
368
    public function getPromptDefault()
369
    {
370
        return $this->promptDefault;
371
    }
372
373
    /**
374
     * Set the value of promptDefault
375
     *
376
     * @param string $promptDefault
377
     * @return $this
378
     */
379
    public function setPromptDefault($promptDefault)
380
    {
381
        $this->promptDefault = $promptDefault;
382
383
        return $this;
384
    }
385
}
386