1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace LeKoala\Tabulator; |
4
|
|
|
|
5
|
|
|
use Exception; |
6
|
|
|
use SilverStripe\ORM\DB; |
7
|
|
|
use SilverStripe\Forms\Form; |
8
|
|
|
use SilverStripe\ORM\ArrayList; |
9
|
|
|
use SilverStripe\View\SSViewer; |
10
|
|
|
use SilverStripe\Control\Cookie; |
11
|
|
|
use SilverStripe\ORM\DataObject; |
12
|
|
|
use SilverStripe\View\ArrayData; |
13
|
|
|
use SilverStripe\ORM\HasManyList; |
14
|
|
|
use SilverStripe\Control\Director; |
15
|
|
|
use SilverStripe\ORM\ManyManyList; |
16
|
|
|
use SilverStripe\ORM\RelationList; |
17
|
|
|
use SilverStripe\Control\Controller; |
18
|
|
|
use SilverStripe\Control\HTTPRequest; |
19
|
|
|
use SilverStripe\Control\HTTPResponse; |
20
|
|
|
use SilverStripe\ORM\ValidationResult; |
21
|
|
|
use SilverStripe\Control\RequestHandler; |
22
|
|
|
use SilverStripe\Security\SecurityToken; |
23
|
|
|
use SilverStripe\ORM\ValidationException; |
24
|
|
|
use SilverStripe\Forms\GridField\GridFieldDetailForm_ItemRequest; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* Endpoint for actions related to a specific record |
28
|
|
|
* |
29
|
|
|
* It also allows to display a form to edit this record |
30
|
|
|
*/ |
31
|
|
|
class TabulatorGrid_ItemRequest extends RequestHandler |
32
|
|
|
{ |
33
|
|
|
|
34
|
|
|
private static $allowed_actions = [ |
35
|
|
|
'edit', |
36
|
|
|
'ajaxEdit', |
37
|
|
|
'ajaxMove', |
38
|
|
|
'view', |
39
|
|
|
'delete', |
40
|
|
|
'unlink', |
41
|
|
|
'customAction', |
42
|
|
|
'ItemEditForm', |
43
|
|
|
]; |
44
|
|
|
|
45
|
|
|
protected TabulatorGrid $tabulatorGrid; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* @var DataObject |
49
|
|
|
*/ |
50
|
|
|
protected $record; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* @var array |
54
|
|
|
*/ |
55
|
|
|
protected $manipulatedData = null; |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* This represents the current parent RequestHandler (which does not necessarily need to be a Controller). |
59
|
|
|
* It allows us to traverse the RequestHandler chain upwards to reach the Controller stack. |
60
|
|
|
* |
61
|
|
|
* @var RequestHandler |
62
|
|
|
*/ |
63
|
|
|
protected $popupController; |
64
|
|
|
|
65
|
|
|
protected string $hash = ''; |
66
|
|
|
|
67
|
|
|
protected string $template = ''; |
68
|
|
|
|
69
|
|
|
private static $url_handlers = [ |
70
|
|
|
'customAction/$CustomAction' => 'customAction', |
71
|
|
|
'$Action!' => '$Action', |
72
|
|
|
'' => 'edit', |
73
|
|
|
]; |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* |
77
|
|
|
* @param TabulatorGrid $tabulatorGrid |
78
|
|
|
* @param DataObject $record |
79
|
|
|
* @param RequestHandler $requestHandler |
80
|
|
|
*/ |
81
|
|
|
public function __construct($tabulatorGrid, $record, $requestHandler) |
82
|
|
|
{ |
83
|
|
|
$this->tabulatorGrid = $tabulatorGrid; |
84
|
|
|
$this->record = $record; |
85
|
|
|
$this->popupController = $requestHandler; |
86
|
|
|
parent::__construct(); |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
public function Link($action = null) |
90
|
|
|
{ |
91
|
|
|
return Controller::join_links( |
92
|
|
|
$this->tabulatorGrid->Link('item'), |
93
|
|
|
$this->record->ID ? $this->record->ID : 'new', |
94
|
|
|
$action |
95
|
|
|
); |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
public function AbsoluteLink($action = null) |
99
|
|
|
{ |
100
|
|
|
return Director::absoluteURL($this->Link($action)); |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
protected function getManipulatedData(): array |
104
|
|
|
{ |
105
|
|
|
if ($this->manipulatedData) { |
|
|
|
|
106
|
|
|
return $this->manipulatedData; |
107
|
|
|
} |
108
|
|
|
$grid = $this->getTabulatorGrid(); |
109
|
|
|
|
110
|
|
|
$state = $grid->getState($this->popupController->getRequest()); |
111
|
|
|
|
112
|
|
|
$currentPage = $state['page']; |
113
|
|
|
$itemsPerPage = $state['limit']; |
114
|
|
|
|
115
|
|
|
$limit = $itemsPerPage + 2; |
116
|
|
|
$offset = max(0, $itemsPerPage * ($currentPage - 1) - 1); |
117
|
|
|
|
118
|
|
|
$list = $grid->getManipulatedData($limit, $offset, $state['sort'], $state['filter']); |
119
|
|
|
|
120
|
|
|
$this->manipulatedData = $list; |
121
|
|
|
return $list; |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
public function index(HTTPRequest $request) |
125
|
|
|
{ |
126
|
|
|
$controller = $this->getToplevelController(); |
127
|
|
|
return $controller->redirect($this->Link('edit')); |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
protected function returnWithinContext(HTTPRequest $request, RequestHandler $controller, Form $form) |
131
|
|
|
{ |
132
|
|
|
$data = $this->customise([ |
133
|
|
|
'Backlink' => $controller->hasMethod('Backlink') ? $controller->Backlink() : $controller->Link(), |
134
|
|
|
'ItemEditForm' => $form, |
135
|
|
|
]); |
136
|
|
|
|
137
|
|
|
$return = $data->renderWith('LeKoala\\Tabulator\\TabulatorGrid_ItemEditForm'); |
138
|
|
|
if ($request->isAjax()) { |
139
|
|
|
return $return; |
140
|
|
|
} |
141
|
|
|
// If not requested by ajax, we need to render it within the controller context+template |
142
|
|
|
return $controller->customise([ |
143
|
|
|
'Content' => $return, |
144
|
|
|
]); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
protected function editFailure(): HTTPResponse |
148
|
|
|
{ |
149
|
|
|
return $this->httpError(403, _t( |
150
|
|
|
__CLASS__ . '.EditPermissionsFailure', |
151
|
|
|
'It seems you don\'t have the necessary permissions to edit "{ObjectTitle}"', |
152
|
|
|
['ObjectTitle' => $this->record->singular_name()] |
153
|
|
|
)); |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
/** |
157
|
|
|
* This is responsible to display an edit form, like GridFieldDetailForm, but much simpler |
158
|
|
|
* |
159
|
|
|
* @return mixed |
160
|
|
|
*/ |
161
|
|
|
public function edit(HTTPRequest $request) |
162
|
|
|
{ |
163
|
|
|
if (!$this->record->canEdit()) { |
164
|
|
|
return $this->editFailure(); |
165
|
|
|
} |
166
|
|
|
$controller = $this->getToplevelController(); |
167
|
|
|
|
168
|
|
|
$form = $this->ItemEditForm(); |
169
|
|
|
|
170
|
|
|
return $this->returnWithinContext($request, $controller, $form); |
|
|
|
|
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
public function ajaxEdit(HTTPRequest $request) |
174
|
|
|
{ |
175
|
|
|
$SecurityID = $request->postVar('SecurityID'); |
176
|
|
|
if (!SecurityToken::inst()->check($SecurityID)) { |
177
|
|
|
return $this->httpError(404, "Invalid SecurityID"); |
178
|
|
|
} |
179
|
|
|
if (!$this->record->canEdit()) { |
180
|
|
|
return $this->editFailure(); |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
$preventEmpty = []; |
184
|
|
|
if ($this->record->hasMethod('tabulatorPreventEmpty')) { |
185
|
|
|
$preventEmpty = $this->record->tabulatorPreventEmpty(); |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
$Data = $request->postVar("Data"); |
|
|
|
|
189
|
|
|
$Column = $request->postVar("Column"); |
190
|
|
|
$Value = $request->postVar("Value"); |
191
|
|
|
|
192
|
|
|
if (!$Value && in_array($Column, $preventEmpty)) { |
193
|
|
|
return $this->httpError(400, _t(__CLASS__ . '.ValueCannotBeEmpty', 'Value cannot be empty')); |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
try { |
197
|
|
|
$updatedValue = $this->executeEdit($Column, $Value); |
|
|
|
|
198
|
|
|
} catch (Exception $e) { |
199
|
|
|
return $this->httpError(400, $e->getMessage()); |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
$response = new HTTPResponse(json_encode([ |
203
|
|
|
'success' => true, |
204
|
|
|
'message' => _t(__CLASS__ . '.RecordEdited', 'Record edited'), |
205
|
|
|
'value' => $updatedValue, |
206
|
|
|
])); |
207
|
|
|
$response->addHeader('Content-Type', 'application/json'); |
208
|
|
|
return $response; |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
public function executeEdit(string $Column, $Value) |
212
|
|
|
{ |
213
|
|
|
$field = $Column; |
214
|
|
|
$rel = $relField = null; |
215
|
|
|
if (strpos($Column, ".") !== false) { |
216
|
|
|
$parts = explode(".", $Column); |
217
|
|
|
$rel = $parts[0]; |
218
|
|
|
$relField = $parts[1]; |
219
|
|
|
$field = $rel . "ID"; |
220
|
|
|
if (!is_numeric($Value)) { |
221
|
|
|
return $this->httpError(400, "ID must have a numerical value"); |
222
|
|
|
} |
223
|
|
|
} |
224
|
|
|
if (!$field) { |
225
|
|
|
return $this->httpError(400, "Field must not be empty"); |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
$grid = $this->tabulatorGrid; |
229
|
|
|
|
230
|
|
|
$list = $grid->getDataList(); |
231
|
|
|
|
232
|
|
|
if ($list instanceof ManyManyList) { |
233
|
|
|
$extraData = $list->getExtraData($grid->getName(), $this->record->ID); |
234
|
|
|
|
235
|
|
|
// Is it a many many field |
236
|
|
|
// This is a bit basic but it works |
237
|
|
|
if (isset($extraData[$Column])) { |
238
|
|
|
$extra = [ |
239
|
|
|
$Column => $Value |
240
|
|
|
]; |
241
|
|
|
$list->add($this->record, $extra); |
242
|
|
|
return $Value; |
243
|
|
|
} |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
|
247
|
|
|
// Its on the object itself |
248
|
|
|
$this->record->$field = $Value; |
249
|
|
|
$this->record->write(); |
250
|
|
|
$updatedValue = $this->record->$field; |
251
|
|
|
if ($rel) { |
252
|
|
|
/** @var DataObject $relObject */ |
253
|
|
|
$relObject = $this->record->$rel(); |
254
|
|
|
$updatedValue = $relObject->relField($relField); |
255
|
|
|
} |
256
|
|
|
return $updatedValue; |
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
public function ajaxMove(HTTPRequest $request) |
260
|
|
|
{ |
261
|
|
|
$SecurityID = $request->postVar('SecurityID'); |
262
|
|
|
if (!SecurityToken::inst()->check($SecurityID)) { |
263
|
|
|
return $this->httpError(404, "Invalid SecurityID"); |
264
|
|
|
} |
265
|
|
|
if (!$this->record->canEdit()) { |
266
|
|
|
return $this->editFailure(); |
267
|
|
|
} |
268
|
|
|
$Data = $request->postVar("Data"); |
269
|
|
|
if (is_string($Data)) { |
270
|
|
|
$Data = json_decode($Data, JSON_OBJECT_AS_ARRAY); |
|
|
|
|
271
|
|
|
} |
272
|
|
|
$Sort = $request->postVar("Sort"); |
273
|
|
|
|
274
|
|
|
try { |
275
|
|
|
$updatedSort = $this->executeSort($Data, $Sort); |
|
|
|
|
276
|
|
|
} catch (Exception $e) { |
277
|
|
|
return $this->httpError(400, $e->getMessage()); |
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
$response = new HTTPResponse(json_encode([ |
281
|
|
|
'success' => true, |
282
|
|
|
'message' => _t(__CLASS__ . '.RecordMove', 'Record moved'), |
283
|
|
|
'value' => $updatedSort, |
284
|
|
|
])); |
285
|
|
|
$response->addHeader('Content-Type', 'application/json'); |
286
|
|
|
return $response; |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
public function executeSort(array $Data, int $Sort, string $sortField = 'Sort'): int |
290
|
|
|
{ |
291
|
|
|
$table = DataObject::getSchema()->baseDataTable(get_class($this->record)); |
292
|
|
|
|
293
|
|
|
if (!isset($Data[$sortField])) { |
294
|
|
|
return $this->httpError(403, _t( |
295
|
|
|
__CLASS__ . '.UnableToResolveSort', |
296
|
|
|
'Unable to resolve previous sort order' |
297
|
|
|
)); |
298
|
|
|
} |
299
|
|
|
|
300
|
|
|
$prevSort = $Data[$sortField]; |
301
|
|
|
|
302
|
|
|
// Just make sure you don't have 0 (except first record) or equal sorts |
303
|
|
|
if ($prevSort < $Sort) { |
304
|
|
|
$set = "$sortField = $sortField - 1"; |
305
|
|
|
$where = "$sortField > $prevSort and $sortField <= $Sort"; |
306
|
|
|
} else { |
307
|
|
|
$set = "$sortField = $sortField + 1"; |
308
|
|
|
$where = "$sortField < $prevSort and $sortField >= $Sort"; |
309
|
|
|
} |
310
|
|
|
DB::query("UPDATE `$table` SET $set WHERE $where"); |
311
|
|
|
$this->record->$sortField = $Sort; |
312
|
|
|
$this->record->write(); |
313
|
|
|
|
314
|
|
|
return $this->record->Sort; |
|
|
|
|
315
|
|
|
} |
316
|
|
|
|
317
|
|
|
|
318
|
|
|
/** |
319
|
|
|
* Delete from the row level |
320
|
|
|
* |
321
|
|
|
* @param HTTPRequest $request |
322
|
|
|
* @return void |
323
|
|
|
*/ |
324
|
|
|
public function delete(HTTPRequest $request) |
325
|
|
|
{ |
326
|
|
|
if (!$this->record->canDelete()) { |
327
|
|
|
return $this->httpError(403, _t( |
328
|
|
|
__CLASS__ . '.DeletePermissionsFailure', |
329
|
|
|
'It seems you don\'t have the necessary permissions to delete "{ObjectTitle}"', |
330
|
|
|
['ObjectTitle' => $this->record->singular_name()] |
331
|
|
|
)); |
332
|
|
|
} |
333
|
|
|
|
334
|
|
|
$title = $this->record->getTitle(); |
335
|
|
|
$this->record->delete(); |
336
|
|
|
|
337
|
|
|
$message = _t( |
338
|
|
|
'SilverStripe\\Forms\\GridField\\GridFieldDetailForm.Deleted', |
339
|
|
|
'Deleted {type} {name}', |
340
|
|
|
[ |
341
|
|
|
'type' => $this->record->i18n_singular_name(), |
342
|
|
|
'name' => htmlspecialchars($title, ENT_QUOTES) |
343
|
|
|
] |
344
|
|
|
); |
345
|
|
|
|
346
|
|
|
//when an item is deleted, redirect to the parent controller |
347
|
|
|
$controller = $this->getToplevelController(); |
348
|
|
|
|
349
|
|
|
if ($this->isSilverStripeAdmin($controller)) { |
350
|
|
|
$controller->getRequest()->addHeader('X-Pjax', 'Content'); |
351
|
|
|
} |
352
|
|
|
|
353
|
|
|
//redirect back to admin section |
354
|
|
|
$response = $controller->redirect($this->getBackLink(), 302); |
355
|
|
|
|
356
|
|
|
$this->sessionMessage($message, "good"); |
357
|
|
|
|
358
|
|
|
return $response; |
|
|
|
|
359
|
|
|
} |
360
|
|
|
|
361
|
|
|
/** |
362
|
|
|
* Unlink from the row level |
363
|
|
|
* |
364
|
|
|
* @param HTTPRequest $request |
365
|
|
|
* @return void |
366
|
|
|
*/ |
367
|
|
|
public function unlink(HTTPRequest $request) |
368
|
|
|
{ |
369
|
|
|
//TODO: isn't that too strict?? |
370
|
|
|
if (!$this->record->canDelete()) { |
371
|
|
|
return $this->httpError(403, _t( |
372
|
|
|
__CLASS__ . '.UnlinkPermissionsFailure', |
373
|
|
|
'It seems you don\'t have the necessary permissions to unlink "{ObjectTitle}"', |
374
|
|
|
['ObjectTitle' => $this->record->singular_name()] |
375
|
|
|
)); |
376
|
|
|
} |
377
|
|
|
|
378
|
|
|
$title = $this->record->getTitle(); |
379
|
|
|
$this->tabulatorGrid->getDataList()->remove($this->record); |
380
|
|
|
|
381
|
|
|
$message = _t( |
382
|
|
|
'SilverStripe\\Forms\\GridField\\GridFieldDetailForm.Deleted', |
383
|
|
|
'Unlinked {type} {name}', |
384
|
|
|
[ |
385
|
|
|
'type' => $this->record->i18n_singular_name(), |
386
|
|
|
'name' => htmlspecialchars($title, ENT_QUOTES) |
387
|
|
|
] |
388
|
|
|
); |
389
|
|
|
|
390
|
|
|
//when an item is deleted, redirect to the parent controller |
391
|
|
|
$controller = $this->getToplevelController(); |
392
|
|
|
d($controller); |
|
|
|
|
393
|
|
|
|
394
|
|
|
if ($this->isSilverStripeAdmin($controller)) { |
395
|
|
|
$controller->getRequest()->addHeader('X-Pjax', 'Content'); |
396
|
|
|
} |
397
|
|
|
|
398
|
|
|
//redirect back to admin section |
399
|
|
|
$response = $controller->redirect($this->getBackLink(), 302); |
400
|
|
|
|
401
|
|
|
$this->sessionMessage($message, "good"); |
402
|
|
|
|
403
|
|
|
return $response; |
|
|
|
|
404
|
|
|
} |
405
|
|
|
|
406
|
|
|
/** |
407
|
|
|
* @return mixed |
408
|
|
|
*/ |
409
|
|
|
public function view(HTTPRequest $request) |
410
|
|
|
{ |
411
|
|
|
if (!$this->record->canView()) { |
412
|
|
|
return $this->httpError(403, _t( |
413
|
|
|
__CLASS__ . '.ViewPermissionsFailure', |
414
|
|
|
'It seems you don\'t have the necessary permissions to view "{ObjectTitle}"', |
415
|
|
|
['ObjectTitle' => $this->record->singular_name()] |
416
|
|
|
)); |
417
|
|
|
} |
418
|
|
|
|
419
|
|
|
$controller = $this->getToplevelController(); |
420
|
|
|
|
421
|
|
|
$form = $this->ItemEditForm(); |
422
|
|
|
$form->makeReadonly(); |
|
|
|
|
423
|
|
|
|
424
|
|
|
return $this->returnWithinContext($request, $controller, $form); |
|
|
|
|
425
|
|
|
} |
426
|
|
|
|
427
|
|
|
/** |
428
|
|
|
* This is responsible to forward actions to the model if necessary |
429
|
|
|
* @param HTTPRequest $request |
430
|
|
|
* @return HTTPResponse |
431
|
|
|
*/ |
432
|
|
|
public function customAction(HTTPRequest $request) |
433
|
|
|
{ |
434
|
|
|
// This gets populated thanks to our updated URL handler |
435
|
|
|
$params = $request->params(); |
436
|
|
|
$customAction = $params['CustomAction'] ?? null; |
437
|
|
|
$ID = $params['ID'] ?? 0; |
438
|
|
|
|
439
|
|
|
$dataClass = $this->tabulatorGrid->getModelClass(); |
440
|
|
|
$record = DataObject::get_by_id($dataClass, $ID); |
441
|
|
|
$rowActions = $record->tabulatorRowActions(); |
442
|
|
|
$validActions = array_keys($rowActions); |
443
|
|
|
if (!$customAction || !in_array($customAction, $validActions)) { |
444
|
|
|
return $this->httpError(403, _t( |
445
|
|
|
__CLASS__ . '.CustomActionPermissionsFailure', |
446
|
|
|
'It seems you don\'t have the necessary permissions to {ActionName} "{ObjectTitle}"', |
447
|
|
|
['ActionName' => $customAction, 'ObjectTitle' => $this->record->singular_name()] |
448
|
|
|
)); |
449
|
|
|
} |
450
|
|
|
|
451
|
|
|
$clickedAction = $rowActions[$customAction]; |
452
|
|
|
|
453
|
|
|
$error = false; |
454
|
|
|
try { |
455
|
|
|
$result = $record->$customAction(); |
456
|
|
|
} catch (Exception $e) { |
457
|
|
|
$error = true; |
458
|
|
|
$result = $e->getMessage(); |
459
|
|
|
} |
460
|
|
|
|
461
|
|
|
// Maybe it's a custom redirect or a file ? |
462
|
|
|
if ($result && $result instanceof HTTPResponse) { |
463
|
|
|
return $result; |
464
|
|
|
} |
465
|
|
|
|
466
|
|
|
// Show message on controller or in form |
467
|
|
|
$controller = $this->getToplevelController(); |
468
|
|
|
$response = $controller->getResponse(); |
469
|
|
|
if (Director::is_ajax()) { |
470
|
|
|
$responseData = [ |
471
|
|
|
'message' => $result, |
472
|
|
|
'status' => $error ? 'error' : 'success', |
473
|
|
|
]; |
474
|
|
|
if (!empty($clickedAction['reload'])) { |
475
|
|
|
$responseData['reload'] = true; |
476
|
|
|
} |
477
|
|
|
if (!empty($clickedAction['refresh'])) { |
478
|
|
|
$responseData['refresh'] = true; |
479
|
|
|
} |
480
|
|
|
$response->setBody(json_encode($responseData)); |
481
|
|
|
// 4xx status makes a red box |
482
|
|
|
if ($error) { |
483
|
|
|
$response->setStatusCode(400); |
484
|
|
|
} |
485
|
|
|
return $response; |
486
|
|
|
} |
487
|
|
|
|
488
|
|
|
$url = $this->getDefaultBackLink(); |
489
|
|
|
$response = $this->redirect($url); |
490
|
|
|
|
491
|
|
|
$this->sessionMessage($result, $error ? "error" : "good", "html"); |
492
|
|
|
|
493
|
|
|
return $response; |
494
|
|
|
} |
495
|
|
|
|
496
|
|
|
public function sessionMessage($message, $type = ValidationResult::TYPE_ERROR, $cast = ValidationResult::CAST_TEXT) |
497
|
|
|
{ |
498
|
|
|
$controller = $this->getToplevelController(); |
499
|
|
|
if ($controller->hasMethod('sessionMessage')) { |
500
|
|
|
$controller->sessionMessage($message, $type, $cast); |
501
|
|
|
} else { |
502
|
|
|
$form = $this->ItemEditForm(); |
503
|
|
|
if ($form) { |
|
|
|
|
504
|
|
|
$form->sessionMessage($message, $type, $cast); |
|
|
|
|
505
|
|
|
} |
506
|
|
|
} |
507
|
|
|
} |
508
|
|
|
|
509
|
|
|
/** |
510
|
|
|
* Builds an item edit form |
511
|
|
|
* |
512
|
|
|
* @return Form|HTTPResponse |
513
|
|
|
*/ |
514
|
|
|
public function ItemEditForm() |
515
|
|
|
{ |
516
|
|
|
$list = $this->tabulatorGrid->getList(); |
517
|
|
|
$controller = $this->getToplevelController(); |
518
|
|
|
|
519
|
|
|
try { |
520
|
|
|
$record = $this->getRecord(); |
521
|
|
|
} catch (Exception $e) { |
522
|
|
|
$url = $controller->getRequest()->getURL(); |
523
|
|
|
$noActionURL = $controller->removeAction($url); |
524
|
|
|
//clear the existing redirect |
525
|
|
|
$controller->getResponse()->removeHeader('Location'); |
526
|
|
|
return $controller->redirect($noActionURL, 302); |
527
|
|
|
} |
528
|
|
|
|
529
|
|
|
// If we are creating a new record in a has-many list, then |
530
|
|
|
// pre-populate the record's foreign key. |
531
|
|
|
if ($list instanceof HasManyList && !$this->record->isInDB()) { |
532
|
|
|
$key = $list->getForeignKey(); |
533
|
|
|
$id = $list->getForeignID(); |
534
|
|
|
$record->$key = $id; |
535
|
|
|
} |
536
|
|
|
|
537
|
|
|
if (!$record->canView()) { |
538
|
|
|
return $controller->httpError(403, _t( |
539
|
|
|
__CLASS__ . '.ViewPermissionsFailure', |
540
|
|
|
'It seems you don\'t have the necessary permissions to view "{ObjectTitle}"', |
541
|
|
|
['ObjectTitle' => $this->record->singular_name()] |
542
|
|
|
)); |
543
|
|
|
} |
544
|
|
|
|
545
|
|
|
if ($record->hasMethod("tabulatorCMSFields")) { |
546
|
|
|
$fields = $record->tabulatorCMSFields(); |
547
|
|
|
} else { |
548
|
|
|
$fields = $record->getCMSFields(); |
549
|
|
|
} |
550
|
|
|
|
551
|
|
|
// If we are creating a new record in a has-many list, then |
552
|
|
|
// Disable the form field as it has no effect. |
553
|
|
|
if ($list instanceof HasManyList && !$this->record->isInDB()) { |
554
|
|
|
$key = $list->getForeignKey(); |
555
|
|
|
|
556
|
|
|
if ($field = $fields->dataFieldByName($key)) { |
557
|
|
|
$fields->makeFieldReadonly($field); |
558
|
|
|
} |
559
|
|
|
} |
560
|
|
|
|
561
|
|
|
$compatLayer = $this->tabulatorGrid->getCompatLayer($controller); |
562
|
|
|
|
563
|
|
|
$actions = $compatLayer->getFormActions($this); |
564
|
|
|
$this->extend('updateFormActions', $actions); |
565
|
|
|
|
566
|
|
|
$validator = null; |
567
|
|
|
|
568
|
|
|
$form = new Form( |
569
|
|
|
$this, |
570
|
|
|
'ItemEditForm', |
571
|
|
|
$fields, |
572
|
|
|
$actions, |
573
|
|
|
$validator |
574
|
|
|
); |
575
|
|
|
|
576
|
|
|
$form->loadDataFrom($record, $record->ID == 0 ? Form::MERGE_IGNORE_FALSEISH : Form::MERGE_DEFAULT); |
577
|
|
|
|
578
|
|
|
if ($record->ID && !$record->canEdit()) { |
579
|
|
|
// Restrict editing of existing records |
580
|
|
|
$form->makeReadonly(); |
581
|
|
|
// Hack to re-enable delete button if user can delete |
582
|
|
|
if ($record->canDelete()) { |
583
|
|
|
$form->Actions()->fieldByName('action_doDelete')->setReadonly(false); |
|
|
|
|
584
|
|
|
} |
585
|
|
|
} |
586
|
|
|
$cannotCreate = !$record->ID && !$record->canCreate(null, $this->getCreateContext()); |
587
|
|
|
if ($cannotCreate || $this->tabulatorGrid->isViewOnly()) { |
588
|
|
|
// Restrict creation of new records |
589
|
|
|
$form->makeReadonly(); |
590
|
|
|
} |
591
|
|
|
|
592
|
|
|
// Load many_many extraData for record. |
593
|
|
|
// Fields with the correct 'ManyMany' namespace need to be added manually through getCMSFields(). |
594
|
|
|
if ($list instanceof ManyManyList) { |
595
|
|
|
$extraData = $list->getExtraData('', $this->record->ID); |
596
|
|
|
$form->loadDataFrom(['ManyMany' => $extraData]); |
597
|
|
|
} |
598
|
|
|
|
599
|
|
|
// Coupling with CMS |
600
|
|
|
$compatLayer->adjustItemEditForm($this, $form); |
601
|
|
|
|
602
|
|
|
$this->extend("updateItemEditForm", $form); |
603
|
|
|
|
604
|
|
|
return $form; |
605
|
|
|
} |
606
|
|
|
|
607
|
|
|
/** |
608
|
|
|
* Build context for verifying canCreate |
609
|
|
|
* |
610
|
|
|
* @return array |
611
|
|
|
*/ |
612
|
|
|
protected function getCreateContext() |
613
|
|
|
{ |
614
|
|
|
$grid = $this->tabulatorGrid; |
615
|
|
|
$context = []; |
616
|
|
|
if ($grid->getList() instanceof RelationList) { |
617
|
|
|
$record = $grid->getForm()->getRecord(); |
618
|
|
|
if ($record && $record instanceof DataObject) { |
|
|
|
|
619
|
|
|
$context['Parent'] = $record; |
620
|
|
|
} |
621
|
|
|
} |
622
|
|
|
return $context; |
623
|
|
|
} |
624
|
|
|
|
625
|
|
|
/** |
626
|
|
|
* @return \SilverStripe\Control\Controller|\SilverStripe\Admin\LeftAndMain|TabulatorGrid_ItemRequest |
|
|
|
|
627
|
|
|
*/ |
628
|
|
|
public function getToplevelController(): RequestHandler |
629
|
|
|
{ |
630
|
|
|
$c = $this->popupController; |
631
|
|
|
// Maybe our field is included in a GridField or in a TabulatorGrid? |
632
|
|
|
while ($c && ($c instanceof GridFieldDetailForm_ItemRequest || $c instanceof TabulatorGrid_ItemRequest)) { |
633
|
|
|
$c = $c->getController(); |
634
|
|
|
} |
635
|
|
|
return $c; |
636
|
|
|
} |
637
|
|
|
|
638
|
|
|
public function getDefaultBackLink(): string |
639
|
|
|
{ |
640
|
|
|
$url = $this->getBackURL() |
641
|
|
|
?: $this->getReturnReferer() |
642
|
|
|
?: $this->AbsoluteLink(); |
643
|
|
|
return $url; |
644
|
|
|
} |
645
|
|
|
|
646
|
|
|
public function getBackLink(): string |
647
|
|
|
{ |
648
|
|
|
$backlink = ''; |
649
|
|
|
$toplevelController = $this->getToplevelController(); |
650
|
|
|
if ($this->popupController->hasMethod('Breadcrumbs')) { |
651
|
|
|
$parents = $this->popupController->Breadcrumbs(false); |
652
|
|
|
if ($parents && $parents = $parents->items) { |
653
|
|
|
$backlink = array_pop($parents)->Link; |
654
|
|
|
} |
655
|
|
|
} |
656
|
|
|
if ($toplevelController && $toplevelController->hasMethod('Backlink')) { |
657
|
|
|
$backlink = $toplevelController->Backlink(); |
658
|
|
|
} |
659
|
|
|
if (!$backlink) { |
660
|
|
|
$backlink = $toplevelController->Link(); |
661
|
|
|
} |
662
|
|
|
return $backlink; |
663
|
|
|
} |
664
|
|
|
|
665
|
|
|
/** |
666
|
|
|
* Get the list of extra data from the $record as saved into it by |
667
|
|
|
* {@see Form::saveInto()} |
668
|
|
|
* |
669
|
|
|
* Handles detection of falsey values explicitly saved into the |
670
|
|
|
* DataObject by formfields |
671
|
|
|
* |
672
|
|
|
* @param DataObject $record |
673
|
|
|
* @param SS_List $list |
|
|
|
|
674
|
|
|
* @return array List of data to write to the relation |
675
|
|
|
*/ |
676
|
|
|
protected function getExtraSavedData($record, $list) |
677
|
|
|
{ |
678
|
|
|
// Skip extra data if not ManyManyList |
679
|
|
|
if (!($list instanceof ManyManyList)) { |
680
|
|
|
return null; |
681
|
|
|
} |
682
|
|
|
|
683
|
|
|
$data = []; |
684
|
|
|
foreach ($list->getExtraFields() as $field => $dbSpec) { |
685
|
|
|
$savedField = "ManyMany[{$field}]"; |
686
|
|
|
if ($record->hasField($savedField)) { |
687
|
|
|
$data[$field] = $record->getField($savedField); |
688
|
|
|
} |
689
|
|
|
} |
690
|
|
|
return $data; |
691
|
|
|
} |
692
|
|
|
|
693
|
|
|
public function doSave($data, $form) |
694
|
|
|
{ |
695
|
|
|
$isNewRecord = $this->record->ID == 0; |
696
|
|
|
|
697
|
|
|
// Check permission |
698
|
|
|
if (!$this->record->canEdit()) { |
699
|
|
|
return $this->editFailure(); |
700
|
|
|
} |
701
|
|
|
|
702
|
|
|
// _activetab is used in cms-action |
703
|
|
|
$this->hash = $data['_hash'] ?? $data['_activetab'] ?? ''; |
704
|
|
|
|
705
|
|
|
// Save from form data |
706
|
|
|
$error = false; |
707
|
|
|
|
708
|
|
|
// Leave it to FormRequestHandler::getAjaxErrorResponse so that we don't lose our data on error |
709
|
|
|
// try { |
710
|
|
|
$this->saveFormIntoRecord($data, $form); |
711
|
|
|
|
712
|
|
|
$title = $this->record->Title ?? ''; |
713
|
|
|
$link = '<a href="' . $this->Link('edit') . '">"' |
714
|
|
|
. htmlspecialchars($title, ENT_QUOTES) |
715
|
|
|
. '"</a>'; |
716
|
|
|
$message = _t( |
717
|
|
|
'SilverStripe\\Forms\\GridField\\GridFieldDetailForm.Saved', |
718
|
|
|
'Saved {name} {link}', |
719
|
|
|
[ |
720
|
|
|
'name' => $this->record->i18n_singular_name(), |
721
|
|
|
'link' => $link |
722
|
|
|
] |
723
|
|
|
); |
724
|
|
|
// } catch (Exception $e) { |
725
|
|
|
// $message = $e->getMessage(); |
726
|
|
|
// $error = true; |
727
|
|
|
// } |
728
|
|
|
|
729
|
|
|
// Redirect after save |
730
|
|
|
$response = $this->redirectAfterSave($isNewRecord); |
731
|
|
|
|
732
|
|
|
// Session message may add stuff to the response, so create the redirect before |
733
|
|
|
$this->sessionMessage($message, $error ? "error" : "good", 'html'); |
|
|
|
|
734
|
|
|
|
735
|
|
|
return $response; |
736
|
|
|
} |
737
|
|
|
|
738
|
|
|
/** |
739
|
|
|
* Gets the edit link for a record |
740
|
|
|
* |
741
|
|
|
* @param int $id The ID of the record in the GridField |
742
|
|
|
* @return string |
743
|
|
|
*/ |
744
|
|
|
public function getEditLink($id) |
745
|
|
|
{ |
746
|
|
|
$link = Controller::join_links( |
747
|
|
|
$this->tabulatorGrid->Link(), |
748
|
|
|
'item', |
749
|
|
|
$id |
750
|
|
|
); |
751
|
|
|
|
752
|
|
|
return $link; |
753
|
|
|
} |
754
|
|
|
|
755
|
|
|
/** |
756
|
|
|
* @param int $offset The offset from the current record |
757
|
|
|
* @return int|bool |
758
|
|
|
*/ |
759
|
|
|
private function getAdjacentRecordID($offset) |
760
|
|
|
{ |
761
|
|
|
$list = $this->getManipulatedData(); |
762
|
|
|
$map = array_column($list['data'], "ID"); |
763
|
|
|
$index = array_search($this->record->ID, $map); |
764
|
|
|
return isset($map[$index + $offset]) ? $map[$index + $offset] : false; |
765
|
|
|
} |
766
|
|
|
|
767
|
|
|
/** |
768
|
|
|
* Gets the ID of the previous record in the list. |
769
|
|
|
*/ |
770
|
|
|
public function getPreviousRecordID(): int |
771
|
|
|
{ |
772
|
|
|
return $this->getAdjacentRecordID(-1); |
|
|
|
|
773
|
|
|
} |
774
|
|
|
|
775
|
|
|
/** |
776
|
|
|
* Gets the ID of the next record in the list. |
777
|
|
|
*/ |
778
|
|
|
public function getNextRecordID(): int |
779
|
|
|
{ |
780
|
|
|
return $this->getAdjacentRecordID(1); |
|
|
|
|
781
|
|
|
} |
782
|
|
|
|
783
|
|
|
/** |
784
|
|
|
* This is expected in lekoala/silverstripe-cms-actions ActionsGridFieldItemRequest |
785
|
|
|
* @return HTTPResponse |
786
|
|
|
*/ |
787
|
|
|
public function getResponse() |
788
|
|
|
{ |
789
|
|
|
return $this->getToplevelController()->getResponse(); |
790
|
|
|
} |
791
|
|
|
|
792
|
|
|
/** |
793
|
|
|
* Response object for this request after a successful save |
794
|
|
|
* |
795
|
|
|
* @param bool $isNewRecord True if this record was just created |
796
|
|
|
* @return HTTPResponse|DBHTMLText |
|
|
|
|
797
|
|
|
*/ |
798
|
|
|
protected function redirectAfterSave($isNewRecord) |
799
|
|
|
{ |
800
|
|
|
$controller = $this->getToplevelController(); |
801
|
|
|
if ($isNewRecord) { |
802
|
|
|
return $this->redirect($this->Link()); |
803
|
|
|
} elseif ($this->tabulatorGrid->hasByIDList() && $this->tabulatorGrid->getByIDList()->byID($this->record->ID)) { |
804
|
|
|
return $this->redirect($this->getDefaultBackLink()); |
805
|
|
|
} else { |
806
|
|
|
// Changes to the record properties might've excluded the record from |
807
|
|
|
// a filtered list, so return back to the main view if it can't be found |
808
|
|
|
$url = $controller->getRequest()->getURL(); |
809
|
|
|
$noActionURL = $controller->removeAction($url); |
810
|
|
|
if ($this->isSilverStripeAdmin($controller)) { |
811
|
|
|
$controller->getRequest()->addHeader('X-Pjax', 'Content'); |
812
|
|
|
} |
813
|
|
|
return $controller->redirect($noActionURL, 302); |
814
|
|
|
} |
815
|
|
|
} |
816
|
|
|
|
817
|
|
|
protected function getHashValue() |
818
|
|
|
{ |
819
|
|
|
if ($this->hash) { |
820
|
|
|
$hash = $this->hash; |
821
|
|
|
} else { |
822
|
|
|
$hash = Cookie::get('hash'); |
823
|
|
|
} |
824
|
|
|
if ($hash) { |
825
|
|
|
$hash = '#' . ltrim($hash, '#'); |
826
|
|
|
} |
827
|
|
|
return $hash; |
828
|
|
|
} |
829
|
|
|
|
830
|
|
|
/** |
831
|
|
|
* Redirect to the given URL. |
832
|
|
|
* |
833
|
|
|
* @param string $url |
834
|
|
|
* @param int $code |
835
|
|
|
* @return HTTPResponse |
836
|
|
|
*/ |
837
|
|
|
public function redirect($url, $code = 302): HTTPResponse |
838
|
|
|
{ |
839
|
|
|
$hash = $this->getHashValue(); |
840
|
|
|
if ($hash) { |
841
|
|
|
$url .= $hash; |
842
|
|
|
} |
843
|
|
|
|
844
|
|
|
$controller = $this->getToplevelController(); |
845
|
|
|
$response = $controller->redirect($url, $code); |
846
|
|
|
|
847
|
|
|
// if ($hash) { |
848
|
|
|
// We also pass it as a hash |
849
|
|
|
// @link https://github.com/whatwg/fetch/issues/1167 |
850
|
|
|
// $response = $response->addHeader('X-Hash', $hash); |
851
|
|
|
// } |
852
|
|
|
|
853
|
|
|
return $response; |
854
|
|
|
} |
855
|
|
|
|
856
|
|
|
public function httpError($errorCode, $errorMessage = null) |
857
|
|
|
{ |
858
|
|
|
$controller = $this->getToplevelController(); |
859
|
|
|
return $controller->httpError($errorCode, $errorMessage); |
860
|
|
|
} |
861
|
|
|
|
862
|
|
|
/** |
863
|
|
|
* Loads the given form data into the underlying dataobject and relation |
864
|
|
|
* |
865
|
|
|
* @param array $data |
866
|
|
|
* @param Form $form |
867
|
|
|
* @throws ValidationException On error |
868
|
|
|
* @return DataObject Saved record |
869
|
|
|
*/ |
870
|
|
|
protected function saveFormIntoRecord($data, $form) |
871
|
|
|
{ |
872
|
|
|
$list = $this->tabulatorGrid->getList(); |
873
|
|
|
|
874
|
|
|
// Check object matches the correct classname |
875
|
|
|
if (isset($data['ClassName']) && $data['ClassName'] != $this->record->ClassName) { |
876
|
|
|
$newClassName = $data['ClassName']; |
877
|
|
|
// The records originally saved attribute was overwritten by $form->saveInto($record) before. |
878
|
|
|
// This is necessary for newClassInstance() to work as expected, and trigger change detection |
879
|
|
|
// on the ClassName attribute |
880
|
|
|
$this->record->setClassName($this->record->ClassName); |
881
|
|
|
// Replace $record with a new instance |
882
|
|
|
$this->record = $this->record->newClassInstance($newClassName); |
883
|
|
|
} |
884
|
|
|
|
885
|
|
|
// Save form and any extra saved data into this dataobject. |
886
|
|
|
// Set writeComponents = true to write has-one relations / join records |
887
|
|
|
$form->saveInto($this->record); |
888
|
|
|
// https://github.com/silverstripe/silverstripe-assets/issues/365 |
889
|
|
|
$this->record->write(); |
890
|
|
|
$this->extend('onAfterSave', $this->record); |
891
|
|
|
|
892
|
|
|
$extraData = $this->getExtraSavedData($this->record, $list); |
893
|
|
|
$list->add($this->record, $extraData); |
|
|
|
|
894
|
|
|
|
895
|
|
|
return $this->record; |
896
|
|
|
} |
897
|
|
|
|
898
|
|
|
/** |
899
|
|
|
* Delete from ItemRequest action |
900
|
|
|
* |
901
|
|
|
* @param array $data |
902
|
|
|
* @param Form $form |
903
|
|
|
* @return HTTPResponse |
904
|
|
|
* @throws ValidationException |
905
|
|
|
*/ |
906
|
|
|
public function doDelete($data, $form) |
907
|
|
|
{ |
908
|
|
|
$title = $this->record->Title; |
909
|
|
|
if (!$this->record->canDelete()) { |
910
|
|
|
throw new ValidationException( |
911
|
|
|
_t('SilverStripe\\Forms\\GridField\\GridFieldDetailForm.DeletePermissionsFailure', "No delete permissions") |
912
|
|
|
); |
913
|
|
|
} |
914
|
|
|
|
915
|
|
|
$this->record->delete(); |
916
|
|
|
|
917
|
|
|
$message = _t( |
918
|
|
|
'SilverStripe\\Forms\\GridField\\GridFieldDetailForm.Deleted', |
919
|
|
|
'Deleted {type} {name}', |
920
|
|
|
[ |
921
|
|
|
'type' => $this->record->i18n_singular_name(), |
922
|
|
|
'name' => htmlspecialchars($title, ENT_QUOTES) |
923
|
|
|
] |
924
|
|
|
); |
925
|
|
|
|
926
|
|
|
|
927
|
|
|
$backForm = $form; |
|
|
|
|
928
|
|
|
$toplevelController = $this->getToplevelController(); |
929
|
|
|
if ($this->isSilverStripeAdmin($toplevelController)) { |
930
|
|
|
$backForm = $toplevelController->getEditForm(); |
931
|
|
|
} |
932
|
|
|
//when an item is deleted, redirect to the parent controller |
933
|
|
|
$controller = $this->getToplevelController(); |
934
|
|
|
|
935
|
|
|
if ($this->isSilverStripeAdmin($toplevelController)) { |
936
|
|
|
$controller->getRequest()->addHeader('X-Pjax', 'Content'); |
937
|
|
|
} |
938
|
|
|
$response = $controller->redirect($this->getBackLink(), 302); //redirect back to admin section |
939
|
|
|
$this->sessionMessage($message, "good"); |
940
|
|
|
|
941
|
|
|
return $response; |
942
|
|
|
} |
943
|
|
|
|
944
|
|
|
public function isSilverStripeAdmin($controller) |
945
|
|
|
{ |
946
|
|
|
if ($controller) { |
947
|
|
|
return is_subclass_of($controller, \SilverStripe\Admin\LeftAndMain::class); |
948
|
|
|
} |
949
|
|
|
return false; |
950
|
|
|
} |
951
|
|
|
|
952
|
|
|
/** |
953
|
|
|
* @param string $template |
954
|
|
|
* @return $this |
955
|
|
|
*/ |
956
|
|
|
public function setTemplate($template) |
957
|
|
|
{ |
958
|
|
|
$this->template = $template; |
959
|
|
|
return $this; |
960
|
|
|
} |
961
|
|
|
|
962
|
|
|
/** |
963
|
|
|
* @return string |
964
|
|
|
*/ |
965
|
|
|
public function getTemplate() |
966
|
|
|
{ |
967
|
|
|
return $this->template; |
968
|
|
|
} |
969
|
|
|
|
970
|
|
|
/** |
971
|
|
|
* Get list of templates to use |
972
|
|
|
* |
973
|
|
|
* @return array |
974
|
|
|
*/ |
975
|
|
|
public function getTemplates() |
976
|
|
|
{ |
977
|
|
|
$templates = SSViewer::get_templates_by_class($this, '', __CLASS__); |
978
|
|
|
// Prefer any custom template |
979
|
|
|
if ($this->getTemplate()) { |
980
|
|
|
array_unshift($templates, $this->getTemplate()); |
981
|
|
|
} |
982
|
|
|
return $templates; |
983
|
|
|
} |
984
|
|
|
|
985
|
|
|
/** |
986
|
|
|
* @return Controller |
987
|
|
|
*/ |
988
|
|
|
public function getController() |
989
|
|
|
{ |
990
|
|
|
return $this->popupController; |
991
|
|
|
} |
992
|
|
|
|
993
|
|
|
/** |
994
|
|
|
* @return TabulatorGrid |
995
|
|
|
*/ |
996
|
|
|
public function getTabulatorGrid() |
997
|
|
|
{ |
998
|
|
|
return $this->tabulatorGrid; |
999
|
|
|
} |
1000
|
|
|
|
1001
|
|
|
/** |
1002
|
|
|
* @return DataObject |
1003
|
|
|
*/ |
1004
|
|
|
public function getRecord() |
1005
|
|
|
{ |
1006
|
|
|
return $this->record; |
1007
|
|
|
} |
1008
|
|
|
|
1009
|
|
|
/** |
1010
|
|
|
* CMS-specific functionality: Passes through navigation breadcrumbs |
1011
|
|
|
* to the template, and includes the currently edited record (if any). |
1012
|
|
|
* see {@link LeftAndMain->Breadcrumbs()} for details. |
1013
|
|
|
* |
1014
|
|
|
* @param boolean $unlinked |
1015
|
|
|
* @return ArrayList |
1016
|
|
|
*/ |
1017
|
|
|
public function Breadcrumbs($unlinked = false) |
1018
|
|
|
{ |
1019
|
|
|
if (!$this->popupController->hasMethod('Breadcrumbs')) { |
1020
|
|
|
return null; |
1021
|
|
|
} |
1022
|
|
|
|
1023
|
|
|
/** @var ArrayList $items */ |
1024
|
|
|
$items = $this->popupController->Breadcrumbs($unlinked); |
1025
|
|
|
|
1026
|
|
|
if (!$items) { |
|
|
|
|
1027
|
|
|
$items = new ArrayList(); |
1028
|
|
|
} |
1029
|
|
|
|
1030
|
|
|
if ($this->record && $this->record->ID) { |
1031
|
|
|
$title = ($this->record->Title) ? $this->record->Title : "#{$this->record->ID}"; |
1032
|
|
|
$items->push(new ArrayData([ |
1033
|
|
|
'Title' => $title, |
1034
|
|
|
'Link' => $this->Link() |
1035
|
|
|
])); |
1036
|
|
|
} else { |
1037
|
|
|
$items->push(new ArrayData([ |
1038
|
|
|
'Title' => _t('SilverStripe\\Forms\\GridField\\GridField.NewRecord', 'New {type}', ['type' => $this->record->i18n_singular_name()]), |
1039
|
|
|
'Link' => false |
1040
|
|
|
])); |
1041
|
|
|
} |
1042
|
|
|
|
1043
|
|
|
$this->extend('updateBreadcrumbs', $items); |
1044
|
|
|
return $items; |
1045
|
|
|
} |
1046
|
|
|
} |
1047
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.