1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* @copyright Copyright (c) 2015 ublaboo <[email protected]> |
5
|
|
|
* @author Pavel Janda <[email protected]> |
6
|
|
|
* @package Ublaboo |
7
|
|
|
*/ |
8
|
|
|
|
9
|
|
|
namespace Ublaboo\DataGrid; |
10
|
|
|
|
11
|
|
|
use Nette; |
12
|
|
|
use Ublaboo\DataGrid\Utils\ArraysHelper; |
13
|
|
|
use Nette\Application\UI\Form; |
14
|
|
|
|
15
|
|
|
class DataGrid extends Nette\Application\UI\Control |
16
|
|
|
{ |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* @var string |
20
|
|
|
* @todo Tell about this on github |
21
|
|
|
*/ |
22
|
|
|
public static $icon_prefix = 'fa fa-'; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* When set to TRUE, datagrid throws an exception |
26
|
|
|
* when tring to get related entity within join and entity does not exist |
27
|
|
|
* @var bool |
28
|
|
|
*/ |
29
|
|
|
public $strict_entity_property = FALSE; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* @var int |
33
|
|
|
* @persistent |
34
|
|
|
*/ |
35
|
|
|
public $page = 1; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* @var int |
39
|
|
|
* @persistent |
40
|
|
|
*/ |
41
|
|
|
public $per_page; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* @var array |
45
|
|
|
* @persistent |
46
|
|
|
*/ |
47
|
|
|
public $sort = []; |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* @var array |
51
|
|
|
* @persistent |
52
|
|
|
*/ |
53
|
|
|
public $filter = []; |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* @var Callable[] |
57
|
|
|
*/ |
58
|
|
|
public $onRender = []; |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* @var array |
62
|
|
|
*/ |
63
|
|
|
protected $items_per_page_list = [10, 20, 50]; |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* @var string |
67
|
|
|
*/ |
68
|
|
|
protected $template_file; |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* @var Column\IColumn[] |
72
|
|
|
*/ |
73
|
|
|
protected $columns = []; |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* @var Column\Action[] |
77
|
|
|
*/ |
78
|
|
|
protected $actions = []; |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* @var GroupAction\GroupActionCollection |
82
|
|
|
*/ |
83
|
|
|
protected $group_action_collection; |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* @var Filter\Filter[] |
87
|
|
|
*/ |
88
|
|
|
protected $filters = []; |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* @var Export\Export[] |
92
|
|
|
*/ |
93
|
|
|
protected $exports = []; |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* @var DataModel |
97
|
|
|
*/ |
98
|
|
|
protected $dataModel; |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* @var DataFilter |
102
|
|
|
*/ |
103
|
|
|
protected $dataFilter; |
104
|
|
|
|
105
|
|
|
/** |
106
|
|
|
* @var string |
107
|
|
|
*/ |
108
|
|
|
protected $primary_key = 'id'; |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* @var bool |
112
|
|
|
*/ |
113
|
|
|
protected $do_paginate = TRUE; |
114
|
|
|
|
115
|
|
|
/** |
116
|
|
|
* @var bool |
117
|
|
|
*/ |
118
|
|
|
protected $csv_export = TRUE; |
119
|
|
|
|
120
|
|
|
/** |
121
|
|
|
* @var bool |
122
|
|
|
*/ |
123
|
|
|
protected $csv_export_filtered = TRUE; |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* @var bool |
127
|
|
|
*/ |
128
|
|
|
protected $sortable = FALSE; |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* @var string |
132
|
|
|
*/ |
133
|
|
|
protected $original_template; |
134
|
|
|
|
135
|
|
|
/** |
136
|
|
|
* @var array |
137
|
|
|
*/ |
138
|
|
|
protected $redraw_item; |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* @var mixed |
142
|
|
|
*/ |
143
|
|
|
protected $translator; |
144
|
|
|
|
145
|
|
|
/** |
146
|
|
|
* @var bool |
147
|
|
|
*/ |
148
|
|
|
protected $force_filter_active; |
149
|
|
|
|
150
|
|
|
/** |
151
|
|
|
* @var callable |
152
|
|
|
*/ |
153
|
|
|
protected $tree_view_children_callback; |
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* @var string |
157
|
|
|
*/ |
158
|
|
|
protected $tree_view_has_children_column; |
159
|
|
|
|
160
|
|
|
/** |
161
|
|
|
* @var bool |
162
|
|
|
*/ |
163
|
|
|
protected $outer_filter_rendering = FALSE; |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* @var bool |
167
|
|
|
*/ |
168
|
|
|
private $remember_state = TRUE; |
169
|
|
|
|
170
|
|
|
/** |
171
|
|
|
* @var bool |
172
|
|
|
*/ |
173
|
|
|
private $refresh_url = TRUE; |
174
|
|
|
|
175
|
|
|
/** |
176
|
|
|
* @var Nette\Http\SessionSection |
177
|
|
|
*/ |
178
|
|
|
private $grid_session; |
179
|
|
|
|
180
|
|
|
/** |
181
|
|
|
* @var array |
182
|
|
|
*/ |
183
|
|
|
private $items_detail = []; |
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* @var array |
187
|
|
|
*/ |
188
|
|
|
private $row_conditions = [ |
189
|
|
|
'group_action' => FALSE, |
190
|
|
|
'action' => [] |
191
|
|
|
]; |
192
|
|
|
|
193
|
|
|
|
194
|
|
|
/** |
195
|
|
|
* @param Nette\ComponentModel\IContainer|NULL $parent |
196
|
|
|
* @param string $name |
197
|
|
|
*/ |
198
|
|
|
public function __construct(Nette\ComponentModel\IContainer $parent = NULL, $name = NULL) |
199
|
|
|
{ |
200
|
|
|
parent::__construct($parent, $name); |
201
|
|
|
|
202
|
|
|
$this->monitor('Nette\Application\UI\Presenter'); |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
|
206
|
|
|
/** |
207
|
|
|
* {inheritDoc} |
208
|
|
|
* @return void |
209
|
|
|
*/ |
210
|
|
|
public function attached($presenter) |
211
|
|
|
{ |
212
|
|
|
parent::attached($presenter); |
213
|
|
|
|
214
|
|
|
if ($presenter instanceof Nette\Application\UI\Presenter) { |
215
|
|
|
/** |
216
|
|
|
* Get session |
217
|
|
|
*/ |
218
|
|
|
$this->grid_session = $this->getPresenter()->getSession($this->getSessionSectionName()); |
219
|
|
|
|
220
|
|
|
/** |
221
|
|
|
* Try to find previous filters/pagination/sort in session |
222
|
|
|
*/ |
223
|
|
|
$this->findSessionFilters(); |
224
|
|
|
} |
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
|
228
|
|
|
/** |
229
|
|
|
* Find some unique session key name |
230
|
|
|
* @return string |
231
|
|
|
*/ |
232
|
|
|
public function getSessionSectionName() |
233
|
|
|
{ |
234
|
|
|
return $this->getPresenter()->getName().':'.$this->getName(); |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
|
238
|
|
|
/** |
239
|
|
|
* Render template |
240
|
|
|
* @return void |
241
|
|
|
*/ |
242
|
|
|
public function render() |
243
|
|
|
{ |
244
|
|
|
/** |
245
|
|
|
* Check whether datagrid has set some columns, initiated data source, etc |
246
|
|
|
*/ |
247
|
|
|
if (!($this->dataModel instanceof DataModel)) { |
248
|
|
|
throw new DataGridException('You have to set a data source first.'); |
249
|
|
|
} |
250
|
|
|
|
251
|
|
|
if (empty($this->columns)) { |
252
|
|
|
throw new DataGridException('You have to add at least one column.'); |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
$this->template->setTranslator($this->getTranslator()); |
256
|
|
|
|
257
|
|
|
/** |
258
|
|
|
* Invoke some possible events |
259
|
|
|
*/ |
260
|
|
|
$this->onRender($this); |
261
|
|
|
|
262
|
|
|
/** |
263
|
|
|
* Prepare data for rendering (datagrid may render just one item) |
264
|
|
|
*/ |
265
|
|
|
$rows = []; |
266
|
|
|
|
267
|
|
|
if (!empty($this->redraw_item)) { |
268
|
|
|
$items = $this->dataModel->filterRow($this->redraw_item); |
269
|
|
|
} else { |
270
|
|
|
$items = Nette\Utils\Callback::invokeArgs( |
271
|
|
|
[$this->dataModel, 'filterData'], |
272
|
|
|
[ |
273
|
|
|
$this->getPaginator(), |
274
|
|
|
$this->sort, |
275
|
|
|
$this->assableFilters() |
276
|
|
|
] |
277
|
|
|
); |
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
foreach ($items as $item) { |
281
|
|
|
$rows[] = new Row($this, $item, $this->getPrimaryKey()); |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
if ($this->isTreeView()) { |
285
|
|
|
$this->template->tree_view_has_children_column = $this->tree_view_has_children_column; |
286
|
|
|
} |
287
|
|
|
|
288
|
|
|
$this->template->rows = $rows; |
289
|
|
|
|
290
|
|
|
$this->template->columns = $this->columns; |
291
|
|
|
$this->template->actions = $this->actions; |
292
|
|
|
$this->template->exports = $this->exports; |
293
|
|
|
$this->template->filters = $this->filters; |
294
|
|
|
|
295
|
|
|
$this->template->filter_active = $this->isFilterActive(); |
296
|
|
|
$this->template->original_template = $this->getOriginalTemplateFile(); |
297
|
|
|
$this->template->icon_prefix = static::$icon_prefix; |
298
|
|
|
$this->template->items_detail = $this->items_detail; |
299
|
|
|
|
300
|
|
|
/** |
301
|
|
|
* Walkaround for Latte (does not know $form in snippet in {form} etc) |
302
|
|
|
*/ |
303
|
|
|
$this->template->filter = $this['filter']; |
304
|
|
|
|
305
|
|
|
/** |
306
|
|
|
* Set template file and render it |
307
|
|
|
*/ |
308
|
|
|
$this->template->setFile($this->getTemplateFile())->render(); |
309
|
|
|
} |
310
|
|
|
|
311
|
|
|
|
312
|
|
|
/** |
313
|
|
|
* Return current paginator class |
314
|
|
|
* @return NULL|Components\DataGridPaginator\DataGridPaginator |
315
|
|
|
*/ |
316
|
|
|
public function getPaginator() |
317
|
|
|
{ |
318
|
|
|
if ($this->isPaginated() && $this->per_page !== 'all') { |
319
|
|
|
return $this['paginator']; |
320
|
|
|
} |
321
|
|
|
|
322
|
|
|
return NULL; |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
|
326
|
|
|
/** |
327
|
|
|
* @param string $primary_key |
328
|
|
|
*/ |
329
|
|
|
public function setPrimaryKey($primary_key) |
330
|
|
|
{ |
331
|
|
|
$this->primary_key = $primary_key; |
332
|
|
|
|
333
|
|
|
return $this; |
334
|
|
|
} |
335
|
|
|
|
336
|
|
|
|
337
|
|
|
/** |
338
|
|
|
* Set Grid data source |
339
|
|
|
* @param DataSource\IDataSource|array|\DibiFluent $source |
340
|
|
|
* @return DataGrid |
341
|
|
|
*/ |
342
|
|
|
public function setDataSource($source) |
343
|
|
|
{ |
344
|
|
|
if ($source instanceof DataSource\IDataSource) { |
|
|
|
|
345
|
|
|
// $source is ready for interact |
346
|
|
|
|
347
|
|
|
} else if (is_array($source)) { |
348
|
|
|
$data_source = new DataSource\ArrayDataSource($source); |
349
|
|
|
|
350
|
|
|
} else if ($source instanceof \DibiFluent) { |
351
|
|
|
$driver = $source->getConnection()->getDriver(); |
352
|
|
|
|
353
|
|
|
if ($driver instanceof \DibiOdbcDriver) { |
354
|
|
|
$data_source = new DataSource\DibiFluentMssqlDataSource($source, $this->primary_key); |
355
|
|
|
|
356
|
|
|
} else if ($driver instanceof \DibiMsSqlDriver) { |
357
|
|
|
$data_source = new DataSource\DibiFluentMssqlDataSource($source, $this->primary_key); |
358
|
|
|
|
359
|
|
|
} else { |
360
|
|
|
$data_source = new DataSource\DibiFluentDataSource($source, $this->primary_key); |
361
|
|
|
} |
362
|
|
|
|
363
|
|
|
} else if ($source instanceof Nette\Database\Table\Selection) { |
364
|
|
|
$data_source = new DataSource\NetteDatabaseTableDataSource($source, $this->primary_key); |
365
|
|
|
|
366
|
|
|
} else if ($source instanceof \Kdyby\Doctrine\QueryBuilder) { |
367
|
|
|
$data_source = new DataSource\DoctrineDataSource($source, $this->primary_key); |
368
|
|
|
|
369
|
|
|
} else { |
370
|
|
|
$data_source_class = $source ? get_class($source) : 'NULL'; |
371
|
|
|
throw new DataGridException("DataGrid can not take [$data_source_class] as data source."); |
372
|
|
|
} |
373
|
|
|
|
374
|
|
|
$this->dataModel = new DataModel($data_source); |
375
|
|
|
|
376
|
|
|
return $this; |
377
|
|
|
} |
378
|
|
|
|
379
|
|
|
|
380
|
|
|
/** |
381
|
|
|
* Is filter active? |
382
|
|
|
* @return boolean |
383
|
|
|
*/ |
384
|
|
|
public function isFilterActive() |
385
|
|
|
{ |
386
|
|
|
$is_filter = ArraysHelper::testTruthy($this->filter); |
387
|
|
|
|
388
|
|
|
return ($is_filter) || $this->force_filter_active; |
389
|
|
|
} |
390
|
|
|
|
391
|
|
|
|
392
|
|
|
/** |
393
|
|
|
* Tell that filter is active from whatever reasons |
394
|
|
|
* return self |
395
|
|
|
*/ |
396
|
|
|
public function setFilterActive() |
397
|
|
|
{ |
398
|
|
|
$this->force_filter_active = TRUE; |
399
|
|
|
|
400
|
|
|
return $this; |
401
|
|
|
} |
402
|
|
|
|
403
|
|
|
|
404
|
|
|
/** |
405
|
|
|
* If we want to sent some initial filter |
406
|
|
|
* @param array $filter |
407
|
|
|
*/ |
408
|
|
|
public function setFilter(array $filter) |
409
|
|
|
{ |
410
|
|
|
$this->filter = $filter; |
411
|
|
|
|
412
|
|
|
return $this; |
413
|
|
|
} |
414
|
|
|
|
415
|
|
|
|
416
|
|
|
/** |
417
|
|
|
* Set options of select "items_per_page" |
418
|
|
|
* @param array $items_per_page_list |
419
|
|
|
*/ |
420
|
|
|
public function setItemsPerPageList(array $items_per_page_list) |
421
|
|
|
{ |
422
|
|
|
$this->items_per_page_list = $items_per_page_list; |
423
|
|
|
|
424
|
|
|
return $this; |
425
|
|
|
} |
426
|
|
|
|
427
|
|
|
|
428
|
|
|
/** |
429
|
|
|
* Set custom template file to render |
430
|
|
|
* @param string $template_file |
431
|
|
|
*/ |
432
|
|
|
public function setTemplateFile($template_file) |
433
|
|
|
{ |
434
|
|
|
$this->template_file = $template_file; |
435
|
|
|
|
436
|
|
|
return $this; |
437
|
|
|
} |
438
|
|
|
|
439
|
|
|
|
440
|
|
|
/** |
441
|
|
|
* Get DataGrid template file |
442
|
|
|
* @return string |
443
|
|
|
*/ |
444
|
|
|
public function getTemplateFile() |
445
|
|
|
{ |
446
|
|
|
return $this->template_file ?: $this->getOriginalTemplateFile(); |
447
|
|
|
} |
448
|
|
|
|
449
|
|
|
|
450
|
|
|
/** |
451
|
|
|
* Get DataGrid original template file |
452
|
|
|
* @return string |
453
|
|
|
*/ |
454
|
|
|
public function getOriginalTemplateFile() |
455
|
|
|
{ |
456
|
|
|
return __DIR__.'/templates/datagrid.latte'; |
457
|
|
|
} |
458
|
|
|
|
459
|
|
|
|
460
|
|
|
/** |
461
|
|
|
* Order Grid to "be paginated" |
462
|
|
|
* @param bool $do |
463
|
|
|
*/ |
464
|
|
|
public function setPagination($do) |
465
|
|
|
{ |
466
|
|
|
$this->do_paginate = (bool) $do; |
467
|
|
|
|
468
|
|
|
return $this; |
469
|
|
|
} |
470
|
|
|
|
471
|
|
|
|
472
|
|
|
/** |
473
|
|
|
* Tell whether Grid is paginated |
474
|
|
|
* @return bool |
475
|
|
|
*/ |
476
|
|
|
public function isPaginated() |
477
|
|
|
{ |
478
|
|
|
return $this->do_paginate; |
479
|
|
|
} |
480
|
|
|
|
481
|
|
|
|
482
|
|
|
/** |
483
|
|
|
* Set grido to be sortable |
484
|
|
|
* @param bool $sortable |
485
|
|
|
*/ |
486
|
|
|
public function setSortable($sortable = TRUE) |
487
|
|
|
{ |
488
|
|
|
if ($this->getItemsDetail()) { |
489
|
|
|
throw new DataGridException('You can not use both sortable datagrid and items detail.'); |
490
|
|
|
} |
491
|
|
|
|
492
|
|
|
$this->sortable = (bool) $sortable; |
493
|
|
|
|
494
|
|
|
return $this; |
495
|
|
|
} |
496
|
|
|
|
497
|
|
|
|
498
|
|
|
/** |
499
|
|
|
* Tell whether DataGrid is sortable |
500
|
|
|
* @return bool |
501
|
|
|
*/ |
502
|
|
|
public function isSortable() |
503
|
|
|
{ |
504
|
|
|
return $this->sortable; |
505
|
|
|
} |
506
|
|
|
|
507
|
|
|
|
508
|
|
|
/** |
509
|
|
|
* Is tree view set? |
510
|
|
|
* @return boolean |
511
|
|
|
*/ |
512
|
|
|
public function isTreeView() |
513
|
|
|
{ |
514
|
|
|
return (bool) $this->tree_view_children_callback; |
515
|
|
|
} |
516
|
|
|
|
517
|
|
|
|
518
|
|
|
/** |
519
|
|
|
* Setting tree view |
520
|
|
|
* @param callable $get_children_callback |
521
|
|
|
* @param string $tree_view_has_children_column |
522
|
|
|
*/ |
523
|
|
|
public function setTreeView($get_children_callback, $tree_view_has_children_column = 'has_children') |
524
|
|
|
{ |
525
|
|
|
if (!is_callable($get_children_callback)) { |
526
|
|
|
throw new DataGridException( |
527
|
|
|
'Parameters to method DataGrid::setTreeView must be of type callable' |
528
|
|
|
); |
529
|
|
|
} |
530
|
|
|
|
531
|
|
|
$this->tree_view_children_callback = $get_children_callback; |
532
|
|
|
$this->tree_view_has_children_column = $tree_view_has_children_column; |
533
|
|
|
|
534
|
|
|
/** |
535
|
|
|
* TUrn off pagination |
536
|
|
|
*/ |
537
|
|
|
$this->setPagination(NULL); |
538
|
|
|
|
539
|
|
|
/** |
540
|
|
|
* Set tree view template file |
541
|
|
|
*/ |
542
|
|
|
if (!$this->template_file) { |
543
|
|
|
$this->setTemplateFile(__DIR__.'/templates/datagrid_tree.latte'); |
544
|
|
|
} |
545
|
|
|
|
546
|
|
|
return $this; |
547
|
|
|
} |
548
|
|
|
|
549
|
|
|
|
550
|
|
|
/******************************************************************************** |
551
|
|
|
* Columns * |
552
|
|
|
********************************************************************************/ |
553
|
|
|
|
554
|
|
|
|
555
|
|
|
/** |
556
|
|
|
* Add text column with no other formating |
557
|
|
|
* @param string $key |
558
|
|
|
* @param string $name |
559
|
|
|
* @param string|null $column |
560
|
|
|
* @return Column\Column |
561
|
|
|
*/ |
562
|
|
|
public function addColumnText($key, $name, $column = NULL) |
563
|
|
|
{ |
564
|
|
|
$this->addColumnCheck($key); |
565
|
|
|
$column = $column ?: $key; |
566
|
|
|
|
567
|
|
|
return $this->columns[$key] = new Column\ColumnText($column, $name); |
568
|
|
|
} |
569
|
|
|
|
570
|
|
|
|
571
|
|
|
/** |
572
|
|
|
* Add column with link |
573
|
|
|
* @param string $key |
574
|
|
|
* @param string $name |
575
|
|
|
* @param string|null $column |
576
|
|
|
* @return Column\Column |
577
|
|
|
*/ |
578
|
|
|
public function addColumnLink($key, $name, $href = NULL, $column = NULL, array $params = NULL) |
579
|
|
|
{ |
580
|
|
|
$this->addColumnCheck($key); |
581
|
|
|
$column = $column ?: $key; |
582
|
|
|
$href = $href ?: $key; |
583
|
|
|
|
584
|
|
|
if (NULL === $params) { |
585
|
|
|
$params = [$this->primary_key]; |
586
|
|
|
} |
587
|
|
|
|
588
|
|
|
return $this->columns[$key] = new Column\ColumnLink($this, $column, $name, $href, $params); |
589
|
|
|
} |
590
|
|
|
|
591
|
|
|
|
592
|
|
|
/** |
593
|
|
|
* Add column with possible number formating |
594
|
|
|
* @param string $key |
595
|
|
|
* @param string $name |
596
|
|
|
* @param string|null $column |
597
|
|
|
* @return Column\Column |
598
|
|
|
*/ |
599
|
|
|
public function addColumnNumber($key, $name, $column = NULL) |
600
|
|
|
{ |
601
|
|
|
$this->addColumnCheck($key); |
602
|
|
|
$column = $column ?: $key; |
603
|
|
|
|
604
|
|
|
return $this->columns[$key] = new Column\ColumnNumber($column, $name); |
605
|
|
|
} |
606
|
|
|
|
607
|
|
|
|
608
|
|
|
/** |
609
|
|
|
* Add column with date formating |
610
|
|
|
* @param string $key |
611
|
|
|
* @param string $name |
612
|
|
|
* @param string|null $column |
613
|
|
|
* @return Column\Column |
614
|
|
|
*/ |
615
|
|
|
public function addColumnDateTime($key, $name, $column = NULL) |
616
|
|
|
{ |
617
|
|
|
$this->addColumnCheck($key); |
618
|
|
|
$column = $column ?: $key; |
619
|
|
|
|
620
|
|
|
return $this->columns[$key] = new Column\ColumnDateTime($column, $name); |
621
|
|
|
} |
622
|
|
|
|
623
|
|
|
|
624
|
|
|
/** |
625
|
|
|
* Return existing column |
626
|
|
|
* @param string $key |
627
|
|
|
* @return Column\Column |
628
|
|
|
* @throws DataGridException |
629
|
|
|
*/ |
630
|
|
|
public function getColumn($key) |
631
|
|
|
{ |
632
|
|
|
if (!isset($this->columns[$key])) { |
633
|
|
|
throw new DataGridException("There is no column at key [$key] defined."); |
634
|
|
|
} |
635
|
|
|
|
636
|
|
|
return $this->columns[$key]; |
637
|
|
|
} |
638
|
|
|
|
639
|
|
|
|
640
|
|
|
/** |
641
|
|
|
* Remove column |
642
|
|
|
* @param string $key |
643
|
|
|
* @return void |
644
|
|
|
*/ |
645
|
|
|
public function removeColumn($key) |
646
|
|
|
{ |
647
|
|
|
unset($this->columns[$key]); |
648
|
|
|
} |
649
|
|
|
|
650
|
|
|
|
651
|
|
|
/** |
652
|
|
|
* Check whether given key already exists in $this->columns |
653
|
|
|
* @param string $key |
654
|
|
|
* @throws DataGridException |
655
|
|
|
*/ |
656
|
|
|
protected function addColumnCheck($key) |
657
|
|
|
{ |
658
|
|
|
if (isset($this->columns[$key])) { |
659
|
|
|
throw new DataGridException("There is already column at key [$key] defined."); |
660
|
|
|
} |
661
|
|
|
} |
662
|
|
|
|
663
|
|
|
|
664
|
|
|
/******************************************************************************** |
665
|
|
|
* Actions * |
666
|
|
|
********************************************************************************/ |
667
|
|
|
|
668
|
|
|
|
669
|
|
|
/** |
670
|
|
|
* Create action |
671
|
|
|
* @param string $key |
672
|
|
|
* @param string $name |
673
|
|
|
* @param string $href |
674
|
|
|
* @param array|null $params |
675
|
|
|
*/ |
676
|
|
|
public function addAction($key, $name = '', $href = NULL, array $params = NULL) |
677
|
|
|
{ |
678
|
|
|
$this->addActionCheck($key); |
679
|
|
|
$href = $href ?: $key; |
680
|
|
|
|
681
|
|
|
if (NULL === $params) { |
682
|
|
|
$params = [$this->primary_key]; |
683
|
|
|
} |
684
|
|
|
|
685
|
|
|
return $this->actions[$key] = new Column\Action($this, $href, $name, $params); |
686
|
|
|
} |
687
|
|
|
|
688
|
|
|
|
689
|
|
|
/** |
690
|
|
|
* Get existing action |
691
|
|
|
* @param string $key |
692
|
|
|
* @return Column\Action |
693
|
|
|
* @throws DataGridException |
694
|
|
|
*/ |
695
|
|
|
public function getAction($key) |
696
|
|
|
{ |
697
|
|
|
if (!isset($this->actions[$key])) { |
698
|
|
|
throw new DataGridException("There is no action at key [$key] defined."); |
699
|
|
|
} |
700
|
|
|
|
701
|
|
|
return $this->actions[$key]; |
702
|
|
|
} |
703
|
|
|
|
704
|
|
|
|
705
|
|
|
/** |
706
|
|
|
* Remove action |
707
|
|
|
* @param string $key |
708
|
|
|
* @return void |
709
|
|
|
*/ |
710
|
|
|
public function removeAction($key) |
711
|
|
|
{ |
712
|
|
|
unset($this->actions[$key]); |
713
|
|
|
} |
714
|
|
|
|
715
|
|
|
|
716
|
|
|
/** |
717
|
|
|
* Check whether given key already exists in $this->filters |
718
|
|
|
* @param string $key |
719
|
|
|
* @throws DataGridException |
720
|
|
|
*/ |
721
|
|
|
protected function addActionCheck($key) |
722
|
|
|
{ |
723
|
|
|
if (isset($this->actions[$key])) { |
724
|
|
|
throw new DataGridException("There is already action at key [$key] defined."); |
725
|
|
|
} |
726
|
|
|
} |
727
|
|
|
|
728
|
|
|
|
729
|
|
|
/******************************************************************************** |
730
|
|
|
* Filters * |
731
|
|
|
********************************************************************************/ |
732
|
|
|
|
733
|
|
|
|
734
|
|
|
/** |
735
|
|
|
* Add filter fot text search |
736
|
|
|
* @param string $key |
737
|
|
|
* @param string $name |
738
|
|
|
* @param array|string $columns |
739
|
|
|
* @throws DataGridException |
740
|
|
|
*/ |
741
|
|
|
public function addFilterText($key, $name, $columns = NULL) |
742
|
|
|
{ |
743
|
|
|
$columns = NULL === $columns ? [$key] : (is_string($columns) ? [$columns] : $columns); |
744
|
|
|
|
745
|
|
|
if (!is_array($columns)) { |
746
|
|
|
throw new DataGridException("Filter Text can except only array or string."); |
747
|
|
|
} |
748
|
|
|
|
749
|
|
|
$this->addFilterCheck($key); |
750
|
|
|
|
751
|
|
|
return $this->filters[$key] = new Filter\FilterText($key, $name, $columns); |
752
|
|
|
} |
753
|
|
|
|
754
|
|
|
|
755
|
|
|
/** |
756
|
|
|
* Add select box filter |
757
|
|
|
* @param string $key |
758
|
|
|
* @param string $name |
759
|
|
|
* @param array $options |
760
|
|
|
* @param string $column |
761
|
|
|
* @throws DataGridException |
762
|
|
|
*/ |
763
|
|
|
public function addFilterSelect($key, $name, $options, $column = NULL) |
764
|
|
|
{ |
765
|
|
|
$column = $column ?: $key; |
766
|
|
|
|
767
|
|
|
if (!is_string($column)) { |
768
|
|
|
throw new DataGridException("Filter Select can only filter through one column."); |
769
|
|
|
} |
770
|
|
|
|
771
|
|
|
$this->addFilterCheck($key); |
772
|
|
|
|
773
|
|
|
return $this->filters[$key] = new Filter\FilterSelect($key, $name, $options, $column); |
774
|
|
|
} |
775
|
|
|
|
776
|
|
|
|
777
|
|
|
/** |
778
|
|
|
* Add datepicker filter |
779
|
|
|
* @param string $key |
780
|
|
|
* @param string $name |
781
|
|
|
* @param string $column |
782
|
|
|
* @throws DataGridException |
783
|
|
|
*/ |
784
|
|
|
public function addFilterDate($key, $name, $column = NULL) |
785
|
|
|
{ |
786
|
|
|
$column = $column ?: $key; |
787
|
|
|
|
788
|
|
|
if (!is_string($column)) { |
789
|
|
|
throw new DataGridException("FilterDate can only filter through one column."); |
790
|
|
|
} |
791
|
|
|
|
792
|
|
|
$this->addFilterCheck($key); |
793
|
|
|
|
794
|
|
|
return $this->filters[$key] = new Filter\FilterDate($key, $name, $column); |
795
|
|
|
} |
796
|
|
|
|
797
|
|
|
|
798
|
|
|
/** |
799
|
|
|
* Add range filter (from - to) |
800
|
|
|
* @param string $key |
801
|
|
|
* @param string $name |
802
|
|
|
* @param string $column |
803
|
|
|
* @throws DataGridException |
804
|
|
|
*/ |
805
|
|
|
public function addFilterRange($key, $name, $column = NULL, $name_second = '-') |
806
|
|
|
{ |
807
|
|
|
$column = $column ?: $key; |
808
|
|
|
|
809
|
|
|
if (!is_string($column)) { |
810
|
|
|
throw new DataGridException("FilterRange can only filter through one column."); |
811
|
|
|
} |
812
|
|
|
|
813
|
|
|
$this->addFilterCheck($key); |
814
|
|
|
|
815
|
|
|
return $this->filters[$key] = new Filter\FilterRange($key, $name, $column, $name_second); |
816
|
|
|
} |
817
|
|
|
|
818
|
|
|
|
819
|
|
|
/** |
820
|
|
|
* Add datepicker filter (from - to) |
821
|
|
|
* @param string $key |
822
|
|
|
* @param string $name |
823
|
|
|
* @param string $column |
824
|
|
|
* @throws DataGridException |
825
|
|
|
*/ |
826
|
|
|
public function addFilterDateRange($key, $name, $column = NULL, $name_second = '-') |
827
|
|
|
{ |
828
|
|
|
$column = $column ?: $key; |
829
|
|
|
|
830
|
|
|
if (!is_string($column)) { |
831
|
|
|
throw new DataGridException("FilterDateRange can only filter through one column."); |
832
|
|
|
} |
833
|
|
|
|
834
|
|
|
$this->addFilterCheck($key); |
835
|
|
|
|
836
|
|
|
return $this->filters[$key] = new Filter\FilterDateRange($key, $name, $column, $name_second); |
837
|
|
|
} |
838
|
|
|
|
839
|
|
|
|
840
|
|
|
/** |
841
|
|
|
* Check whether given key already exists in $this->filters |
842
|
|
|
* @param string $key |
843
|
|
|
* @throws DataGridException |
844
|
|
|
*/ |
845
|
|
|
protected function addFilterCheck($key) |
846
|
|
|
{ |
847
|
|
|
if (isset($this->filters[$key])) { |
848
|
|
|
throw new DataGridException("There is already action at key [$key] defined."); |
849
|
|
|
} |
850
|
|
|
} |
851
|
|
|
|
852
|
|
|
|
853
|
|
|
/** |
854
|
|
|
* Fill array of Filter\Filter[] with values from $this->filter persistent parameter |
855
|
|
|
* Fill array of Column\Column[] with values from $this->sort persistent parameter |
856
|
|
|
* @return Filter\Filter[] $this->filters === Filter\Filter[] |
857
|
|
|
*/ |
858
|
|
|
public function assableFilters() |
859
|
|
|
{ |
860
|
|
|
foreach ($this->filter as $key => $value) { |
861
|
|
|
if (!isset($this->filters[$key])) { |
862
|
|
|
$this->deleteSesssionData($key); |
863
|
|
|
|
864
|
|
|
continue; |
865
|
|
|
} |
866
|
|
|
|
867
|
|
|
if (is_array($value) || $value instanceof \Traversable) { |
868
|
|
|
if (!ArraysHelper::testEmpty($value)) { |
869
|
|
|
$this->filters[$key]->setValue($value); |
870
|
|
|
} |
871
|
|
|
} else { |
872
|
|
|
if ($value !== '' && $value !== NULL) { |
873
|
|
|
$this->filters[$key]->setValue($value); |
874
|
|
|
} |
875
|
|
|
} |
876
|
|
|
} |
877
|
|
|
|
878
|
|
|
foreach ($this->columns as $column) { |
879
|
|
|
if (isset($this->sort[$column->getColumnName()])) { |
880
|
|
|
$column->setSort($this->sort); |
881
|
|
|
} |
882
|
|
|
} |
883
|
|
|
|
884
|
|
|
return $this->filters; |
885
|
|
|
} |
886
|
|
|
|
887
|
|
|
|
888
|
|
|
/** |
889
|
|
|
* Try to restore session stuff |
890
|
|
|
* @return void |
891
|
|
|
*/ |
892
|
|
|
public function findSessionFilters() |
893
|
|
|
{ |
894
|
|
|
if ($this->filter || ($this->page != 1) || $this->sort || $this->per_page) { |
895
|
|
|
return; |
896
|
|
|
} |
897
|
|
|
|
898
|
|
|
if (!$this->remember_state) { |
899
|
|
|
return; |
900
|
|
|
} |
901
|
|
|
|
902
|
|
|
if ($page = $this->getSessionData('_grid_page')) { |
903
|
|
|
$this->page = $page; |
904
|
|
|
} |
905
|
|
|
|
906
|
|
|
if ($per_page = $this->getSessionData('_grid_per_page')) { |
907
|
|
|
$this->per_page = $per_page; |
908
|
|
|
} |
909
|
|
|
|
910
|
|
|
if ($sort = $this->getSessionData('_grid_sort')) { |
911
|
|
|
$this->sort = $sort; |
912
|
|
|
} |
913
|
|
|
|
914
|
|
|
foreach ($this->getSessionData() as $key => $value) { |
915
|
|
|
if (!in_array($key, ['_grid_per_page', '_grid_sort', '_grid_page'])) { |
916
|
|
|
$this->filter[$key] = $value; |
917
|
|
|
} |
918
|
|
|
} |
919
|
|
|
} |
920
|
|
|
|
921
|
|
|
|
922
|
|
|
/** |
923
|
|
|
* Remove filter |
924
|
|
|
* @param string $key |
925
|
|
|
* @return void |
926
|
|
|
*/ |
927
|
|
|
public function removeFilter($key) |
928
|
|
|
{ |
929
|
|
|
unset($this->filters[$key]); |
930
|
|
|
} |
931
|
|
|
|
932
|
|
|
|
933
|
|
|
/******************************************************************************** |
934
|
|
|
* Exports * |
935
|
|
|
********************************************************************************/ |
936
|
|
|
|
937
|
|
|
|
938
|
|
|
/** |
939
|
|
|
* Add export of type callback |
940
|
|
|
* @param string $text |
941
|
|
|
* @param callable $callback |
942
|
|
|
* @param boolean $filtered |
943
|
|
|
*/ |
944
|
|
|
public function addExportCallback($text, $callback, $filtered = FALSE) |
945
|
|
|
{ |
946
|
|
|
if (!is_callable($callback)) { |
947
|
|
|
throw new DataGridException("Second parameter of ExportCallback must be callable."); |
948
|
|
|
} |
949
|
|
|
|
950
|
|
|
return $this->addToExports(new Export\Export($text, $callback, $filtered)); |
951
|
|
|
} |
952
|
|
|
|
953
|
|
|
|
954
|
|
|
/** |
955
|
|
|
* Add already implemented csv export |
956
|
|
|
* @param string $text |
957
|
|
|
* @param string $csv_file_name |
958
|
|
|
*/ |
959
|
|
|
public function addExportCsv($text, $csv_file_name) |
960
|
|
|
{ |
961
|
|
|
return $this->addToExports(new Export\ExportCsv($text, $csv_file_name, FALSE)); |
962
|
|
|
} |
963
|
|
|
|
964
|
|
|
|
965
|
|
|
/** |
966
|
|
|
* Add already implemented csv export, but for filtered data |
967
|
|
|
* @param string $text |
968
|
|
|
* @param string $csv_file_name |
969
|
|
|
*/ |
970
|
|
|
public function addExportCsvFiltered($text, $csv_file_name) |
971
|
|
|
{ |
972
|
|
|
return $this->addToExports(new Export\ExportCsv($text, $csv_file_name, TRUE)); |
973
|
|
|
} |
974
|
|
|
|
975
|
|
|
|
976
|
|
|
/** |
977
|
|
|
* Add export to array |
978
|
|
|
* @param Export\Export $export |
979
|
|
|
*/ |
980
|
|
|
protected function addToExports(Export\Export $export) |
981
|
|
|
{ |
982
|
|
|
$id = ($s = sizeof($this->exports)) ? ($s + 1) : 1; |
983
|
|
|
|
984
|
|
|
$export->setLink($this->link('export!', ['id' => $id])); |
985
|
|
|
|
986
|
|
|
return $this->exports[$id] = $export; |
987
|
|
|
} |
988
|
|
|
|
989
|
|
|
|
990
|
|
|
/******************************************************************************** |
991
|
|
|
* Group actions * |
992
|
|
|
********************************************************************************/ |
993
|
|
|
|
994
|
|
|
|
995
|
|
|
/** |
996
|
|
|
* Add group actino |
997
|
|
|
* @param string $title |
998
|
|
|
* @param array $options |
999
|
|
|
*/ |
1000
|
|
|
public function addGroupAction($title, $options = []) |
1001
|
|
|
{ |
1002
|
|
|
return $this->getGroupActionCollection()->addGroupAction($title, $options); |
1003
|
|
|
} |
1004
|
|
|
|
1005
|
|
|
|
1006
|
|
|
/** |
1007
|
|
|
* Get collection of all group actions |
1008
|
|
|
* @return GroupAction\GroupActionCollection |
1009
|
|
|
*/ |
1010
|
|
|
public function getGroupActionCollection() |
1011
|
|
|
{ |
1012
|
|
|
if (!$this->group_action_collection) { |
1013
|
|
|
$this->group_action_collection = new GroupAction\GroupActionCollection(); |
1014
|
|
|
} |
1015
|
|
|
|
1016
|
|
|
return $this->group_action_collection; |
1017
|
|
|
} |
1018
|
|
|
|
1019
|
|
|
|
1020
|
|
|
/******************************************************************************** |
1021
|
|
|
* Signals * |
1022
|
|
|
********************************************************************************/ |
1023
|
|
|
|
1024
|
|
|
|
1025
|
|
|
/** |
1026
|
|
|
* Handler for changind page (just refresh site with page as persistent paramter set) |
1027
|
|
|
* @param int $page |
1028
|
|
|
* @return void |
1029
|
|
|
*/ |
1030
|
|
|
public function handlePage($page) |
1031
|
|
|
{ |
1032
|
|
|
/** |
1033
|
|
|
* Session stuff |
1034
|
|
|
*/ |
1035
|
|
|
$this->page = $page; |
1036
|
|
|
$this->saveSessionData('_grid_page', $page); |
1037
|
|
|
|
1038
|
|
|
$this->reload(['table']); |
1039
|
|
|
} |
1040
|
|
|
|
1041
|
|
|
|
1042
|
|
|
/** |
1043
|
|
|
* Handler for sorting |
1044
|
|
|
* @return void |
1045
|
|
|
*/ |
1046
|
|
|
public function handleSort(array $sort) |
1047
|
|
|
{ |
1048
|
|
|
/** |
1049
|
|
|
* Session stuff |
1050
|
|
|
*/ |
1051
|
|
|
$this->sort = $sort; |
1052
|
|
|
$this->saveSessionData('_grid_sort', $this->sort); |
1053
|
|
|
|
1054
|
|
|
$this->reload(['table']); |
1055
|
|
|
} |
1056
|
|
|
|
1057
|
|
|
|
1058
|
|
|
/** |
1059
|
|
|
* handler for reseting the filter |
1060
|
|
|
* @return void |
1061
|
|
|
*/ |
1062
|
|
|
public function handleResetFilter() |
1063
|
|
|
{ |
1064
|
|
|
/** |
1065
|
|
|
* Session stuff |
1066
|
|
|
*/ |
1067
|
|
|
$this->deleteSesssionData('_grid_page'); |
1068
|
|
|
|
1069
|
|
|
foreach ($this->getSessionData() as $key => $value) { |
1070
|
|
|
if (!in_array($key, ['_grid_per_page', '_grid_sort', '_grid_page'])) { |
1071
|
|
|
$this->deleteSesssionData($key); |
1072
|
|
|
if ($value instanceof \Traversable) { |
1073
|
|
|
foreach ($value as $key2 => $value2) { |
1074
|
|
|
$this->filter[$key][$key2] = NULL; |
1075
|
|
|
} |
1076
|
|
|
} else { |
1077
|
|
|
$this->filter[$key] = NULL; |
1078
|
|
|
} |
1079
|
|
|
} |
1080
|
|
|
} |
1081
|
|
|
|
1082
|
|
|
$this->reload(['grid']); |
1083
|
|
|
} |
1084
|
|
|
|
1085
|
|
|
|
1086
|
|
|
/** |
1087
|
|
|
* Handler for export |
1088
|
|
|
* @param int $id Key for particular export class in array $this->exports |
1089
|
|
|
* @return void |
1090
|
|
|
*/ |
1091
|
|
|
public function handleExport($id) |
1092
|
|
|
{ |
1093
|
|
|
if (!isset($this->exports[$id])) { |
1094
|
|
|
throw new Nette\Application\ForbiddenRequestException; |
1095
|
|
|
} |
1096
|
|
|
|
1097
|
|
|
$export = $this->exports[$id]; |
1098
|
|
|
|
1099
|
|
|
if ($export->isFiltered()) { |
1100
|
|
|
$sort = $this->sort; |
1101
|
|
|
$filter = $this->assableFilters(); |
1102
|
|
|
} else { |
1103
|
|
|
$sort = $this->primary_key; |
1104
|
|
|
$filter = []; |
1105
|
|
|
} |
1106
|
|
|
|
1107
|
|
|
if (NULL === $this->dataModel) { |
1108
|
|
|
throw new DataGridException('You have to set a data source first.'); |
1109
|
|
|
} |
1110
|
|
|
|
1111
|
|
|
$rows = []; |
1112
|
|
|
|
1113
|
|
|
$items = Nette\Utils\Callback::invokeArgs( |
1114
|
|
|
[$this->dataModel, 'filterData'], [NULL, $sort, $filter] |
1115
|
|
|
); |
1116
|
|
|
|
1117
|
|
|
foreach ($items as $item) { |
1118
|
|
|
$rows[] = new Row($this, $item, $this->getPrimaryKey()); |
1119
|
|
|
} |
1120
|
|
|
|
1121
|
|
|
if ($export instanceof Export\ExportCsv) { |
1122
|
|
|
$export->invoke($rows, $this); |
1123
|
|
|
} else { |
1124
|
|
|
$export->invoke($items, $this); |
1125
|
|
|
} |
1126
|
|
|
|
1127
|
|
|
if ($export->isAjax()) { |
1128
|
|
|
$this->reload(); |
1129
|
|
|
} |
1130
|
|
|
} |
1131
|
|
|
|
1132
|
|
|
|
1133
|
|
|
/** |
1134
|
|
|
* Handler for getting children of parent item (e.g. category) |
1135
|
|
|
* @param int $parent |
1136
|
|
|
* @return void |
1137
|
|
|
*/ |
1138
|
|
|
public function handleGetChildren($parent) |
1139
|
|
|
{ |
1140
|
|
|
$this->setDataSource( |
1141
|
|
|
call_user_func($this->tree_view_children_callback, $parent) |
1142
|
|
|
); |
1143
|
|
|
|
1144
|
|
|
if ($this->getPresenter()->isAjax()) { |
1145
|
|
|
$this->getPresenter()->payload->_datagrid_url = $this->refresh_url; |
1146
|
|
|
$this->getPresenter()->payload->_datagrid_tree = $parent; |
1147
|
|
|
|
1148
|
|
|
$this->redrawControl('items'); |
1149
|
|
|
} else { |
1150
|
|
|
$this->getPresenter()->redirect('this'); |
1151
|
|
|
} |
1152
|
|
|
} |
1153
|
|
|
|
1154
|
|
|
|
1155
|
|
|
/** |
1156
|
|
|
* Handler for getting item detail |
1157
|
|
|
* @param mixed $id |
1158
|
|
|
* @return void |
1159
|
|
|
*/ |
1160
|
|
|
public function handleGetItemDetail($id) |
1161
|
|
|
{ |
1162
|
|
|
$this->template->toggle_detail = $id; |
1163
|
|
|
$this->redraw_item = [$this->items_detail->getPrimaryWhereColumn() => $id]; |
1164
|
|
|
|
1165
|
|
|
if ($this->getPresenter()->isAjax()) { |
1166
|
|
|
$this->getPresenter()->payload->_datagrid_toggle_detail = $id; |
1167
|
|
|
$this->redrawControl('items'); |
1168
|
|
|
} else { |
1169
|
|
|
$this->getPresenter()->redirect('this'); |
1170
|
|
|
} |
1171
|
|
|
} |
1172
|
|
|
|
1173
|
|
|
|
1174
|
|
|
/** |
1175
|
|
|
* Handler for inline editing |
1176
|
|
|
* @param mixed $id |
1177
|
|
|
* @param mixed $key |
1178
|
|
|
* @return void |
1179
|
|
|
*/ |
1180
|
|
|
public function handleEdit($id, $key) |
1181
|
|
|
{ |
1182
|
|
|
$column = $this->getColumn($key); |
1183
|
|
|
$value = $this->getPresenter()->getRequest()->getPost('value'); |
1184
|
|
|
|
1185
|
|
|
call_user_func_array($column->getEditableCallback(), [$id, $value]); |
1186
|
|
|
} |
1187
|
|
|
|
1188
|
|
|
|
1189
|
|
|
/** |
1190
|
|
|
* Redraw $this |
1191
|
|
|
* @return void |
1192
|
|
|
*/ |
1193
|
|
|
public function reload($snippets = []) |
1194
|
|
|
{ |
1195
|
|
|
if ($this->getPresenter()->isAjax()) { |
1196
|
|
|
$this->redrawControl('tbody'); |
1197
|
|
|
$this->redrawControl('pagination'); |
1198
|
|
|
|
1199
|
|
|
/** |
1200
|
|
|
* manualy reset exports links... |
1201
|
|
|
*/ |
1202
|
|
|
$this->resetExportsLinks(); |
1203
|
|
|
$this->redrawControl('exports'); |
1204
|
|
|
|
1205
|
|
|
foreach ($snippets as $snippet) { |
1206
|
|
|
$this->redrawControl($snippet); |
1207
|
|
|
} |
1208
|
|
|
|
1209
|
|
|
$this->getPresenter()->payload->_datagrid_url = $this->refresh_url; |
1210
|
|
|
} else { |
1211
|
|
|
$this->getPresenter()->redirect('this'); |
1212
|
|
|
} |
1213
|
|
|
} |
1214
|
|
|
|
1215
|
|
|
|
1216
|
|
|
/** |
1217
|
|
|
* Redraw just one row via ajax |
1218
|
|
|
* @param int $id |
1219
|
|
|
* @param mixed $primary_where_column |
1220
|
|
|
* @return void |
1221
|
|
|
*/ |
1222
|
|
|
public function redrawItem($id, $primary_where_column = NULL) |
1223
|
|
|
{ |
1224
|
|
|
$this->redraw_item = [($primary_where_column ?: $this->primary_key) => $id]; |
1225
|
|
|
|
1226
|
|
|
$this->redrawControl('items'); |
1227
|
|
|
$this->getPresenter()->payload->_datagrid_url = $this->refresh_url; |
1228
|
|
|
} |
1229
|
|
|
|
1230
|
|
|
|
1231
|
|
|
/******************************************************************************** |
1232
|
|
|
* Components * |
1233
|
|
|
********************************************************************************/ |
1234
|
|
|
|
1235
|
|
|
|
1236
|
|
|
/** |
1237
|
|
|
* Paginator factory |
1238
|
|
|
* @return Components\DataGridPaginator\DataGridPaginator |
1239
|
|
|
*/ |
1240
|
|
|
public function createComponentPaginator() |
1241
|
|
|
{ |
1242
|
|
|
/** |
1243
|
|
|
* Init paginator |
1244
|
|
|
*/ |
1245
|
|
|
$component = new Components\DataGridPaginator\DataGridPaginator; |
1246
|
|
|
$paginator = $component->getPaginator(); |
1247
|
|
|
|
1248
|
|
|
$paginator->setPage($this->page); |
1249
|
|
|
$paginator->setItemsPerPage($this->getPerPage()); |
1250
|
|
|
|
1251
|
|
|
return $component; |
1252
|
|
|
} |
1253
|
|
|
|
1254
|
|
|
|
1255
|
|
|
/** |
1256
|
|
|
* PerPage form factory |
1257
|
|
|
* @return Form |
1258
|
|
|
*/ |
1259
|
|
|
public function createComponentPerPage() |
1260
|
|
|
{ |
1261
|
|
|
$form = new Form; |
1262
|
|
|
|
1263
|
|
|
$form->addSelect('per_page', '', $this->getItemsPerPageList()) |
1264
|
|
|
->setValue($this->getPerPage()); |
1265
|
|
|
|
1266
|
|
|
$form->addSubmit('submit', ''); |
1267
|
|
|
|
1268
|
|
|
$saveSessionData = [$this, 'saveSessionData']; |
1269
|
|
|
|
1270
|
|
|
$form->onSuccess[] = function($form, $values) use ($saveSessionData) { |
1271
|
|
|
/** |
1272
|
|
|
* Session stuff |
1273
|
|
|
*/ |
1274
|
|
|
$saveSessionData('_grid_per_page', $values->per_page); |
1275
|
|
|
|
1276
|
|
|
/** |
1277
|
|
|
* Other stuff |
1278
|
|
|
*/ |
1279
|
|
|
$this->per_page = $values->per_page; |
1280
|
|
|
$this->reload(); |
1281
|
|
|
}; |
1282
|
|
|
|
1283
|
|
|
return $form; |
1284
|
|
|
} |
1285
|
|
|
|
1286
|
|
|
|
1287
|
|
|
/** |
1288
|
|
|
* FilterAndGroupAction form factory |
1289
|
|
|
* @return Form |
1290
|
|
|
*/ |
1291
|
|
|
public function createComponentFilter() |
1292
|
|
|
{ |
1293
|
|
|
$form = new Form($this, 'filter'); |
1294
|
|
|
|
1295
|
|
|
$form->setMethod('get'); |
1296
|
|
|
|
1297
|
|
|
/** |
1298
|
|
|
* Filter part |
1299
|
|
|
*/ |
1300
|
|
|
$filter_container = $form->addContainer('filter'); |
1301
|
|
|
|
1302
|
|
|
foreach ($this->filters as $filter) { |
1303
|
|
|
$filter->addToFormContainer($filter_container, $filter_container); |
1304
|
|
|
} |
1305
|
|
|
|
1306
|
|
|
/** |
1307
|
|
|
* Group action part |
1308
|
|
|
*/ |
1309
|
|
|
$group_action_container = $form->addContainer('group_action'); |
1310
|
|
|
|
1311
|
|
|
if ($this->hasGroupActions()) { |
1312
|
|
|
$this->getGroupActionCollection()->addToFormContainer($group_action_container, $form, $this->getTranslator()); |
1313
|
|
|
} |
1314
|
|
|
|
1315
|
|
|
$form->setDefaults(['filter' => $this->filter]); |
1316
|
|
|
|
1317
|
|
|
$form->onSubmit[] = [$this, 'filterSucceeded']; |
1318
|
|
|
|
1319
|
|
|
return $form; |
1320
|
|
|
} |
1321
|
|
|
|
1322
|
|
|
|
1323
|
|
|
/** |
1324
|
|
|
* Set $this->filter values after filter form submitted |
1325
|
|
|
* @param Form $form |
1326
|
|
|
* @return void |
1327
|
|
|
*/ |
1328
|
|
|
public function filterSucceeded(Form $form) |
1329
|
|
|
{ |
1330
|
|
|
$values = $form->getValues(); |
1331
|
|
|
|
1332
|
|
|
if ($this->getPresenter()->isAjax()) { |
1333
|
|
|
if (isset($form['group_action']['submit']) && $form['group_action']['submit']->isSubmittedBy()) { |
1334
|
|
|
return; |
1335
|
|
|
} |
1336
|
|
|
} |
1337
|
|
|
|
1338
|
|
|
$values = $values['filter']; |
1339
|
|
|
|
1340
|
|
|
foreach ($values as $key => $value) { |
1341
|
|
|
/** |
1342
|
|
|
* Session stuff |
1343
|
|
|
*/ |
1344
|
|
|
$this->saveSessionData($key, $value); |
1345
|
|
|
|
1346
|
|
|
/** |
1347
|
|
|
* Other stuff |
1348
|
|
|
*/ |
1349
|
|
|
$this->filter[$key] = $value; |
1350
|
|
|
} |
1351
|
|
|
|
1352
|
|
|
$this->reload(); |
1353
|
|
|
} |
1354
|
|
|
|
1355
|
|
|
|
1356
|
|
|
/******************************************************************************** |
1357
|
|
|
* Support functions * |
1358
|
|
|
********************************************************************************/ |
1359
|
|
|
|
1360
|
|
|
|
1361
|
|
|
public function resetExportsLinks() |
1362
|
|
|
{ |
1363
|
|
|
foreach ($this->exports as $id => $export) { |
1364
|
|
|
$export->setLink($this->link('export!', ['id' => $id])); |
1365
|
|
|
} |
1366
|
|
|
} |
1367
|
|
|
|
1368
|
|
|
|
1369
|
|
|
/** |
1370
|
|
|
* Get parameter per_page |
1371
|
|
|
* @return int |
1372
|
|
|
*/ |
1373
|
|
|
public function getPerPage() |
1374
|
|
|
{ |
1375
|
|
|
$per_page = $this->per_page ?: reset($this->items_per_page_list); |
1376
|
|
|
|
1377
|
|
|
if ($per_page !== 'all' && !in_array($this->per_page, $this->items_per_page_list)) { |
1378
|
|
|
$per_page = reset($this->items_per_page_list); |
1379
|
|
|
} |
1380
|
|
|
|
1381
|
|
|
return $per_page; |
1382
|
|
|
} |
1383
|
|
|
|
1384
|
|
|
|
1385
|
|
|
/** |
1386
|
|
|
* Get associative array of items_per_page_list |
1387
|
|
|
* @return array |
1388
|
|
|
*/ |
1389
|
|
|
public function getItemsPerPageList() |
1390
|
|
|
{ |
1391
|
|
|
$list = array_flip($this->items_per_page_list); |
1392
|
|
|
|
1393
|
|
|
foreach ($list as $key => $value) { |
1394
|
|
|
$list[$key] = $key; |
1395
|
|
|
} |
1396
|
|
|
|
1397
|
|
|
$list['all'] = $this->getTranslator()->translate('Vše'); |
1398
|
|
|
|
1399
|
|
|
return $list; |
1400
|
|
|
} |
1401
|
|
|
|
1402
|
|
|
|
1403
|
|
|
/** |
1404
|
|
|
* Get primary key of datagrid data source |
1405
|
|
|
* @return string |
1406
|
|
|
*/ |
1407
|
|
|
public function getPrimaryKey() |
1408
|
|
|
{ |
1409
|
|
|
return $this->primary_key; |
1410
|
|
|
} |
1411
|
|
|
|
1412
|
|
|
|
1413
|
|
|
/** |
1414
|
|
|
* Get set of set columns |
1415
|
|
|
* @return Column\IColumn[] |
1416
|
|
|
*/ |
1417
|
|
|
public function getColumns() |
1418
|
|
|
{ |
1419
|
|
|
return $this->columns; |
1420
|
|
|
} |
1421
|
|
|
|
1422
|
|
|
|
1423
|
|
|
/** |
1424
|
|
|
* Has datagrid some group actions? |
1425
|
|
|
* @return boolean |
1426
|
|
|
*/ |
1427
|
|
|
public function hasGroupActions() |
1428
|
|
|
{ |
1429
|
|
|
return (bool) $this->group_action_collection; |
1430
|
|
|
} |
1431
|
|
|
|
1432
|
|
|
|
1433
|
|
|
/** |
1434
|
|
|
* Get translator for datagrid |
1435
|
|
|
* @return Nette\Localization\ITranslator |
1436
|
|
|
*/ |
1437
|
|
|
public function getTranslator() |
1438
|
|
|
{ |
1439
|
|
|
if (!$this->translator) { |
1440
|
|
|
$this->translator = new Localization\SimpleTranslator; |
1441
|
|
|
} |
1442
|
|
|
|
1443
|
|
|
return $this->translator; |
1444
|
|
|
} |
1445
|
|
|
|
1446
|
|
|
|
1447
|
|
|
/** |
1448
|
|
|
* Set datagrid translator |
1449
|
|
|
* @param Nette\Localization\ITranslator $translator |
1450
|
|
|
*/ |
1451
|
|
|
public function setTranslator(Nette\Localization\ITranslator $translator) |
1452
|
|
|
{ |
1453
|
|
|
$this->translator = $translator; |
1454
|
|
|
|
1455
|
|
|
return $this; |
1456
|
|
|
} |
1457
|
|
|
|
1458
|
|
|
|
1459
|
|
|
/** |
1460
|
|
|
* Should be datagrid filters rendered separately? |
1461
|
|
|
* @param boolean $out |
1462
|
|
|
*/ |
1463
|
|
|
public function setOuterFilterRendering($out = TRUE) |
1464
|
|
|
{ |
1465
|
|
|
$this->outer_filter_rendering = (bool) $out; |
1466
|
|
|
} |
1467
|
|
|
|
1468
|
|
|
|
1469
|
|
|
/** |
1470
|
|
|
* Are datagrid filters rendered separately? |
1471
|
|
|
* @return boolean |
1472
|
|
|
*/ |
1473
|
|
|
public function hasOuterFilterRendering() |
1474
|
|
|
{ |
1475
|
|
|
return $this->outer_filter_rendering; |
1476
|
|
|
} |
1477
|
|
|
|
1478
|
|
|
|
1479
|
|
|
/** |
1480
|
|
|
* Set order of datagrid columns |
1481
|
|
|
* @param array $order |
1482
|
|
|
*/ |
1483
|
|
|
public function setColumnsOrder($order) |
1484
|
|
|
{ |
1485
|
|
|
$new_order = []; |
1486
|
|
|
|
1487
|
|
|
foreach ($order as $key) { |
1488
|
|
|
if (isset($this->columns[$key])) { |
1489
|
|
|
$new_order[$key] = $this->columns[$key]; |
1490
|
|
|
} |
1491
|
|
|
} |
1492
|
|
|
|
1493
|
|
|
if (sizeof($new_order) === sizeof($this->columns)) { |
1494
|
|
|
$this->columns = $new_order; |
1495
|
|
|
} else { |
1496
|
|
|
throw new DataGridException('When changing columns order, you have to specify all columns'); |
1497
|
|
|
} |
1498
|
|
|
} |
1499
|
|
|
|
1500
|
|
|
|
1501
|
|
|
/** |
1502
|
|
|
* Should datagrid remember its filters/pagination/etc using session? |
1503
|
|
|
* @param bool $remember |
1504
|
|
|
*/ |
1505
|
|
|
public function setRememberState($remember = TRUE) |
1506
|
|
|
{ |
1507
|
|
|
$this->remember_state = (bool) $remember; |
1508
|
|
|
|
1509
|
|
|
return $this |
1510
|
|
|
} |
|
|
|
|
1511
|
|
|
|
1512
|
|
|
|
1513
|
|
|
/** |
1514
|
|
|
* Should datagrid refresh url using history API? |
1515
|
|
|
* @param bool $refresh |
1516
|
|
|
*/ |
1517
|
|
|
public function setRefreshUrl($refresh = TRUE) |
1518
|
|
|
{ |
1519
|
|
|
$this->refresh_url = (bool) $refresh; |
1520
|
|
|
} |
1521
|
|
|
|
1522
|
|
|
|
1523
|
|
|
/** |
1524
|
|
|
* Get session data if functionality is enabled |
1525
|
|
|
* @param string $key |
1526
|
|
|
* @return mixed |
1527
|
|
|
*/ |
1528
|
|
|
public function getSessionData($key = NULL) |
1529
|
|
|
{ |
1530
|
|
|
if (!$this->remember_state) { |
1531
|
|
|
return NULL; |
1532
|
|
|
} |
1533
|
|
|
|
1534
|
|
|
return $key ? $this->grid_session->{$key} : $this->grid_session; |
1535
|
|
|
} |
1536
|
|
|
|
1537
|
|
|
|
1538
|
|
|
/** |
1539
|
|
|
* Save session data - just if it is enabled |
1540
|
|
|
* @param string $key |
1541
|
|
|
* @param mixed $value |
1542
|
|
|
* @return void |
1543
|
|
|
*/ |
1544
|
|
|
public function saveSessionData($key, $value) |
1545
|
|
|
{ |
1546
|
|
|
|
1547
|
|
|
if ($this->remember_state) { |
1548
|
|
|
$this->grid_session->{$key} = $value; |
1549
|
|
|
} |
1550
|
|
|
} |
1551
|
|
|
|
1552
|
|
|
|
1553
|
|
|
/** |
1554
|
|
|
* Delete session data |
1555
|
|
|
* @return void |
1556
|
|
|
*/ |
1557
|
|
|
public function deleteSesssionData($key) |
1558
|
|
|
{ |
1559
|
|
|
unset($this->grid_session->{$key}); |
1560
|
|
|
} |
1561
|
|
|
|
1562
|
|
|
|
1563
|
|
|
/** |
1564
|
|
|
* Get items detail parameters |
1565
|
|
|
* @return array |
1566
|
|
|
*/ |
1567
|
|
|
public function getItemsDetail() |
1568
|
|
|
{ |
1569
|
|
|
return $this->items_detail; |
1570
|
|
|
} |
1571
|
|
|
|
1572
|
|
|
|
1573
|
|
|
/** |
1574
|
|
|
* Items can have thair detail - toggled |
1575
|
|
|
* @param mixed $detail callable|string|bool |
1576
|
|
|
*/ |
1577
|
|
|
public function setItemsDetail($detail = TRUE, $primary_where_column = NULL) |
1578
|
|
|
{ |
1579
|
|
|
if ($this->isSortable()) { |
1580
|
|
|
throw new DataGridException('You can not use both sortable datagrid and items detail.'); |
1581
|
|
|
} |
1582
|
|
|
|
1583
|
|
|
$this->items_detail = new Column\ItemDetail( |
1584
|
|
|
$this, |
1585
|
|
|
$primary_where_column ?: $this->primary_key |
1586
|
|
|
); |
1587
|
|
|
|
1588
|
|
|
if (is_string($detail)) { |
1589
|
|
|
/** |
1590
|
|
|
* Item detail will be in separate template |
1591
|
|
|
*/ |
1592
|
|
|
$this->items_detail->setType('template'); |
1593
|
|
|
$this->items_detail->setTemplate($detail); |
1594
|
|
|
|
1595
|
|
|
} else if (is_callable($detail)) { |
1596
|
|
|
/** |
1597
|
|
|
* Item detail will be rendered via custom callback renderer |
1598
|
|
|
*/ |
1599
|
|
|
$this->items_detail->setType('renderer'); |
1600
|
|
|
$this->items_detail->setRenderer($detail); |
1601
|
|
|
|
1602
|
|
|
} else if (TRUE === $detail) { |
1603
|
|
|
/** |
1604
|
|
|
* Item detail will be rendered probably via block #detail |
1605
|
|
|
*/ |
1606
|
|
|
$this->items_detail->setType('block'); |
1607
|
|
|
|
1608
|
|
|
} else { |
1609
|
|
|
throw new DataGridException( |
1610
|
|
|
'::setItemsDetail() can be called either with no parameters or with parameter = template path or callable renderer.' |
1611
|
|
|
); |
1612
|
|
|
} |
1613
|
|
|
|
1614
|
|
|
return $this->items_detail; |
1615
|
|
|
} |
1616
|
|
|
|
1617
|
|
|
|
1618
|
|
|
/** |
1619
|
|
|
* Get cont of columns |
1620
|
|
|
* @return int |
1621
|
|
|
*/ |
1622
|
|
|
public function getColumnsCount() |
1623
|
|
|
{ |
1624
|
|
|
$count = sizeof($this->columns); |
1625
|
|
|
|
1626
|
|
|
if ($this->actions || $this->isSortable() || $this->getItemsDetail()) { |
1627
|
|
|
$count++; |
1628
|
|
|
} |
1629
|
|
|
|
1630
|
|
|
if ($this->hasGroupActions()) { |
1631
|
|
|
$count++; |
1632
|
|
|
} |
1633
|
|
|
|
1634
|
|
|
return $count; |
1635
|
|
|
} |
1636
|
|
|
|
1637
|
|
|
|
1638
|
|
|
public function allowRowsGroupAction(callable $condition) |
1639
|
|
|
{ |
1640
|
|
|
$this->row_conditions['group_action'] = $condition; |
1641
|
|
|
} |
1642
|
|
|
|
1643
|
|
|
|
1644
|
|
|
public function allowRowsAction($key, callable $condition) |
1645
|
|
|
{ |
1646
|
|
|
$this->row_conditions['action'][$key] = $condition; |
1647
|
|
|
} |
1648
|
|
|
|
1649
|
|
|
|
1650
|
|
|
public function getRowCondition($name, $key = NULL) |
1651
|
|
|
{ |
1652
|
|
|
if (!isset($this->row_conditions[$name])) { |
1653
|
|
|
return FALSE; |
1654
|
|
|
} |
1655
|
|
|
|
1656
|
|
|
$condition = $this->row_conditions[$name]; |
1657
|
|
|
|
1658
|
|
|
if (!$key) { |
1659
|
|
|
return $condition; |
1660
|
|
|
} |
1661
|
|
|
|
1662
|
|
|
return isset($condition[$key]) ? $condition[$key] : FALSE; |
1663
|
|
|
} |
1664
|
|
|
|
1665
|
|
|
} |
1666
|
|
|
|
1667
|
|
|
|
1668
|
|
|
class DataGridException extends \Exception |
|
|
|
|
1669
|
|
|
{ |
1670
|
|
|
} |
1671
|
|
|
|
This check looks for the bodies of
if
statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.These
if
bodies can be removed. If you have an empty if but statements in theelse
branch, consider inverting the condition.could be turned into
This is much more concise to read.