1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace SilverStripe\Forms\GridField; |
4
|
|
|
|
5
|
|
|
use LogicException; |
6
|
|
|
use SilverStripe\Admin\LeftAndMain; |
|
|
|
|
7
|
|
|
use SilverStripe\Control\Controller; |
8
|
|
|
use SilverStripe\Control\HTTPResponse; |
9
|
|
|
use SilverStripe\Core\Config\Config; |
10
|
|
|
use SilverStripe\Core\Convert; |
11
|
|
|
use SilverStripe\Dev\Deprecation; |
12
|
|
|
use SilverStripe\Forms\FieldGroup; |
13
|
|
|
use SilverStripe\Forms\FieldList; |
14
|
|
|
use SilverStripe\Forms\Form; |
15
|
|
|
use SilverStripe\Forms\Schema\FormSchema; |
16
|
|
|
use SilverStripe\Forms\TextField; |
17
|
|
|
use SilverStripe\ORM\ArrayList; |
18
|
|
|
use SilverStripe\ORM\Filterable; |
19
|
|
|
use SilverStripe\ORM\SS_List; |
20
|
|
|
use SilverStripe\View\ArrayData; |
21
|
|
|
use SilverStripe\View\SSViewer; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* GridFieldFilterHeader alters the {@link GridField} with some filtering |
25
|
|
|
* fields in the header of each column. |
26
|
|
|
* |
27
|
|
|
* @see GridField |
28
|
|
|
*/ |
29
|
|
|
class GridFieldFilterHeader implements |
30
|
|
|
GridField_URLHandler, |
31
|
|
|
GridField_HTMLProvider, |
32
|
|
|
GridField_DataManipulator, |
33
|
|
|
GridField_ActionProvider |
34
|
|
|
{ |
35
|
|
|
/** |
36
|
|
|
* See {@link setThrowExceptionOnBadDataType()} |
37
|
|
|
* |
38
|
|
|
* @var bool |
39
|
|
|
*/ |
40
|
|
|
protected $throwExceptionOnBadDataType = true; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* Indicates that this component should revert to displaying it's legacy |
44
|
|
|
* table header style rather than the react driven search box |
45
|
|
|
* |
46
|
|
|
* @deprecated 4.3.0:5.0.0 Will be removed in 5.0 |
47
|
|
|
* @var bool |
48
|
|
|
*/ |
49
|
|
|
public $useLegacyFilterHeader = false; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* Forces all filter components to revert to displaying the legacy |
53
|
|
|
* table header style rather than the react driven search box |
54
|
|
|
* |
55
|
|
|
* @deprecated 4.3.0:5.0.0 Will be removed in 5.0 |
56
|
|
|
* @config |
57
|
|
|
* @var bool |
58
|
|
|
*/ |
59
|
|
|
private static $force_legacy = false; |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* @var \SilverStripe\ORM\Search\SearchContext |
63
|
|
|
*/ |
64
|
|
|
protected $searchContext = null; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* @var Form |
68
|
|
|
*/ |
69
|
|
|
protected $searchForm = null; |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* @var callable |
73
|
|
|
* @deprecated 4.3.0:5.0.0 Will be removed in 5.0 |
74
|
|
|
*/ |
75
|
|
|
protected $updateSearchContextCallback = null; |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* @var callable |
79
|
|
|
* @deprecated 4.3.0:5.0.0 Will be removed in 5.0 |
80
|
|
|
*/ |
81
|
|
|
protected $updateSearchFormCallback = null; |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* @inheritDoc |
85
|
|
|
*/ |
86
|
|
|
public function getURLHandlers($gridField) |
87
|
|
|
{ |
88
|
|
|
return [ |
89
|
|
|
'GET schema/SearchForm' => 'getSearchFormSchema' |
90
|
|
|
]; |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* @param bool $useLegacy This will be removed in 5.0 |
95
|
|
|
* @param callable|null $updateSearchContext This will be removed in 5.0 |
96
|
|
|
* @param callable|null $updateSearchForm This will be removed in 5.0 |
97
|
|
|
*/ |
98
|
|
|
public function __construct( |
99
|
|
|
$useLegacy = false, |
100
|
|
|
callable $updateSearchContext = null, |
101
|
|
|
callable $updateSearchForm = null |
102
|
|
|
) { |
103
|
|
|
$this->useLegacyFilterHeader = Config::inst()->get(self::class, 'force_legacy') || $useLegacy; |
|
|
|
|
104
|
|
|
$this->updateSearchContextCallback = $updateSearchContext; |
|
|
|
|
105
|
|
|
$this->updateSearchFormCallback = $updateSearchForm; |
|
|
|
|
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* Determine what happens when this component is used with a list that isn't {@link SS_Filterable}. |
110
|
|
|
* |
111
|
|
|
* - true: An exception is thrown |
112
|
|
|
* - false: This component will be ignored - it won't make any changes to the GridField. |
113
|
|
|
* |
114
|
|
|
* By default, this is set to true so that it's clearer what's happening, but the predefined |
115
|
|
|
* {@link GridFieldConfig} subclasses set this to false for flexibility. |
116
|
|
|
* |
117
|
|
|
* @param bool $throwExceptionOnBadDataType |
118
|
|
|
*/ |
119
|
|
|
public function setThrowExceptionOnBadDataType($throwExceptionOnBadDataType) |
120
|
|
|
{ |
121
|
|
|
$this->throwExceptionOnBadDataType = $throwExceptionOnBadDataType; |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* See {@link setThrowExceptionOnBadDataType()} |
126
|
|
|
*/ |
127
|
|
|
public function getThrowExceptionOnBadDataType() |
128
|
|
|
{ |
129
|
|
|
return $this->throwExceptionOnBadDataType; |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* Check that this dataList is of the right data type. |
134
|
|
|
* Returns false if it's a bad data type, and if appropriate, throws an exception. |
135
|
|
|
* |
136
|
|
|
* @param SS_List $dataList |
137
|
|
|
* @return bool |
138
|
|
|
*/ |
139
|
|
|
protected function checkDataType($dataList) |
140
|
|
|
{ |
141
|
|
|
if ($dataList instanceof Filterable) { |
142
|
|
|
return true; |
143
|
|
|
} else { |
144
|
|
|
if ($this->throwExceptionOnBadDataType) { |
145
|
|
|
throw new LogicException( |
146
|
|
|
static::class . " expects an SS_Filterable list to be passed to the GridField." |
147
|
|
|
); |
148
|
|
|
} |
149
|
|
|
return false; |
150
|
|
|
} |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
/** |
154
|
|
|
* If the GridField has a filterable datalist, return an array of actions |
155
|
|
|
* |
156
|
|
|
* @param GridField $gridField |
157
|
|
|
* @return array |
158
|
|
|
*/ |
159
|
|
|
public function getActions($gridField) |
160
|
|
|
{ |
161
|
|
|
if (!$this->checkDataType($gridField->getList())) { |
162
|
|
|
return []; |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
return ['filter', 'reset']; |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* If the GridField has a filterable datalist, return an array of actions |
170
|
|
|
* |
171
|
|
|
* @param GridField $gridField |
172
|
|
|
* @return void |
173
|
|
|
*/ |
174
|
|
|
public function handleAction(GridField $gridField, $actionName, $arguments, $data) |
175
|
|
|
{ |
176
|
|
|
if (!$this->checkDataType($gridField->getList())) { |
177
|
|
|
return; |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
$state = $gridField->State->GridFieldFilterHeader; |
|
|
|
|
181
|
|
|
$state->Columns = null; |
182
|
|
|
if ($actionName === 'filter') { |
183
|
|
|
if (isset($data['filter'][$gridField->getName()])) { |
184
|
|
|
foreach ($data['filter'][$gridField->getName()] as $key => $filter) { |
185
|
|
|
$state->Columns->$key = $filter; |
186
|
|
|
} |
187
|
|
|
} |
188
|
|
|
} |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
|
192
|
|
|
/** |
193
|
|
|
* @inheritDoc |
194
|
|
|
*/ |
195
|
|
|
public function getManipulatedData(GridField $gridField, SS_List $dataList) |
196
|
|
|
{ |
197
|
|
|
if (!$this->checkDataType($dataList)) { |
198
|
|
|
return $dataList; |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
/** @var Filterable $dataList */ |
202
|
|
|
/** @var GridState_Data $columns */ |
203
|
|
|
$columns = $gridField->State->GridFieldFilterHeader->Columns(null); |
|
|
|
|
204
|
|
|
if (empty($columns)) { |
205
|
|
|
return $dataList; |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
$filterArguments = $columns->toArray(); |
209
|
|
|
$dataListClone = clone($dataList); |
210
|
|
|
$results = $this->getSearchContext($gridField) |
211
|
|
|
->getQuery($filterArguments, false, false, $dataListClone); |
212
|
|
|
|
213
|
|
|
return $results; |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
/** |
217
|
|
|
* Returns whether this {@link GridField} has any columns to filter on at all |
218
|
|
|
* |
219
|
|
|
* @param GridField $gridField |
220
|
|
|
* @return boolean |
221
|
|
|
*/ |
222
|
|
|
public function canFilterAnyColumns($gridField) |
223
|
|
|
{ |
224
|
|
|
$list = $gridField->getList(); |
225
|
|
|
|
226
|
|
|
if (!$this->checkDataType($list)) { |
227
|
|
|
return false; |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
$columns = $gridField->getColumns(); |
231
|
|
|
foreach ($columns as $columnField) { |
232
|
|
|
$metadata = $gridField->getColumnMetadata($columnField); |
233
|
|
|
$title = $metadata['title']; |
234
|
|
|
|
235
|
|
|
if ($title && $list->canFilterBy($columnField)) { |
|
|
|
|
236
|
|
|
return true; |
237
|
|
|
} |
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
return false; |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
|
244
|
|
|
/** |
245
|
|
|
* Generate a search context based on the model class of the of the GridField |
246
|
|
|
* |
247
|
|
|
* @param GridField $gridfield |
248
|
|
|
* @return \SilverStripe\ORM\Search\SearchContext |
249
|
|
|
*/ |
250
|
|
|
public function getSearchContext(GridField $gridField) |
251
|
|
|
{ |
252
|
|
|
if (!$this->searchContext) { |
253
|
|
|
$this->searchContext = singleton($gridField->getModelClass())->getDefaultSearchContext(); |
254
|
|
|
|
255
|
|
|
if ($this->updateSearchContextCallback) { |
|
|
|
|
256
|
|
|
call_user_func($this->updateSearchContextCallback, $this->searchContext); |
|
|
|
|
257
|
|
|
} |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
return $this->searchContext; |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
/** |
264
|
|
|
* Returns the search field schema for the component |
265
|
|
|
* |
266
|
|
|
* @param GridField $gridfield |
267
|
|
|
* @return string |
268
|
|
|
*/ |
269
|
|
|
public function getSearchFieldSchema(GridField $gridField) |
270
|
|
|
{ |
271
|
|
|
$schemaUrl = Controller::join_links($gridField->Link(), 'schema/SearchForm'); |
272
|
|
|
|
273
|
|
|
$context = $this->getSearchContext($gridField); |
274
|
|
|
$params = $gridField->getRequest()->postVar('filter') ?: []; |
275
|
|
|
if (array_key_exists($gridField->getName(), $params)) { |
276
|
|
|
$params = $params[$gridField->getName()]; |
277
|
|
|
} |
278
|
|
|
if ($context->getSearchParams()) { |
279
|
|
|
$params = array_merge($context->getSearchParams(), $params); |
280
|
|
|
} |
281
|
|
|
$context->setSearchParams($params); |
282
|
|
|
|
283
|
|
|
$searchField = $context->getSearchFields()->first(); |
284
|
|
|
$searchField = $searchField && property_exists($searchField, 'name') ? $searchField->name : null; |
285
|
|
|
|
286
|
|
|
$name = $gridField->Title ?: singleton($gridField->getModelClass())->i18n_plural_name(); |
|
|
|
|
287
|
|
|
|
288
|
|
|
// Prefix "Search__" onto the filters for the React component |
289
|
|
|
$filters = $context->getSearchParams(); |
290
|
|
|
if (!$this->useLegacyFilterHeader && !empty($filters)) { |
|
|
|
|
291
|
|
|
$filters = array_combine(array_map(function ($key) { |
292
|
|
|
return 'Search__' . $key; |
293
|
|
|
}, array_keys($filters)), $filters); |
294
|
|
|
} |
295
|
|
|
|
296
|
|
|
$searchAction = GridField_FormAction::create($gridField, 'filter', false, 'filter', null); |
297
|
|
|
$clearAction = GridField_FormAction::create($gridField, 'reset', false, 'reset', null); |
298
|
|
|
$schema = [ |
299
|
|
|
'formSchemaUrl' => $schemaUrl, |
300
|
|
|
'name' => $searchField, |
301
|
|
|
'placeholder' => _t(__CLASS__ . '.Search', 'Search "{name}"', ['name' => $name]), |
302
|
|
|
'filters' => $filters ?: new \stdClass, // stdClass maps to empty json object '{}' |
303
|
|
|
'gridfield' => $gridField->getName(), |
304
|
|
|
'searchAction' => $searchAction->getAttribute('name'), |
305
|
|
|
'searchActionState' => $searchAction->getAttribute('data-action-state'), |
306
|
|
|
'clearAction' => $clearAction->getAttribute('name'), |
307
|
|
|
'clearActionState' => $clearAction->getAttribute('data-action-state'), |
308
|
|
|
]; |
309
|
|
|
|
310
|
|
|
return json_encode($schema); |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
/** |
314
|
|
|
* Returns the search form for the component |
315
|
|
|
* |
316
|
|
|
* @param GridField $gridField |
317
|
|
|
* @return Form|null |
318
|
|
|
*/ |
319
|
|
|
public function getSearchForm(GridField $gridField) |
320
|
|
|
{ |
321
|
|
|
$searchContext = $this->getSearchContext($gridField); |
322
|
|
|
$searchFields = $searchContext->getSearchFields(); |
323
|
|
|
|
324
|
|
|
if ($searchFields->count() === 0) { |
325
|
|
|
return null; |
326
|
|
|
} |
327
|
|
|
|
328
|
|
|
if ($this->searchForm) { |
329
|
|
|
return $this->searchForm; |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
// Append a prefix to search field names to prevent conflicts with other fields in the search form |
333
|
|
|
foreach ($searchFields as $field) { |
334
|
|
|
$field->setName('Search__' . $field->getName()); |
335
|
|
|
} |
336
|
|
|
|
337
|
|
|
$columns = $gridField->getColumns(); |
338
|
|
|
|
339
|
|
|
// Update field titles to match column titles |
340
|
|
|
foreach ($columns as $columnField) { |
341
|
|
|
$metadata = $gridField->getColumnMetadata($columnField); |
342
|
|
|
// Get the field name, without any modifications |
343
|
|
|
$name = explode('.', $columnField); |
344
|
|
|
$title = $metadata['title']; |
345
|
|
|
$field = $searchFields->fieldByName($name[0]); |
346
|
|
|
|
347
|
|
|
if ($field) { |
348
|
|
|
$field->setTitle($title); |
349
|
|
|
} |
350
|
|
|
} |
351
|
|
|
|
352
|
|
|
foreach ($searchFields->getIterator() as $field) { |
353
|
|
|
$field->addExtraClass('stacked'); |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
$name = $gridField->Title ?: singleton($gridField->getModelClass())->i18n_plural_name(); |
|
|
|
|
357
|
|
|
|
358
|
|
|
$this->searchForm = $form = new Form( |
359
|
|
|
$gridField, |
360
|
|
|
$name . "SearchForm", |
361
|
|
|
$searchFields, |
362
|
|
|
new FieldList() |
363
|
|
|
); |
364
|
|
|
|
365
|
|
|
$form->setFormMethod('get'); |
366
|
|
|
$form->setFormAction($gridField->Link()); |
367
|
|
|
$form->addExtraClass('cms-search-form form--no-dividers'); |
368
|
|
|
$form->disableSecurityToken(); // This form is not tied to session so we disable this |
369
|
|
|
$form->loadDataFrom($searchContext->getSearchParams()); |
370
|
|
|
|
371
|
|
|
if ($this->updateSearchFormCallback) { |
|
|
|
|
372
|
|
|
call_user_func($this->updateSearchFormCallback, $form); |
|
|
|
|
373
|
|
|
} |
374
|
|
|
|
375
|
|
|
return $this->searchForm; |
376
|
|
|
} |
377
|
|
|
|
378
|
|
|
/** |
379
|
|
|
* Returns the search form schema for the component |
380
|
|
|
* |
381
|
|
|
* @param GridField $gridfield |
382
|
|
|
* @return HTTPResponse |
383
|
|
|
*/ |
384
|
|
|
public function getSearchFormSchema(GridField $gridField) |
385
|
|
|
{ |
386
|
|
|
$form = $this->getSearchForm($gridField); |
387
|
|
|
|
388
|
|
|
// If there are no filterable fields, return a 400 response |
389
|
|
|
if (!$form) { |
390
|
|
|
return new HTTPResponse(_t(__CLASS__ . '.SearchFormFaliure', 'No search form could be generated'), 400); |
391
|
|
|
} |
392
|
|
|
|
393
|
|
|
$parts = $gridField->getRequest()->getHeader(LeftAndMain::SCHEMA_HEADER); |
394
|
|
|
$schemaID = $gridField->getRequest()->getURL(); |
395
|
|
|
$data = FormSchema::singleton() |
396
|
|
|
->getMultipartSchema($parts, $schemaID, $form); |
397
|
|
|
|
398
|
|
|
$response = new HTTPResponse(json_encode($data)); |
399
|
|
|
$response->addHeader('Content-Type', 'application/json'); |
400
|
|
|
return $response; |
401
|
|
|
} |
402
|
|
|
|
403
|
|
|
/** |
404
|
|
|
* Generate fields for the legacy filter header row |
405
|
|
|
* |
406
|
|
|
* @deprecated 5.0 |
407
|
|
|
* @param GridField $gridfield |
408
|
|
|
* @return ArrayList|null |
409
|
|
|
*/ |
410
|
|
|
public function getLegacyFilterHeader(GridField $gridField) |
411
|
|
|
{ |
412
|
|
|
Deprecation::notice('5.0', 'Table row based filter header will be removed in favor of search field in 5.0'); |
413
|
|
|
|
414
|
|
|
$list = $gridField->getList(); |
415
|
|
|
if (!$this->checkDataType($list)) { |
416
|
|
|
return null; |
417
|
|
|
} |
418
|
|
|
|
419
|
|
|
$columns = $gridField->getColumns(); |
420
|
|
|
$filterArguments = $gridField->State->GridFieldFilterHeader->Columns->toArray(); |
|
|
|
|
421
|
|
|
$currentColumn = 0; |
422
|
|
|
$canFilter = false; |
423
|
|
|
$fieldsList = new ArrayList(); |
424
|
|
|
|
425
|
|
|
foreach ($columns as $columnField) { |
426
|
|
|
$currentColumn++; |
427
|
|
|
$metadata = $gridField->getColumnMetadata($columnField); |
428
|
|
|
$title = $metadata['title']; |
429
|
|
|
$fields = new FieldGroup(); |
430
|
|
|
|
431
|
|
|
if ($title && $list->canFilterBy($columnField)) { |
432
|
|
|
$canFilter = true; |
433
|
|
|
|
434
|
|
|
$value = ''; |
435
|
|
|
if (isset($filterArguments[$columnField])) { |
436
|
|
|
$value = $filterArguments[$columnField]; |
437
|
|
|
} |
438
|
|
|
$field = new TextField('filter[' . $gridField->getName() . '][' . $columnField . ']', '', $value); |
439
|
|
|
$field->addExtraClass('grid-field__sort-field'); |
440
|
|
|
$field->addExtraClass('no-change-track'); |
441
|
|
|
|
442
|
|
|
$field->setAttribute( |
443
|
|
|
'placeholder', |
444
|
|
|
_t('SilverStripe\\Forms\\GridField\\GridField.FilterBy', "Filter by ") |
445
|
|
|
. _t('SilverStripe\\Forms\\GridField\\GridField.' . $metadata['title'], $metadata['title']) |
446
|
|
|
); |
447
|
|
|
|
448
|
|
|
$fields->push($field); |
449
|
|
|
$fields->push( |
450
|
|
|
GridField_FormAction::create($gridField, 'reset', false, 'reset', null) |
451
|
|
|
->addExtraClass('btn font-icon-cancel btn-secondary btn--no-text ss-gridfield-button-reset') |
452
|
|
|
->setAttribute('title', _t('SilverStripe\\Forms\\GridField\\GridField.ResetFilter', "Reset")) |
453
|
|
|
->setAttribute('id', 'action_reset_' . $gridField->getModelClass() . '_' . $columnField) |
454
|
|
|
); |
455
|
|
|
} |
456
|
|
|
|
457
|
|
|
if ($currentColumn == count($columns)) { |
458
|
|
|
$fields->push( |
459
|
|
|
GridField_FormAction::create($gridField, 'filter', false, 'filter', null) |
460
|
|
|
->addExtraClass( |
461
|
|
|
'btn font-icon-search btn--no-text btn--icon-large grid-field__filter-submit' |
462
|
|
|
. ' ss-gridfield-button-filter' |
463
|
|
|
) |
464
|
|
|
->setAttribute('title', _t('SilverStripe\\Forms\\GridField\\GridField.Filter', 'Filter')) |
465
|
|
|
->setAttribute('id', 'action_filter_' . $gridField->getModelClass() . '_' . $columnField) |
466
|
|
|
); |
467
|
|
|
$fields->push( |
468
|
|
|
GridField_FormAction::create($gridField, 'reset', false, 'reset', null) |
469
|
|
|
->addExtraClass( |
470
|
|
|
'btn font-icon-cancel btn--no-text grid-field__filter-clear btn--icon-md' |
471
|
|
|
. 'ss-gridfield-button-close' |
472
|
|
|
) |
473
|
|
|
->setAttribute('title', _t('SilverStripe\\Forms\\GridField\\GridField.ResetFilter', "Reset")) |
474
|
|
|
->setAttribute('id', 'action_reset_' . $gridField->getModelClass() . '_' . $columnField) |
475
|
|
|
); |
476
|
|
|
$fields->addExtraClass('grid-field__filter-buttons'); |
477
|
|
|
$fields->addExtraClass('no-change-track'); |
478
|
|
|
} |
479
|
|
|
|
480
|
|
|
$fieldsList->push($fields); |
481
|
|
|
} |
482
|
|
|
|
483
|
|
|
return $canFilter ? $fieldsList : null; |
484
|
|
|
} |
485
|
|
|
|
486
|
|
|
/** |
487
|
|
|
* Either returns the legacy filter header or the search button and field |
488
|
|
|
* |
489
|
|
|
* @param GridField $gridField |
490
|
|
|
* @return array|null |
491
|
|
|
*/ |
492
|
|
|
public function getHTMLFragments($gridField) |
493
|
|
|
{ |
494
|
|
|
$forTemplate = new ArrayData([]); |
495
|
|
|
|
496
|
|
|
if (!$this->canFilterAnyColumns($gridField)) { |
497
|
|
|
return null; |
498
|
|
|
} |
499
|
|
|
|
500
|
|
|
if ($this->useLegacyFilterHeader) { |
|
|
|
|
501
|
|
|
$fieldsList = $this->getLegacyFilterHeader($gridField); |
|
|
|
|
502
|
|
|
$forTemplate->Fields = $fieldsList; |
|
|
|
|
503
|
|
|
$filterTemplates = SSViewer::get_templates_by_class($this, '_Row', __CLASS__); |
504
|
|
|
return ['header' => $forTemplate->renderWith($filterTemplates)]; |
505
|
|
|
} else { |
506
|
|
|
$fieldSchema = $this->getSearchFieldSchema($gridField); |
507
|
|
|
$forTemplate->SearchFieldSchema = $fieldSchema; |
|
|
|
|
508
|
|
|
$searchTemplates = SSViewer::get_templates_by_class($this, '_Search', __CLASS__); |
509
|
|
|
return [ |
510
|
|
|
'before' => $forTemplate->renderWith($searchTemplates), |
511
|
|
|
'buttons-before-right' => sprintf( |
512
|
|
|
'<button type="button" name="showFilter" aria-label="%s" title="%s"' . |
513
|
|
|
' class="btn btn-secondary font-icon-search btn--no-text btn--icon-large' |
514
|
|
|
. ' grid-field__filter-open"></button>', |
515
|
|
|
_t('SilverStripe\\Forms\\GridField\\GridField.OpenFilter', "Open search and filter"), |
516
|
|
|
_t('SilverStripe\\Forms\\GridField\\GridField.OpenFilter', "Open search and filter") |
517
|
|
|
) |
518
|
|
|
]; |
519
|
|
|
} |
520
|
|
|
} |
521
|
|
|
} |
522
|
|
|
|
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:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths