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
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
![]() |
|||||
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 |
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.