Completed
Push — master ( dc823a...493190 )
by Martin
04:54 queued 02:13
created

DataGrid::setDataSource()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 5
cts 5
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 6
nc 1
nop 1
crap 1
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 Nette\Application\UI\Link;
13
use Nette\Application\UI\PresenterComponent;
14
use Ublaboo\DataGrid\Utils\ArraysHelper;
15
use Nette\Application\UI\Form;
16
use Ublaboo\DataGrid\Exception\DataGridException;
17
use Ublaboo\DataGrid\Exception\DataGridColumnNotFoundException;
18
use Ublaboo\DataGrid\Exception\DataGridFilterNotFoundException;
19
use Ublaboo\DataGrid\Exception\DataGridHasToBeAttachedToPresenterComponentException;
20
use Ublaboo\DataGrid\Filter\IFilterDate;
21
use Ublaboo\DataGrid\Utils\DateTimeHelper;
22
use Ublaboo\DataGrid\Utils\Sorting;
23
use Ublaboo\DataGrid\InlineEdit\InlineEdit;
24
use Ublaboo\DataGrid\ColumnsSummary;
25
use Ublaboo\DataGrid\Toolbar\ToolbarButton;
26
use Ublaboo\DataGrid\AggregationFunction\TDataGridAggregationFunction;
27
28
/**
29
 * @method onRedraw()
30
 * @method onRender()
31
 * @method onColumnAdd()
32
 */
33 1
class DataGrid extends Nette\Application\UI\Control
34
{
35
36 1
	use TDataGridAggregationFunction;
37
38
	/**
39
	 * @var callable[]
40
	 */
41
	public $onRedraw;
42
43
	/**
44
	 * @var callable[]
45
	 */
46
	public $onRender = [];
47
48
	/**
49
	 * @var callable[]
50
	 */
51
	public $onExport;
52
53
	/**
54
	 * @var callable[]
55
	 */
56
	public $onColumnAdd;
57
58
	/**
59
	 * @var callable[]
60
	 * @deprecated use $onFiltersAssembled
61
	 */
62
	public $onFiltersAssabled;
63
64
	/**
65
	 * @var callable[]
66
	 */
67
	public $onFiltersAssembled;
68
69
	/**
70
	 * @var string
71
	 */
72
	public static $icon_prefix = 'fa fa-';
73
74
	/**
75
	 * Default form method
76
	 * @var string
77
	 */
78
	public static $form_method = 'post';
79
80
	/**
81
	 * When set to TRUE, datagrid throws an exception
82
	 * 	when tring to get related entity within join and entity does not exist
83
	 * @var bool
84
	 */
85
	public $strict_entity_property = FALSE;
86
87
	/**
88
	 * When set to TRUE, datagrid throws an exception
89
	 * 	when tring to set filter value, that does not exist (select, multiselect, etc)
90
	 * @var bool
91
	 */
92
	public $strict_session_filter_values = TRUE;
93
94
	/**
95
	 * @var int
96
	 * @persistent
97
	 */
98
	public $page = 1;
99
100
	/**
101
	 * @var int|string
102
	 * @persistent
103
	 */
104
	public $per_page;
105
106
	/**
107
	 * @var array
108
	 * @persistent
109
	 */
110
	public $sort = [];
111
112
	/**
113
	 * @var array
114
	 */
115
	public $default_sort = [];
116
117
	/**
118
	 * @var array
119
	 */
120
	public $default_filter = [];
121
122
	/**
123
	 * @var bool
124
	 */
125
	public $default_filter_use_on_reset = TRUE;
126
127
	/**
128
	 * @var bool
129
	 */
130
	public $default_sort_use_on_reset = TRUE;
131
132
	/**
133
	 * @var array
134
	 * @persistent
135
	 */
136
	public $filter = [];
137
138
	/**
139
	 * @var callable|null
140
	 */
141
	protected $sort_callback = NULL;
142
143
	/**
144
	 * @var bool
145
	 */
146
	protected $use_happy_components = TRUE;
147
148
	/**
149
	 * @var callable
150
	 */
151
	protected $rowCallback;
152
153
	/**
154
	 * @var array
155
	 */
156
	protected $items_per_page_list;
157
158
	/**
159
	 * @var int
160
	 */
161
	protected $default_per_page;
162
163
	/**
164
	 * @var string
165
	 */
166
	protected $template_file;
167
168
	/**
169
	 * @var Column\IColumn[]
170
	 */
171
	protected $columns = [];
172
173
	/**
174
	 * @var Column\Action[]
175
	 */
176
	protected $actions = [];
177
178
	/**
179
	 * @var GroupAction\GroupActionCollection
180
	 */
181
	protected $group_action_collection;
182
183
	/**
184
	 * @var Filter\Filter[]
185
	 */
186
	protected $filters = [];
187
188
	/**
189
	 * @var Export\Export[]
190
	 */
191
	protected $exports = [];
192
193
	/**
194
	 * @var ToolbarButton[]
195
	 */
196
	protected $toolbar_buttons = [];
197
198
	/**
199
	 * @var DataModel
200
	 */
201
	protected $dataModel;
202
203
	/**
204
	 * @var DataFilter
205
	 */
206
	protected $dataFilter;
207
208
	/**
209
	 * @var string
210
	 */
211
	protected $primary_key = 'id';
212
213
	/**
214
	 * @var bool
215
	 */
216
	protected $do_paginate = TRUE;
217
218
	/**
219
	 * @var bool
220
	 */
221
	protected $csv_export = TRUE;
222
223
	/**
224
	 * @var bool
225
	 */
226
	protected $csv_export_filtered = TRUE;
227
228
	/**
229
	 * @var bool
230
	 */
231
	protected $sortable = FALSE;
232
233
	/**
234
	 * @var bool
235
	 */
236
	protected $multiSort = FALSE;
237
238
	/**
239
	 * @var string
240
	 */
241
	protected $sortable_handler = 'sort!';
242
243
	/**
244
	 * @var string
245
	 */
246
	protected $original_template;
247
248
	/**
249
	 * @var array
250
	 */
251
	protected $redraw_item;
252
253
	/**
254
	 * @var mixed
255
	 */
256
	protected $translator;
257
258
	/**
259
	 * @var bool
260
	 */
261
	protected $force_filter_active;
262
263
	/**
264
	 * @var callable
265
	 */
266
	protected $tree_view_children_callback;
267
268
	/**
269
	 * @var callable
270
	 */
271
	protected $tree_view_has_children_callback;
272
273
	/**
274
	 * @var string
275
	 */
276
	protected $tree_view_has_children_column;
277
278
	/**
279
	 * @var bool
280
	 */
281
	protected $outer_filter_rendering = FALSE;
282
283
	/**
284
	 * @var bool
285
	 */
286
	protected $collapsible_outer_filters = TRUE;
287
288
	/**
289
	 * @var array
290
	 */
291
	protected $columns_export_order = [];
292
293
	/**
294
	 * @var bool
295
	 */
296
	protected $remember_state = TRUE;
297
298
	/**
299
	 * @var bool
300
	 */
301
	protected $refresh_url = TRUE;
302
303
	/**
304
	 * @var Nette\Http\SessionSection
305
	 */
306
	protected $grid_session;
307
308
	/**
309
	 * @var Column\ItemDetail
310
	 */
311
	protected $items_detail;
312
313
	/**
314
	 * @var array
315
	 */
316
	protected $row_conditions = [
317
		'group_action' => FALSE,
318
		'action' => []
319
	];
320
321
	/**
322
	 * @var array
323
	 */
324
	protected $column_callbacks = [];
325
326
	/**
327
	 * @var bool
328
	 */
329
	protected $can_hide_columns = FALSE;
330
331
	/**
332
	 * @var array
333
	 */
334
	protected $columns_visibility = [];
335
336
	/**
337
	 * @var InlineEdit
338
	 */
339
	protected $inlineEdit;
340
341
	/**
342
	 * @var InlineEdit
343
	 */
344
	protected $inlineAdd;
345
346
	/**
347
	 * @var bool
348
	 */
349
	protected $snippets_set = FALSE;
350
351
	/**
352
	 * @var bool
353
	 */
354
	protected $some_column_default_hide = FALSE;
355
356
	/**
357
	 * @var ColumnsSummary
358
	 */
359
	protected $columnsSummary;
360
361
	/**
362
	 * @var bool
363
	 */
364
	protected $auto_submit = TRUE;
365
366
	/**
367
	 * @var Filter\SubmitButton|NULL
368
	 */
369
	protected $filter_submit_button = NULL;
370
371
	/**
372
	 * @var bool
373
	 */
374
	protected $has_column_reset = TRUE;
375
376
377
	public function __construct()
378
	{
379 1
		parent::__construct();
380
381 1
		$this->monitor('Nette\Application\UI\Presenter');
382
383
		/**
384
		 * Try to find previous filters, pagination, per_page and other values in session
385
		 */
386 1
		$this->onRender[] = [$this, 'findSessionValues'];
387 1
		$this->onExport[] = [$this, 'findSessionValues'];
388
389
		/**
390
		 * Find default filter values
391
		 */
392 1
		$this->onRender[] = [$this, 'findDefaultFilter'];
393 1
		$this->onExport[] = [$this, 'findDefaultFilter'];
394
395
		/**
396
		 * Find default sort
397
		 */
398 1
		$this->onRender[] = [$this, 'findDefaultSort'];
399 1
		$this->onExport[] = [$this, 'findDefaultSort'];
400
401
		/**
402
		 * Find default items per page
403
		 */
404 1
		$this->onRender[] = [$this, 'findDefaultPerPage'];
405
406
		/**
407
		 * Notify about that json js extension
408
		 */
409 1
		$this->onFiltersAssembled[] = [$this, 'sendNonEmptyFiltersInPayload'];
410 1
	}
411
412
413
	/**
414
	 * {inheritDoc}
415
	 * @return void
416
	 */
417
	public function attached(Nette\ComponentModel\IComponent $presenter): void
418
	{
419 1
		parent::attached($presenter);
420
421 1
		if ($presenter instanceof Nette\Application\UI\Presenter) {
0 ignored issues
show
Bug introduced by
The class Nette\Application\UI\Presenter does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
422
			/**
423
			 * Get session
424
			 */
425 1
			if ($this->remember_state) {
426 1
				$this->grid_session = $presenter->getSession($this->getSessionSectionName());
427
			}
428
		}
429 1
	}
430
431
432
	/********************************************************************************
433
	 *                                  RENDERING                                   *
434
	 ********************************************************************************/
435
436
437
	/**
438
	 * Render template
439
	 * @return void
440
	 */
441
	public function render()
442
	{
443
		/**
444
		 * Check whether datagrid has set some columns, initiated data source, etc
445
		 */
446
		if (!($this->dataModel instanceof DataModel)) {
447
			throw new DataGridException('You have to set a data source first.');
448
		}
449
450
		if (empty($this->columns)) {
451
			throw new DataGridException('You have to add at least one column.');
452
		}
453
454
		$this->getTemplate()->setTranslator($this->getTranslator());
455
456
		/**
457
		 * Invoke possible events
458
		 */
459
		$this->onRender($this);
460
461
		/**
462
		 * Prepare data for rendering (datagrid may render just one item)
463
		 */
464
		$rows = [];
465
466
		if (!empty($this->redraw_item)) {
467
			$items = $this->dataModel->filterRow($this->redraw_item);
468
		} else {
469
			$items = Nette\Utils\Callback::invokeArgs(
470
				[$this->dataModel, 'filterData'],
471
				[
472
					$this->getPaginator(),
473
					$this->createSorting($this->sort, $this->sort_callback),
474
					$this->assembleFilters()
475
				]
476
			);
477
		}
478
479
		$callback = $this->rowCallback ?: NULL;
480
		$hasGroupActionOnRows = FALSE;
481
482
		foreach ($items as $item) {
483
			$rows[] = $row = new Row($this, $item, $this->getPrimaryKey());
484
485
			if (!$hasGroupActionOnRows && $row->hasGroupAction()){
486
				$hasGroupActionOnRows = TRUE;
487
			}
488
489
			if ($callback) {
490
				$callback($item, $row->getControl());
491
			}
492
493
			/**
494
			 * Walkaround for item snippet - snippet is the <tr> element and its class has to be also updated
495
			 */
496
			if (!empty($this->redraw_item)) {
497
				$this->getPresenter()->payload->_datagrid_redraw_item_class = $row->getControlClass();
498
				$this->getPresenter()->payload->_datagrid_redraw_item_id = $row->getId();
499
			}
500
		}
501
502
		if ($hasGroupActionOnRows){
503
			$hasGroupActionOnRows = $this->hasGroupActions();
504
		}
505
506
		if ($this->isTreeView()) {
507
			$this->getTemplate()->add('tree_view_has_children_column', $this->tree_view_has_children_column);
508
		}
509
510
		$this->getTemplate()->add('rows', $rows);
511
512
		$this->getTemplate()->add('columns', $this->getColumns());
513
		$this->getTemplate()->add('actions', $this->actions);
514
		$this->getTemplate()->add('exports', $this->exports);
515
		$this->getTemplate()->add('filters', $this->filters);
516
		$this->getTemplate()->add('toolbar_buttons', $this->toolbar_buttons);
517
		$this->getTemplate()->add('aggregation_functions', $this->getAggregationFunctions());
518
		$this->getTemplate()->add('multiple_aggregation_function', $this->getMultipleAggregationFunction());
519
520
		$this->getTemplate()->add('filter_active', $this->isFilterActive());
521
		$this->getTemplate()->add('original_template', $this->getOriginalTemplateFile());
522
		//$this->getTemplate()->add('icon_prefix', static::$icon_prefix);
0 ignored issues
show
Unused Code Comprehensibility introduced by
82% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
523
		$this->getTemplate()->icon_prefix = static::$icon_prefix;
524
		$this->getTemplate()->add('items_detail', $this->items_detail);
525
		$this->getTemplate()->add('columns_visibility', $this->getColumnsVisibility());
526
		$this->getTemplate()->add('columnsSummary', $this->columnsSummary);
527
528
		$this->getTemplate()->add('inlineEdit', $this->inlineEdit);
529
		$this->getTemplate()->add('inlineAdd', $this->inlineAdd);
530
531
		$this->getTemplate()->add('hasGroupActionOnRows', $hasGroupActionOnRows);
532
533
		/**
534
		 * Walkaround for Latte (does not know $form in snippet in {form} etc)
535
		 */
536
		$this->getTemplate()->add('filter', $this['filter']);
537
538
		/**
539
		 * Set template file and render it
540
		 */
541
		$this->getTemplate()->setFile($this->getTemplateFile());
542
		$this->getTemplate()->render();
543
	}
544
545
546
	/********************************************************************************
547
	 *                                 ROW CALLBACK                                 *
548
	 ********************************************************************************/
549
550
551
	/**
552
	 * Each row can be modified with user callback
553
	 * @param  callable  $callback
554
	 * @return static
555
	 */
556
	public function setRowCallback(callable $callback)
557
	{
558
		$this->rowCallback = $callback;
559
560
		return $this;
561
	}
562
563
564
	/********************************************************************************
565
	 *                                 DATA SOURCE                                  *
566
	 ********************************************************************************/
567
568
569
	/**
570
	 * By default ID, you can change that
571
	 * @param string $primary_key
572
	 * @return static
573
	 */
574
	public function setPrimaryKey($primary_key)
575
	{
576
		if ($this->dataModel instanceof DataModel) {
577
			throw new DataGridException('Please set datagrid primary key before setting datasource.');
578
		}
579
580
		$this->primary_key = $primary_key;
581
582
		return $this;
583
	}
584
585
586
	/**
587
	 * Set Grid data source
588
	 * @param DataSource\IDataSource|array|\DibiFluent|\Dibi\Fluent|Nette\Database\Table\Selection|\Doctrine\ORM\QueryBuilder $source
589
	 * @return static
590
	 */
591
	public function setDataSource($source)
592
	{
593 1
		$this->dataModel = new DataModel($source, $this->primary_key);
594
595 1
		$this->dataModel->onBeforeFilter[] = [$this, 'beforeDataModelFilter'];
596 1
		$this->dataModel->onAfterFilter[] = [$this, 'afterDataModelFilter'];
597 1
		$this->dataModel->onAfterPaginated[] = [$this, 'afterDataModelPaginated'];
598
599 1
		return $this;
600
	}
601
602
603
	/**
604
	 * @return DataSource\IDataSource|NULL
605
	 */
606
	public function getDataSource()
607
	{
608
		if (!$this->dataModel) {
609
			return NULL;
610
		}
611
612
		return $this->dataModel->getDataSource();
613
	}
614
615
616
	/********************************************************************************
617
	 *                                  TEMPLATING                                  *
618
	 ********************************************************************************/
619
620
621
	/**
622
	 * Set custom template file to render
623
	 * @param string $template_file
624
	 * @return static
625
	 */
626
	public function setTemplateFile($template_file)
627
	{
628
		$this->template_file = $template_file;
629
630
		return $this;
631
	}
632
633
634
	/**
635
	 * Get DataGrid template file
636
	 * @return string
637
	 * @return static
638
	 */
639
	public function getTemplateFile()
640
	{
641
		return $this->template_file ?: $this->getOriginalTemplateFile();
642
	}
643
644
645
	/**
646
	 * Get DataGrid original template file
647
	 * @return string
648
	 */
649
	public function getOriginalTemplateFile()
650
	{
651
		return __DIR__.'/templates/datagrid.latte';
652
	}
653
654
655
	/**
656
	 * Tell datagrid wheteher to use or not happy components
657
	 * @param  bool|NULL $use If not given, return value of static::$use_happy_components
658
	 * @return void|bool
659
	 */
660
	public function useHappyComponents($use = NULL)
661
	{
662
		if (NULL === $use) {
663
			return $this->use_happy_components;
664
		}
665
666
		$this->use_happy_components = (bool) $use;
667
	}
668
669
670
	/********************************************************************************
671
	 *                                   SORTING                                    *
672
	 ********************************************************************************/
673
674
675
	/**
676
	 * Set default sorting
677
	 * @param array $sort
678
	 * @param bool  $use_on_reset
679
	 * @return static
680
	 */
681
	public function setDefaultSort($sort, $use_on_reset = TRUE)
682
	{
683
		if (is_string($sort)) {
684
			$sort = [$sort => 'ASC'];
685
		} else {
686
			$sort = (array) $sort;
687
		}
688
689
		$this->default_sort = $sort;
690
		$this->default_sort_use_on_reset = (bool) $use_on_reset;
691
692
		return $this;
693
	}
694
695
696
	/**
697
	 * User may set default sorting, apply it
698
	 * @return void
699
	 */
700
	public function findDefaultSort()
701
	{
702 1
		if (!empty($this->sort)) {
703
			return;
704
		}
705
706 1
		if ($this->getSessionData('_grid_has_sorted')) {
707
			return;
708
		}
709
710 1
		if (!empty($this->default_sort)) {
711
			$this->sort = $this->default_sort;
712
		}
713
714 1
		$this->saveSessionData('_grid_sort', $this->sort);
715 1
	}
716
717
718
	/**
719
	 * Set grido to be sortable
720
	 * @param bool $sortable
721
	 * @return static
722
	 */
723
	public function setSortable($sortable = TRUE)
724
	{
725
		if ($this->getItemsDetail()) {
726
			throw new DataGridException('You can not use both sortable datagrid and items detail.');
727
		}
728
729
		$this->sortable = (bool) $sortable;
730
731
		return $this;
732
	}
733
734
735
	/**
736
	 * Tell whether DataGrid is sortable
737
	 * @return bool
738
	 */
739
	public function isSortable()
740
	{
741
		return $this->sortable;
742
	}
743
744
745
	/**
746
	 * Enable multi-sorting capability
747
	 * @param bool  $multiSort
748
	 * @return static
749
	 */
750
	public function setMultiSortEnabled($multiSort = TRUE)
751
	{
752
		$this->multiSort = (bool) $multiSort;
753
754
		return $this;
755
	}
756
757
758
	/**
759
	 * Tell wether DataGrid can be sorted by multiple columns
760
	 * @return bool
761
	 */
762
	public function isMultiSortEnabled()
763
	{
764
		return $this->multiSort;
765
	}
766
767
768
	/**
769
	 * Set sortable handle
770
	 * @param string $handler
771
	 * @return static
772
	 */
773
	public function setSortableHandler($handler = 'sort!')
774
	{
775
		$this->sortable_handler = (string) $handler;
776
777
		return $this;
778
	}
779
780
	/**
781
	 * Return sortable handle name
782
	 * @return string
783
	 */
784
	public function getSortableHandler()
785
	{
786
		return $this->sortable_handler;
787
	}
788
789
790
	/**
791
	 * @param Column  $column
792
	 * @return array
793
	 * @internal
794
	 */
795
	public function getSortNext(\Ublaboo\DataGrid\Column\Column $column)
796
	{
797
		$sort = $column->getSortNext();
798
799
		if ($this->isMultiSortEnabled()) {
800
			$sort = array_merge($this->sort, $sort);
801
		}
802
803
		return array_filter($sort);
804
	}
805
806
807
	/**
808
	 * @param  array         $sort
809
	 * @param  callable|NULL $sort_callback
810
	 * @return Sorting
811
	 */
812
	protected function createSorting(array $sort, callable $sort_callback = NULL)
813
	{
814 1
		foreach ($sort as $key => $order) {
815
			unset($sort[$key]);
816
817
			try {
818
				$column = $this->getColumn($key);
819
820
			} catch (DataGridColumnNotFoundException $e) {
821
				continue;
822
			}
823
824
			$sort[$column->getSortingColumn()] = $order;
825
		}
826
827 1
		if (!$sort_callback && isset($column)) {
828
			$sort_callback = $column->getSortableCallback();
829
		}
830
831 1
		return new Sorting($sort, $sort_callback);
832
	}
833
834
835
	/********************************************************************************
836
	 *                                  TREE VIEW                                   *
837
	 ********************************************************************************/
838
839
840
	/**
841
	 * Is tree view set?
842
	 * @return bool
843
	 */
844
	public function isTreeView()
845
	{
846
		return (bool) $this->tree_view_children_callback;
847
	}
848
849
850
	/**
851
	 * Setting tree view
852
	 * @param callable $get_children_callback
853
	 * @param string|callable $tree_view_has_children_column
854
	 * @return static
855
	 */
856
	public function setTreeView($get_children_callback, $tree_view_has_children_column = 'has_children')
857
	{
858
		if (!is_callable($get_children_callback)) {
859
			throw new DataGridException(
860
				'Parameters to method DataGrid::setTreeView must be of type callable'
861
			);
862
		}
863
864
		if (is_callable($tree_view_has_children_column)) {
865
			$this->tree_view_has_children_callback = $tree_view_has_children_column;
866
			$tree_view_has_children_column = NULL;
867
		}
868
869
		$this->tree_view_children_callback = $get_children_callback;
870
		$this->tree_view_has_children_column = $tree_view_has_children_column;
871
872
		/**
873
		 * TUrn off pagination
874
		 */
875
		$this->setPagination(FALSE);
876
877
		/**
878
		 * Set tree view template file
879
		 */
880
		if (!$this->template_file) {
881
			$this->setTemplateFile(__DIR__.'/templates/datagrid_tree.latte');
882
		}
883
884
		return $this;
885
	}
886
887
888
	/**
889
	 * Is tree view children callback set?
890
	 * @return bool
891
	 */
892
	public function hasTreeViewChildrenCallback()
893
	{
894
		return is_callable($this->tree_view_has_children_callback);
895
	}
896
897
898
	/**
899
	 * @param  mixed $item
900
	 * @return bool
901
	 */
902
	public function treeViewChildrenCallback($item)
903
	{
904
		return call_user_func($this->tree_view_has_children_callback, $item);
905
	}
906
907
908
	/********************************************************************************
909
	 *                                    COLUMNS                                   *
910
	 ********************************************************************************/
911
912
913
	/**
914
	 * Add text column with no other formating
915
	 * @param  string      $key
916
	 * @param  string      $name
917
	 * @param  string|null $column
918
	 * @return Column\ColumnText
919
	 */
920
	public function addColumnText($key, $name, $column = NULL)
921
	{
922 1
		$this->addColumnCheck($key);
923 1
		$column = $column ?: $key;
924
925 1
		return $this->addColumn($key, new Column\ColumnText($this, $key, $column, $name));
926
	}
927
928
929
	/**
930
	 * Add column with link
931
	 * @param  string      $key
932
	 * @param  string      $name
933
	 * @param  string|null $column
934
	 * @return Column\ColumnLink
935
	 */
936
	public function addColumnLink($key, $name, $href = NULL, $column = NULL, array $params = NULL)
937
	{
938 1
		$this->addColumnCheck($key);
939 1
		$column = $column ?: $key;
940 1
		$href = $href ?: $key;
941
942 1
		if (NULL === $params) {
943 1
			$params = [$this->primary_key];
944
		}
945
946 1
		return $this->addColumn($key, new Column\ColumnLink($this, $key, $column, $name, $href, $params));
947
	}
948
949
950
	/**
951
	 * Add column with possible number formating
952
	 * @param  string      $key
953
	 * @param  string      $name
954
	 * @param  string|null $column
955
	 * @return Column\ColumnNumber
956
	 */
957
	public function addColumnNumber($key, $name, $column = NULL)
958
	{
959 1
		$this->addColumnCheck($key);
960 1
		$column = $column ?: $key;
961
962 1
		return $this->addColumn($key, new Column\ColumnNumber($this, $key, $column, $name));
963
	}
964
965
966
	/**
967
	 * Add column with date formating
968
	 * @param  string      $key
969
	 * @param  string      $name
970
	 * @param  string|null $column
971
	 * @return Column\ColumnDateTime
972
	 */
973
	public function addColumnDateTime($key, $name, $column = NULL)
974
	{
975 1
		$this->addColumnCheck($key);
976 1
		$column = $column ?: $key;
977
978 1
		return $this->addColumn($key, new Column\ColumnDateTime($this, $key, $column, $name));
979
	}
980
981
982
	/**
983
	 * Add column status
984
	 * @param  string      $key
985
	 * @param  string      $name
986
	 * @param  string|null $column
987
	 * @return Column\ColumnStatus
988
	 */
989
	public function addColumnStatus($key, $name, $column = NULL)
990
	{
991 1
		$this->addColumnCheck($key);
992 1
		$column = $column ?: $key;
993
994 1
		return $this->addColumn($key, new Column\ColumnStatus($this, $key, $column, $name));
995
	}
996
997
998
	/**
999
	 * @param string $key
1000
	 * @param Column\Column $column
1001
	 * @return Column\Column
1002
	 */
1003
	protected function addColumn($key, Column\Column $column)
1004
	{
1005 1
		$this->onColumnAdd($key, $column);
1006
1007 1
		$this->columns_visibility[$key] = [
1008
			'visible' => TRUE
1009
		];
1010
1011 1
		return $this->columns[$key] = $column;
1012
	}
1013
1014
1015
	/**
1016
	 * Return existing column
1017
	 * @param  string $key
1018
	 * @return Column\Column
1019
	 * @throws DataGridException
1020
	 */
1021
	public function getColumn($key)
1022
	{
1023 1
		if (!isset($this->columns[$key])) {
1024
			throw new DataGridColumnNotFoundException("There is no column at key [$key] defined.");
1025
		}
1026
1027 1
		return $this->columns[$key];
1028
	}
1029
1030
1031
	/**
1032
	 * Remove column
1033
	 * @param string $key
1034
	 * @return static
1035
	 */
1036
	public function removeColumn($key)
1037
	{
1038 1
		unset($this->columns_visibility[$key]);
1039 1
		unset($this->columns[$key]);
1040
1041 1
		return $this;
1042
	}
1043
1044
1045
	/**
1046
	 * Check whether given key already exists in $this->columns
1047
	 * @param  string $key
1048
	 * @throws DataGridException
1049
	 */
1050
	protected function addColumnCheck($key)
1051
	{
1052 1
		if (isset($this->columns[$key])) {
1053
			throw new DataGridException("There is already column at key [$key] defined.");
1054
		}
1055 1
	}
1056
1057
1058
	/********************************************************************************
1059
	 *                                    ACTIONS                                   *
1060
	 ********************************************************************************/
1061
1062
1063
	/**
1064
	 * Create action
1065
	 * @param string     $key
1066
	 * @param string     $name
1067
	 * @param string     $href
1068
	 * @param array|null $params
1069
	 * @return Column\Action
1070
	 */
1071
	public function addAction($key, $name, $href = NULL, array $params = NULL)
1072
	{
1073 1
		$this->addActionCheck($key);
1074
1075 1
		$href = $href ?: $key;
1076
1077 1
		if (NULL === $params) {
1078 1
			$params = [$this->primary_key];
1079
		}
1080
1081 1
		return $this->actions[$key] = new Column\Action($this, $href, $name, $params);
1082
	}
1083
1084
1085
	/**
1086
	 * Create action callback
1087
	 * @param string     $key
1088
	 * @param string     $name
1089
	 * @return Column\Action
1090
	 */
1091
	public function addActionCallback($key, $name, $callback = NULL)
1092
	{
1093
		$this->addActionCheck($key);
1094
1095
		$params = ['__id' => $this->primary_key];
1096
1097
		$this->actions[$key] = $action = new Column\ActionCallback($this, $key, $name, $params);
1098
1099
		if ($callback) {
1100
			if (!is_callable($callback)) {
1101
				throw new DataGridException('ActionCallback callback has to be callable.');
1102
			}
1103
1104
			$action->onClick[] = $callback;
1105
		}
1106
1107
		return $action;
1108
	}
1109
1110
1111
	/**
1112
	 * @param string $key
1113
	 */
1114
	public function addMultiAction($key, $name)
1115
	{
1116
		$this->addActionCheck($key);
1117
1118
		$this->actions[$key] = $action = new Column\MultiAction($this, $name);
1119
1120
		return $action;
1121
	}
1122
1123
1124
	/**
1125
	 * Get existing action
1126
	 * @param  string       $key
1127
	 * @return Column\Action
1128
	 * @throws DataGridException
1129
	 */
1130
	public function getAction($key)
1131
	{
1132
		if (!isset($this->actions[$key])) {
1133
			throw new DataGridException("There is no action at key [$key] defined.");
1134
		}
1135
1136
		return $this->actions[$key];
1137
	}
1138
1139
1140
	/**
1141
	 * Remove action
1142
	 * @param string $key
1143
	 * @return static
1144
	 */
1145
	public function removeAction($key)
1146
	{
1147
		unset($this->actions[$key]);
1148
1149
		return $this;
1150
	}
1151
1152
1153
	/**
1154
	 * Check whether given key already exists in $this->filters
1155
	 * @param  string $key
1156
	 * @throws DataGridException
1157
	 */
1158
	protected function addActionCheck($key)
1159
	{
1160 1
		if (isset($this->actions[$key])) {
1161 1
			throw new DataGridException("There is already action at key [$key] defined.");
1162
		}
1163 1
	}
1164
1165
1166
	/********************************************************************************
1167
	 *                                    FILTERS                                   *
1168
	 ********************************************************************************/
1169
1170
1171
	/**
1172
	 * Add filter fot text search
1173
	 * @param string       $key
1174
	 * @param string       $name
1175
	 * @param array|string $columns
1176
	 * @return Filter\FilterText
1177
	 * @throws DataGridException
1178
	 */
1179
	public function addFilterText($key, $name, $columns = NULL)
1180
	{
1181 1
		$columns = NULL === $columns ? [$key] : (is_string($columns) ? [$columns] : $columns);
1182
1183 1
		if (!is_array($columns)) {
1184
			throw new DataGridException("Filter Text can accept only array or string.");
1185
		}
1186
1187 1
		$this->addFilterCheck($key);
1188
1189 1
		return $this->filters[$key] = new Filter\FilterText($this, $key, $name, $columns);
1190
	}
1191
1192
1193
	/**
1194
	 * Add select box filter
1195
	 * @param string $key
1196
	 * @param string $name
1197
	 * @param array  $options
1198
	 * @param string $column
1199
	 * @return Filter\FilterSelect
1200
	 * @throws DataGridException
1201
	 */
1202
	public function addFilterSelect($key, $name, array $options, $column = NULL)
1203
	{
1204
		$column = $column ?: $key;
1205
1206
		if (!is_string($column)) {
1207
			throw new DataGridException("Filter Select can only filter in one column.");
1208
		}
1209
1210
		$this->addFilterCheck($key);
1211
1212
		return $this->filters[$key] = new Filter\FilterSelect($this, $key, $name, $options, $column);
1213
	}
1214
1215
1216
	/**
1217
	 * Add multi select box filter
1218
	 * @param string $key
1219
	 * @param string $name
1220
	 * @param array  $options
1221
	 * @param string $column
1222
	 * @return Filter\FilterSelect
1223
	 * @throws DataGridException
1224
	 */
1225
	public function addFilterMultiSelect($key, $name, array $options, $column = NULL)
1226
	{
1227
		$column = $column ?: $key;
1228
1229
		if (!is_string($column)) {
1230
			throw new DataGridException("Filter MultiSelect can only filter in one column.");
1231
		}
1232
1233
		$this->addFilterCheck($key);
1234
1235
		return $this->filters[$key] = new Filter\FilterMultiSelect($this, $key, $name, $options, $column);
1236
	}
1237
1238
1239
	/**
1240
	 * Add datepicker filter
1241
	 * @param string $key
1242
	 * @param string $name
1243
	 * @param string $column
1244
	 * @return Filter\FilterDate
1245
	 * @throws DataGridException
1246
	 */
1247
	public function addFilterDate($key, $name, $column = NULL)
1248
	{
1249
		$column = $column ?: $key;
1250
1251
		if (!is_string($column)) {
1252
			throw new DataGridException("FilterDate can only filter in one column.");
1253
		}
1254
1255
		$this->addFilterCheck($key);
1256
1257
		return $this->filters[$key] = new Filter\FilterDate($this, $key, $name, $column);
1258
	}
1259
1260
1261
	/**
1262
	 * Add range filter (from - to)
1263
	 * @param string $key
1264
	 * @param string $name
1265
	 * @param string $column
1266
	 * @return Filter\FilterRange
1267
	 * @throws DataGridException
1268
	 */
1269
	public function addFilterRange($key, $name, $column = NULL, $name_second = '-')
1270
	{
1271
		$column = $column ?: $key;
1272
1273
		if (!is_string($column)) {
1274
			throw new DataGridException("FilterRange can only filter in one column.");
1275
		}
1276
1277
		$this->addFilterCheck($key);
1278
1279
		return $this->filters[$key] = new Filter\FilterRange($this, $key, $name, $column, $name_second);
1280
	}
1281
1282
1283
	/**
1284
	 * Add datepicker filter (from - to)
1285
	 * @param string $key
1286
	 * @param string $name
1287
	 * @param string $column
1288
	 * @return Filter\FilterDateRange
1289
	 * @throws DataGridException
1290
	 */
1291
	public function addFilterDateRange($key, $name, $column = NULL, $name_second = '-')
1292
	{
1293
		$column = $column ?: $key;
1294
1295
		if (!is_string($column)) {
1296
			throw new DataGridException("FilterDateRange can only filter in one column.");
1297
		}
1298
1299
		$this->addFilterCheck($key);
1300
1301
		return $this->filters[$key] = new Filter\FilterDateRange($this, $key, $name, $column, $name_second);
1302
	}
1303
1304
1305
	/**
1306
	 * Check whether given key already exists in $this->filters
1307
	 * @param  string $key
1308
	 * @throws DataGridException
1309
	 */
1310
	protected function addFilterCheck($key)
1311
	{
1312 1
		if (isset($this->filters[$key])) {
1313
			throw new DataGridException("There is already action at key [$key] defined.");
1314
		}
1315 1
	}
1316
1317
1318
	/**
1319
	 * Fill array of Filter\Filter[] with values from $this->filter persistent parameter
1320
	 * Fill array of Column\Column[] with values from $this->sort   persistent parameter
1321
	 * @return Filter\Filter[] $this->filters === Filter\Filter[]
1322
	 */
1323
	public function assembleFilters()
1324
	{
1325 1
		foreach ($this->filter as $key => $value) {
1326
			if (!isset($this->filters[$key])) {
1327
				$this->deleteSessionData($key);
1328
1329
				continue;
1330
			}
1331
1332
			if (is_array($value) || $value instanceof \Traversable) {
1333
				if (!ArraysHelper::testEmpty($value)) {
1334
					$this->filters[$key]->setValue($value);
1335
				}
1336
			} else {
1337
				if ($value !== '' && $value !== NULL) {
1338
					$this->filters[$key]->setValue($value);
1339
				}
1340
			}
1341
		}
1342
1343 1
		foreach ($this->columns as $key => $column) {
1344
			if (isset($this->sort[$key])) {
1345
				$column->setSort($this->sort);
1346
			}
1347
		}
1348
1349
		/**
1350
		 * Invoke possible events
1351
		 */
1352 1
		if (!empty($this->onFiltersAssabled)) {
0 ignored issues
show
Deprecated Code introduced by
The property Ublaboo\DataGrid\DataGrid::$onFiltersAssabled has been deprecated with message: use $onFiltersAssembled

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1353
			@trigger_error('onFiltersAssabled is deprecated, use onFiltersAssembled instead', E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1354
			$this->onFiltersAssabled($this->filters);
1355
		}
1356
1357 1
		$this->onFiltersAssembled($this->filters);
1358 1
		return $this->filters;
1359
	}
1360
1361
	/**
1362
	 * Fill array of Filter\Filter[] with values from $this->filter persistent parameter
1363
	 * Fill array of Column\Column[] with values from $this->sort   persistent parameter
1364
	 * @return Filter\Filter[] $this->filters === Filter\Filter[]
1365
	 * @deprecated use assembleFilters instead
1366
	 */
1367
	public function assableFilters()
1368
	{
1369
		@trigger_error('assableFilters is deprecated, use assembleFilters instead', E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1370
		return $this->assembleFilters();
1371
	}
1372
1373
1374
	/**
1375
	 * Remove filter
1376
	 * @param string $key
1377
	 * @return static
1378
	 */
1379
	public function removeFilter($key)
1380
	{
1381
		unset($this->filters[$key]);
1382
1383
		return $this;
1384
	}
1385
1386
1387
	/**
1388
	 * Get defined filter
1389
	 * @param  string $key
1390
	 * @return Filter\Filter
1391
	 */
1392
	public function getFilter($key)
1393
	{
1394
		if (!isset($this->filters[$key])) {
1395
			throw new DataGridException("Filter [{$key}] is not defined");
1396
		}
1397
1398
		return $this->filters[$key];
1399
	}
1400
1401
1402
	/**
1403
	 * @param bool $strict
1404
	 * @return static
1405
	 */
1406
	public function setStrictSessionFilterValues($strict = TRUE)
1407
	{
1408
		$this->strict_session_filter_values = (bool) $strict;
1409
1410
		return $this;
1411
	}
1412
1413
1414
	/********************************************************************************
1415
	 *                                  FILTERING                                   *
1416
	 ********************************************************************************/
1417
1418
1419
	/**
1420
	 * Is filter active?
1421
	 * @return bool
1422
	 */
1423
	public function isFilterActive()
1424
	{
1425
		$is_filter = ArraysHelper::testTruthy($this->filter);
1426
1427
		return ($is_filter) || $this->force_filter_active;
1428
	}
1429
1430
1431
	/**
1432
	 * Tell that filter is active from whatever reasons
1433
	 * return static
1434
	 */
1435
	public function setFilterActive()
1436
	{
1437
		$this->force_filter_active = TRUE;
1438
1439
		return $this;
1440
	}
1441
1442
1443
	/**
1444
	 * Set filter values (force - overwrite user data)
1445
	 * @param array $filter
1446
	 * @return static
1447
	 */
1448
	public function setFilter(array $filter)
1449
	{
1450
		$this->filter = $filter;
1451
1452
		$this->saveSessionData('_grid_has_filtered', 1);
1453
1454
		return $this;
1455
	}
1456
1457
1458
	/**
1459
	 * If we want to sent some initial filter
1460
	 * @param array $filter
0 ignored issues
show
Documentation introduced by
There is no parameter named $filter. Did you maybe mean $default_filter?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
1461
	 * @param bool  $use_on_reset
1462
	 * @return static
1463
	 */
1464
	public function setDefaultFilter(array $default_filter, $use_on_reset = TRUE)
1465
	{
1466
		foreach ($default_filter as $key => $value) {
1467
			$filter = $this->getFilter($key);
1468
1469
			if (!$filter) {
1470
				throw new DataGridException("Can not set default value to nonexisting filter [$key]");
1471
			}
1472
1473
			if ($filter instanceof Filter\FilterMultiSelect && !is_array($value)) {
1474
				throw new DataGridException(
1475
					"Default value of filter [$key] - MultiSelect has to be an array"
1476
				);
1477
			}
1478
1479
			if ($filter instanceof Filter\FilterRange || $filter instanceof Filter\FilterDateRange) {
1480
				if (!is_array($value)) {
1481
					throw new DataGridException(
1482
						"Default value of filter [$key] - Range/DateRange has to be an array [from/to => ...]"
1483
					);
1484
				}
1485
1486
				$temp = $value;
1487
				unset($temp['from'], $temp['to']);
1488
1489
				if (!empty($temp)) {
1490
					throw new DataGridException(
1491
						"Default value of filter [$key] - Range/DateRange can contain only [from/to => ...] values"
1492
					);
1493
				}
1494
			}
1495
		}
1496
1497
		$this->default_filter = $default_filter;
1498
		$this->default_filter_use_on_reset = (bool) $use_on_reset;
1499
1500
		return $this;
1501
	}
1502
1503
1504
	/**
1505
	 * User may set default filter, find it
1506
	 * @return void
1507
	 */
1508
	public function findDefaultFilter()
1509
	{
1510 1
		if (!empty($this->filter)) {
1511
			return;
1512
		}
1513
1514 1
		if ($this->getSessionData('_grid_has_filtered')) {
1515
			return;
1516
		}
1517
1518 1
		if (!empty($this->default_filter)) {
1519
			$this->filter = $this->default_filter;
1520
		}
1521
1522 1
		foreach ($this->filter as $key => $value) {
1523
			$this->saveSessionData($key, $value);
1524
		}
1525 1
	}
1526
1527
1528
	/**
1529
	 * FilterAndGroupAction form factory
1530
	 * @return Form
1531
	 */
1532
	public function createComponentFilter()
1533
	{
1534
		$form = new Form($this, 'filter');
1535
1536
		$form->setMethod(static::$form_method);
1537
1538
		$form->setTranslator($this->getTranslator());
1539
1540
		/**
1541
		 * InlineEdit part
1542
		 */
1543
		$inline_edit_container = $form->addContainer('inline_edit');
1544
1545
		if ($this->inlineEdit instanceof InlineEdit) {
1546
			$inline_edit_container->addSubmit('submit', 'ublaboo_datagrid.save')
1547
				->setValidationScope([$inline_edit_container]);
1548
			$inline_edit_container->addSubmit('cancel', 'ublaboo_datagrid.cancel')
1549
				->setValidationScope(FALSE);
1550
1551
			$this->inlineEdit->onControlAdd($inline_edit_container);
1552
			$this->inlineEdit->onControlAfterAdd($inline_edit_container);
1553
		}
1554
1555
		/**
1556
		 * InlineAdd part
1557
		 */
1558
		$inline_add_container = $form->addContainer('inline_add');
1559
1560
		if ($this->inlineAdd instanceof InlineEdit) {
1561
			$inline_add_container->addSubmit('submit', 'ublaboo_datagrid.save')
1562
				->setValidationScope([$inline_add_container]);
1563
			$inline_add_container->addSubmit('cancel', 'ublaboo_datagrid.cancel')
1564
				->setValidationScope(FALSE)
1565
				->setAttribute('data-datagrid-cancel-inline-add', TRUE);
1566
1567
			$this->inlineAdd->onControlAdd($inline_add_container);
1568
			$this->inlineAdd->onControlAfterAdd($inline_add_container);
1569
		}
1570
1571
		/**
1572
		 * ItemDetail form part
1573
		 */
1574
		$items_detail_form = $this->getItemDetailForm();
1575
1576
		if ($items_detail_form instanceof Nette\Forms\Container) {
0 ignored issues
show
Bug introduced by
The class Nette\Forms\Container does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
1577
			$form['items_detail_form'] = $items_detail_form;
1578
		}
1579
1580
		/**
1581
		 * Filter part
1582
		 */
1583
		$filter_container = $form->addContainer('filter');
1584
1585
		foreach ($this->filters as $filter) {
1586
			$filter->addToFormContainer($filter_container);
1587
		}
1588
1589
		if (!$this->hasAutoSubmit()) {
1590
			$filter_container['submit'] = $this->getFilterSubmitButton();
1591
		}
1592
1593
		/**
1594
		 * Group action part
1595
		 */
1596
		$group_action_container = $form->addContainer('group_action');
1597
1598
		if ($this->hasGroupActions()) {
1599
			$this->getGroupActionCollection()->addToFormContainer($group_action_container);
1600
		}
1601
1602
		if (!$form->isSubmitted()) {
1603
			$this->setFilterContainerDefaults($form['filter'], $this->filter);
1604
		}
1605
1606
		/**
1607
		 * Per page part
1608
		 */
1609
		$form->addSelect('per_page', '', $this->getItemsPerPageList())
1610
			->setTranslator(NULL);
1611
1612
		if (!$form->isSubmitted()) {
1613
			$form['per_page']->setValue($this->getPerPage());
1614
		}
1615
1616
		$form->addSubmit('per_page_submit', 'ublaboo_datagrid.per_page_submit');
1617
1618
		$form->onSubmit[] = [$this, 'filterSucceeded'];
1619
	}
1620
1621
1622
	/**
1623
	 * @param  Nette\Forms\Container  $container
1624
	 * @param  array|\Iterator  $values
1625
	 * @return void
1626
	 */
1627
	public function setFilterContainerDefaults(Nette\Forms\Container $container, $values)
1628
	{
1629
		foreach ($container->getComponents() as $key => $control) {
1630
			if (!isset($values[$key])) {
1631
				continue;
1632
			}
1633
1634
			if ($control instanceof Nette\Forms\Container) {
0 ignored issues
show
Bug introduced by
The class Nette\Forms\Container does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
1635
				$this->setFilterContainerDefaults($control, $values[$key]);
1636
1637
				continue;
1638
			}
1639
1640
			$value = $values[$key];
1641
1642
			if ($value instanceof \DateTime && ($filter = $this->getFilter($key)) instanceof IFilterDate) {
1643
				$value = $value->format($filter->getPhpFormat());
1644
			}
1645
1646
			try {
1647
				$control->setValue($value);
1648
1649
			} catch (Nette\InvalidArgumentException $e) {
0 ignored issues
show
Bug introduced by
The class Nette\InvalidArgumentException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
1650
				if ($this->strict_session_filter_values) {
1651
					throw $e;
1652
				}
1653
			}
1654
		}
1655
	}
1656
1657
1658
	/**
1659
	 * Set $this->filter values after filter form submitted
1660
	 * @param  Form $form
1661
	 * @return void
1662
	 */
1663
	public function filterSucceeded(Form $form)
1664
	{
1665
		if ($this->snippets_set) {
1666
			return;
1667
		}
1668
1669
		$values = $form->getValues();
1670
1671
		if ($this->getPresenter()->isAjax()) {
1672
			if (isset($form['group_action']['submit']) && $form['group_action']['submit']->isSubmittedBy()) {
1673
				return;
1674
			}
1675
		}
1676
1677
		/**
1678
		 * Per page
1679
		 */
1680
		$this->saveSessionData('_grid_per_page', $values->per_page);
1681
		$this->per_page = $values->per_page;
1682
1683
		/**
1684
		 * Inline edit
1685
		 */
1686
		if (isset($form['inline_edit']) && isset($form['inline_edit']['submit']) && isset($form['inline_edit']['cancel'])) {
1687
			$edit = $form['inline_edit'];
1688
1689
			if ($edit['submit']->isSubmittedBy() || $edit['cancel']->isSubmittedBy()) {
1690
				$id = $form->getHttpData(Form::DATA_LINE, 'inline_edit[_id]');
1691
				$primary_where_column = $form->getHttpData(
1692
					Form::DATA_LINE,
1693
					'inline_edit[_primary_where_column]'
1694
				);
1695
1696
				if ($edit['submit']->isSubmittedBy()) {
1697
					$this->inlineEdit->onSubmit($id, $values->inline_edit);
1698
					$this->getPresenter()->payload->_datagrid_inline_edited = $id;
1699
					$this->getPresenter()->payload->_datagrid_name = $this->getName();
0 ignored issues
show
Bug introduced by
Consider using $this->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
1700
				} else {
1701
					$this->getPresenter()->payload->_datagrid_inline_edit_cancel = $id;
1702
					$this->getPresenter()->payload->_datagrid_name = $this->getName();
0 ignored issues
show
Bug introduced by
Consider using $this->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
1703
				}
1704
1705
				if ($edit['submit']->isSubmittedBy() && !empty($this->inlineEdit->onCustomRedraw)) {
1706
					$this->inlineEdit->onCustomRedraw();
1707
				} else {
1708
					$this->redrawItem($id, $primary_where_column);
1709
				}
1710
1711
				return;
1712
			}
1713
		}
1714
1715
		/**
1716
		 * Inline add
1717
		 */
1718
		if (isset($form['inline_add']) && isset($form['inline_add']['submit']) && isset($form['inline_add']['cancel'])) {
1719
			$add = $form['inline_add'];
1720
1721
			if ($add['submit']->isSubmittedBy() || $add['cancel']->isSubmittedBy()) {
1722
				if ($add['submit']->isSubmittedBy()) {
1723
					$this->inlineAdd->onSubmit($values->inline_add);
1724
1725
					if ($this->getPresenter()->isAjax()) {
1726
						$this->getPresenter()->payload->_datagrid_inline_added = TRUE;
1727
					}
1728
				}
1729
1730
				return;
1731
			}
1732
		}
1733
1734
		/**
1735
		 * Filter itself
1736
		 */
1737
		$values = $values['filter'];
1738
1739
		foreach ($values as $key => $value) {
1740
			/**
1741
			 * Session stuff
1742
			 */
1743
			$this->saveSessionData($key, $value);
1744
1745
			/**
1746
			 * Other stuff
1747
			 */
1748
			$this->filter[$key] = $value;
1749
		}
1750
1751
		if (!empty($values)) {
1752
			$this->saveSessionData('_grid_has_filtered', 1);
1753
		}
1754
1755
		if ($this->getPresenter()->isAjax()) {
1756
			$this->getPresenter()->payload->_datagrid_sort = [];
1757
1758
			foreach ($this->columns as $key => $column) {
1759
				if ($column->isSortable()) {
1760
					$this->getPresenter()->payload->_datagrid_sort[$key] = $this->link('sort!', [
1761
						'sort' => $column->getSortNext()
1762
					]);
1763
				}
1764
			}
1765
		}
1766
1767
		$this->reload();
1768
	}
1769
1770
1771
	/**
1772
	 * Should be datagrid filters rendered separately?
1773
	 * @param bool $out
1774
	 * @return static
1775
	 */
1776
	public function setOuterFilterRendering($out = TRUE)
1777
	{
1778
		$this->outer_filter_rendering = (bool) $out;
1779
1780
		return $this;
1781
	}
1782
1783
1784
	/**
1785
	 * Are datagrid filters rendered separately?
1786
	 * @return bool
1787
	 */
1788
	public function hasOuterFilterRendering()
1789
	{
1790
		return $this->outer_filter_rendering;
1791
	}
1792
1793
1794
	/**
1795
	 * @param bool $collapsible_outer_filters
1796
	 */
1797
	public function setCollapsibleOuterFilters($collapsible_outer_filters = TRUE)
1798
	{
1799
		$this->collapsible_outer_filters = (bool) $collapsible_outer_filters;
1800
	}
1801
1802
1803
	/**
1804
	 * @return bool
1805
	 */
1806
	public function hasCollapsibleOuterFilters()
1807
	{
1808
		return $this->collapsible_outer_filters;
1809
	}
1810
1811
1812
	/**
1813
	 * Try to restore session stuff
1814
	 * @return void
1815
	 * @throws DataGridFilterNotFoundException
1816
	 */
1817
	public function findSessionValues()
1818
	{
1819 1
		if ($this->filter || ($this->page != 1) || !empty($this->sort) || $this->per_page) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->filter of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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.

Loading history...
1820
			return;
1821
		}
1822
1823 1
		if (!$this->remember_state) {
1824
			return;
1825
		}
1826
1827 1
		if ($page = $this->getSessionData('_grid_page')) {
1828
			$this->page = $page;
1829
		}
1830
1831 1
		if ($per_page = $this->getSessionData('_grid_per_page')) {
1832
			$this->per_page = $per_page;
1833
		}
1834
1835 1
		if ($sort = $this->getSessionData('_grid_sort')) {
1836
			$this->sort = $sort;
0 ignored issues
show
Documentation Bug introduced by
It seems like $sort of type * is incompatible with the declared type array of property $sort.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
1837
		}
1838
1839 1
		foreach ($this->getSessionData() as $key => $value) {
1840
			$other_session_keys = [
1841 1
				'_grid_per_page',
1842
				'_grid_sort',
1843
				'_grid_page',
1844
				'_grid_has_sorted',
1845
				'_grid_has_filtered',
1846
				'_grid_hidden_columns',
1847
				'_grid_hidden_columns_manipulated'
1848
			];
1849
1850 1
			if (!in_array($key, $other_session_keys)) {
1851
				try {
1852
					$this->getFilter($key);
1853
1854
					$this->filter[$key] = $value;
1855
1856
				} catch (DataGridException $e) {
1857
					if ($this->strict_session_filter_values) {
1858 1
						throw new DataGridFilterNotFoundException("Session filter: Filter [$key] not found");
1859
					}
1860
				}
1861
			}
1862
		}
1863
1864
		/**
1865
		 * When column is sorted via custom callback, apply it
1866
		 */
1867 1
		if (empty($this->sort_callback) && !empty($this->sort)) {
1868
			foreach ($this->sort as $key => $order) {
1869
				try {
1870
					$column = $this->getColumn($key);
1871
1872
				} catch (DataGridColumnNotFoundException $e) {
1873
					$this->deleteSessionData('_grid_sort');
1874
					$this->sort = [];
1875
1876
					return;
1877
				}
1878
1879
				if ($column && $column->isSortable() && is_callable($column->getSortableCallback())) {
1880
					$this->sort_callback = $column->getSortableCallback();
1881
				}
1882
			}
1883
		}
1884 1
	}
1885
1886
1887
	/********************************************************************************
1888
	 *                                    EXPORTS                                   *
1889
	 ********************************************************************************/
1890
1891
1892
	/**
1893
	 * Add export of type callback
1894
	 * @param string $text
1895
	 * @param callable $callback
1896
	 * @param bool $filtered
1897
	 * @return Export\Export
1898
	 */
1899
	public function addExportCallback($text, $callback, $filtered = FALSE)
1900
	{
1901 1
		if (!is_callable($callback)) {
1902
			throw new DataGridException("Second parameter of ExportCallback must be callable.");
1903
		}
1904
1905 1
		return $this->addToExports(new Export\Export($this, $text, $callback, $filtered));
1906
	}
1907
1908
1909
	/**
1910
	 * Add already implemented csv export
1911
	 * @param string $text
1912
	 * @param string $csv_file_name
1913
	 * @param string|null $output_encoding
1914
	 * @param string|null $delimiter
1915
	 * @param bool $include_bom
1916
	 * @return Export\Export
1917
	 */
1918
	public function addExportCsv($text, $csv_file_name, $output_encoding = NULL, $delimiter = NULL, $include_bom = FALSE)
1919
	{
1920 1
		return $this->addToExports(new Export\ExportCsv(
1921
			$this,
1922
			$text,
1923
			$csv_file_name,
1924 1
			FALSE,
1925
			$output_encoding,
1926
			$delimiter,
1927
			$include_bom
1928
		));
1929
	}
1930
1931
1932
	/**
1933
	 * Add already implemented csv export, but for filtered data
1934
	 * @param string $text
1935
	 * @param string $csv_file_name
1936
	 * @param string|null $output_encoding
1937
	 * @param string|null $delimiter
1938
	 * @param bool $include_bom
1939
	 * @return Export\Export
1940
	 */
1941
	public function addExportCsvFiltered($text, $csv_file_name, $output_encoding = NULL, $delimiter = NULL, $include_bom = FALSE)
1942
	{
1943
		return $this->addToExports(new Export\ExportCsv(
1944
			$this,
1945
			$text,
1946
			$csv_file_name,
1947
			TRUE,
1948
			$output_encoding,
1949
			$delimiter,
1950
			$include_bom
1951
		));
1952
	}
1953
1954
1955
	/**
1956
	 * Add export to array
1957
	 * @param Export\Export $export
1958
	 * @return Export\Export
1959
	 */
1960
	protected function addToExports(Export\Export $export)
1961
	{
1962 1
		$id = ($s = sizeof($this->exports)) ? ($s + 1) : 1;
1963
1964 1
		$export->setLink(new Link($this, 'export!', ['id' => $id]));
1965
1966 1
		return $this->exports[$id] = $export;
1967
	}
1968
1969
1970
	public function resetExportsLinks()
1971
	{
1972
		foreach ($this->exports as $id => $export) {
1973
			$export->setLink(new Link($this, 'export!', ['id' => $id]));
1974
		}
1975
	}
1976
1977
1978
	/********************************************************************************
1979
	 *                                TOOLBAR BUTTONS                               *
1980
	 ********************************************************************************/
1981
1982
1983
	/**
1984
	 * Add toolbar button
1985
	 * @param  string  $key
1986
	 * @param  string  $name
1987
	 * @param  string|NULL  $href
1988
	 * @param  array  $params
1989
	 * @return ToolbarButton
1990
	 * @throws DataGridException
1991
	 */
1992
	public function addToolbarButton(string $key, string $name, string $href = NULL, array $params = []) : ToolbarButton
1993
	{
1994
		if (isset($this->toolbar_buttons[$key])) {
1995
			throw new DataGridException("There is already toolbar button at key [$key] defined.");
1996
		}
1997
1998
		return $this->toolbar_buttons[$key] = new ToolbarButton($this, $href ?: $key, $name, $params);
1999
	}
2000
2001
2002
	/**
2003
	 * Get existing toolbar button
2004
	 * @param  string  $key
2005
	 * @return ToolbarButton
2006
	 * @throws DataGridException
2007
	 */
2008
	public function getToolbarButton(string $key) : ToolbarButton
2009
	{
2010
		if (!isset($this->toolbar_buttons[$key])) {
2011
			throw new DataGridException("There is no toolbar button at key [$key] defined.");
2012
		}
2013
2014
		return $this->toolbar_buttons[$key];
2015
	}
2016
2017
2018
	/**
2019
	 * Remove toolbar button.
2020
	 * @param  string $key
2021
	 * @return static
2022
	 */
2023
	public function removeToolbarButton(string $key)
2024
	{
2025
		unset($this->toolbar_buttons[$key]);
2026
2027
		return $this;
2028
	}
2029
2030
2031
	/********************************************************************************
2032
	 *                                 GROUP ACTIONS                                *
2033
	 ********************************************************************************/
2034
2035
2036
	/**
2037
	 * Alias for add group select action
2038
	 * @param string $title
2039
	 * @param array  $options
2040
	 * @return GroupAction\GroupAction
2041
	 */
2042
	public function addGroupAction($title, $options = [])
2043
	{
2044
		return $this->getGroupActionCollection()->addGroupSelectAction($title, $options);
2045
	}
2046
2047
2048
	/**
2049
	 * Add group action (select box)
2050
	 * @param string $title
2051
	 * @param array  $options
2052
	 * @return GroupAction\GroupAction
2053
	 */
2054
	public function addGroupSelectAction($title, $options = [])
2055
	{
2056
		return $this->getGroupActionCollection()->addGroupSelectAction($title, $options);
2057
	}
2058
2059
2060
	/**
2061
	 * Add group action (multiselect box)
2062
	 * @param string $title
2063
	 * @param array  $options
2064
	 * @return GroupAction\GroupAction
2065
	 */
2066
	public function addGroupMultiSelectAction($title, $options = [])
2067
	{
2068
		return $this->getGroupActionCollection()->addGroupMultiSelectAction($title, $options);
2069
	}
2070
2071
2072
	/**
2073
	 * Add group action (text input)
2074
	 * @param string $title
2075
	 * @return GroupAction\GroupAction
2076
	 */
2077
	public function addGroupTextAction($title)
2078
	{
2079
		return $this->getGroupActionCollection()->addGroupTextAction($title);
2080
	}
2081
2082
2083
	/**
2084
	 * Add group action (textarea)
2085
	 * @param string $title
2086
	 * @return GroupAction\GroupAction
2087
	 */
2088
	public function addGroupTextareaAction($title)
2089
	{
2090
		return $this->getGroupActionCollection()->addGroupTextareaAction($title);
2091
	}
2092
2093
2094
	/**
2095
	 * Get collection of all group actions
2096
	 * @return GroupAction\GroupActionCollection
2097
	 */
2098
	public function getGroupActionCollection()
2099
	{
2100
		if (!$this->group_action_collection) {
2101
			$this->group_action_collection = new GroupAction\GroupActionCollection($this);
2102
		}
2103
2104
		return $this->group_action_collection;
2105
	}
2106
2107
2108
	/**
2109
	 * Has datagrid some group actions?
2110
	 * @return bool
2111
	 */
2112
	public function hasGroupActions()
2113
	{
2114
		return (bool) $this->group_action_collection;
2115
	}
2116
2117
2118
	/********************************************************************************
2119
	 *                                   HANDLERS                                   *
2120
	 ********************************************************************************/
2121
2122
2123
	/**
2124
	 * Handler for changind page (just refresh site with page as persistent paramter set)
2125
	 * @param  int  $page
2126
	 * @return void
2127
	 */
2128
	public function handlePage($page)
2129
	{
2130
		/**
2131
		 * Session stuff
2132
		 */
2133
		$this->page = $page;
2134
		$this->saveSessionData('_grid_page', $page);
2135
2136
		$this->reload(['table']);
2137
	}
2138
2139
2140
	/**
2141
	 * Handler for sorting
2142
	 * @param array $sort
2143
	 * @return void
2144
	 * @throws DataGridColumnNotFoundException
2145
	 */
2146
	public function handleSort(array $sort)
2147
	{
2148
		foreach ($sort as $key => $value) {
2149
			try {
2150
				$column = $this->getColumn($key);
2151
2152
			} catch (DataGridColumnNotFoundException $e) {
2153
				unset($sort[$key]);
2154
				continue;
2155
			}
2156
2157
			if ($column->sortableResetPagination()) {
2158
				$this->saveSessionData('_grid_page', $this->page = 1);
2159
			}
2160
2161
			if ($column->getSortableCallback()) {
2162
				$this->sort_callback = $column->getSortableCallback();
2163
			}
2164
		}
2165
2166
		$this->saveSessionData('_grid_has_sorted', 1);
2167
		$this->saveSessionData('_grid_sort', $this->sort = $sort);
2168
		$this->reload(['table']);
2169
	}
2170
2171
2172
	/**
2173
	 * Handler for reseting the filter
2174
	 * @return void
2175
	 */
2176
	public function handleResetFilter()
2177
	{
2178
		/**
2179
		 * Session stuff
2180
		 */
2181
		$this->deleteSessionData('_grid_page');
2182
2183
		if ($this->default_filter_use_on_reset) {
2184
			$this->deleteSessionData('_grid_has_filtered');
2185
		}
2186
2187
		if ($this->default_sort_use_on_reset) {
2188
			$this->deleteSessionData('_grid_has_sorted');
2189
		}
2190
2191
		foreach ($this->getSessionData() as $key => $value) {
2192
			if (!in_array($key, [
2193
				'_grid_per_page',
2194
				'_grid_sort',
2195
				'_grid_page',
2196
				'_grid_has_filtered',
2197
				'_grid_has_sorted',
2198
				'_grid_hidden_columns',
2199
				'_grid_hidden_columns_manipulated'
2200
			])) {
2201
2202
				$this->deleteSessionData($key);
2203
			}
2204
		}
2205
2206
		$this->filter = [];
2207
2208
		$this->reload(['grid']);
2209
	}
2210
2211
2212
	/**
2213
	 * @param  string $key
2214
	 * @return void
2215
	 */
2216
	public function handleResetColumnFilter($key)
2217
	{
2218
		$this->deleteSessionData($key);
2219
		unset($this->filter[$key]);
2220
2221
		$this->reload(['grid']);
2222
	}
2223
2224
2225
	/**
2226
	 * @param bool $reset
2227
	 * @return static
2228
	 */
2229
	public function setColumnReset($reset = TRUE)
2230
	{
2231
		$this->has_column_reset = (bool) $reset;
2232
2233
		return $this;
2234
	}
2235
2236
2237
	/**
2238
	 * @return bool
2239
	 */
2240
	public function hasColumnReset()
2241
	{
2242 1
		return $this->has_column_reset;
2243
	}
2244
2245
2246
	/**
2247
	 * @param  Filter\Filter[] $filters
2248
	 * @return void
2249
	 */
2250
	public function sendNonEmptyFiltersInPayload($filters)
2251
	{
2252 1
		if (!$this->hasColumnReset()) {
2253
			return;
2254
		}
2255
2256 1
		$non_empty_filters = [];
2257
2258 1
		foreach ($filters as $filter) {
2259 1
			if ($filter->isValueSet()) {
2260 1
				$non_empty_filters[] = $filter->getKey();
2261
			}
2262
		}
2263
2264 1
		$this->getPresenter()->payload->non_empty_filters = $non_empty_filters;
2265 1
	}
2266
2267
2268
	/**
2269
	 * Handler for export
2270
	 * @param  int $id Key for particular export class in array $this->exports
2271
	 * @return void
2272
	 */
2273
	public function handleExport($id)
2274
	{
2275 1
		if (!isset($this->exports[$id])) {
2276
			throw new Nette\Application\ForbiddenRequestException;
2277
		}
2278
2279 1
		if (!empty($this->columns_export_order)) {
2280
			$this->setColumnsOrder($this->columns_export_order);
2281
		}
2282
2283 1
		$export = $this->exports[$id];
2284
2285
		/**
2286
		 * Invoke possible events
2287
		 */
2288 1
		$this->onExport($this);
2289
2290 1
		if ($export->isFiltered()) {
2291 1
			$sort      = $this->sort;
0 ignored issues
show
Unused Code introduced by
$sort is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2292 1
			$filter    = $this->assembleFilters();
2293
		} else {
2294 1
			$sort      = [$this->primary_key => 'ASC'];
0 ignored issues
show
Unused Code introduced by
$sort is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2295 1
			$filter    = [];
2296
		}
2297
2298 1
		if (NULL === $this->dataModel) {
2299 1
			throw new DataGridException('You have to set a data source first.');
2300
		}
2301
2302 1
		$rows = [];
2303
2304 1
		$items = Nette\Utils\Callback::invokeArgs(
2305 1
			[$this->dataModel, 'filterData'], [
2306 1
				NULL,
2307 1
				$this->createSorting($this->sort, $this->sort_callback),
2308 1
				$filter
2309
			]
2310
		);
2311
2312 1
		foreach ($items as $item) {
2313 1
			$rows[] = new Row($this, $item, $this->getPrimaryKey());
2314
		}
2315
2316 1
		if ($export instanceof Export\ExportCsv) {
2317 1
			$export->invoke($rows);
2318
		} else {
2319 1
			$export->invoke($items);
2320
		}
2321
2322 1
		if ($export->isAjax()) {
2323
			$this->reload();
2324
		}
2325 1
	}
2326
2327
2328
	/**
2329
	 * Handler for getting children of parent item (e.g. category)
2330
	 * @param  int $parent
2331
	 * @return void
2332
	 */
2333
	public function handleGetChildren($parent)
2334
	{
2335
		$this->setDataSource(
2336
			call_user_func($this->tree_view_children_callback, $parent)
2337
		);
2338
2339
		if ($this->getPresenter()->isAjax()) {
2340
			$this->getPresenter()->payload->_datagrid_url = $this->refresh_url;
2341
			$this->getPresenter()->payload->_datagrid_tree = $parent;
2342
2343
			$this->redrawControl('items');
2344
2345
			$this->onRedraw();
2346
		} else {
2347
			$this->getPresenter()->redirect('this');
2348
		}
2349
	}
2350
2351
2352
	/**
2353
	 * Handler for getting item detail
2354
	 * @param  mixed $id
2355
	 * @return void
2356
	 */
2357
	public function handleGetItemDetail($id)
2358
	{
2359
		$this->getTemplate()->add('toggle_detail', $id);
2360
		$this->redraw_item = [$this->items_detail->getPrimaryWhereColumn() => $id];
2361
2362
		if ($this->getPresenter()->isAjax()) {
2363
			$this->getPresenter()->payload->_datagrid_toggle_detail = $id;
2364
			$this->redrawControl('items');
2365
2366
			/**
2367
			 * Only for nette 2.4
2368
			 */
2369
			if (method_exists($this->getTemplate()->getLatte(), 'addProvider')) {
2370
				$this->redrawControl('gridSnippets');
2371
			}
2372
2373
			$this->onRedraw();
2374
		} else {
2375
			$this->getPresenter()->redirect('this');
2376
		}
2377
	}
2378
2379
2380
	/**
2381
	 * Handler for inline editing
2382
	 * @param  mixed $id
2383
	 * @param  mixed $key
2384
	 * @return void
2385
	 */
2386
	public function handleEdit($id, $key)
2387
	{
2388
		$column = $this->getColumn($key);
2389
		$value = $this->getPresenter()->getRequest()->getPost('value');
2390
2391
		call_user_func_array($column->getEditableCallback(), [$id, $value]);
2392
	}
2393
2394
2395
	/**
2396
	 * Redraw $this
2397
	 * @return void
2398
	 */
2399
	public function reload($snippets = [])
2400
	{
2401
		if ($this->getPresenter()->isAjax()) {
2402
			$this->redrawControl('tbody');
2403
			$this->redrawControl('pagination');
2404
2405
			/**
2406
			 * manualy reset exports links...
2407
			 */
2408
			$this->resetExportsLinks();
2409
			$this->redrawControl('exports');
2410
2411
			foreach ($snippets as $snippet) {
2412
				$this->redrawControl($snippet);
2413
			}
2414
2415
			$this->getPresenter()->payload->_datagrid_url = $this->refresh_url;
2416
			$this->getPresenter()->payload->_datagrid_name = $this->getName();
0 ignored issues
show
Bug introduced by
Consider using $this->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
2417
2418
			$this->onRedraw();
2419
		} else {
2420
			$this->getPresenter()->redirect('this');
2421
		}
2422
	}
2423
2424
2425
	/**
2426
	 * Handler for column status
2427
	 * @param  string $id
2428
	 * @param  string $key
2429
	 * @param  string $value
2430
	 * @return void
2431
	 */
2432
	public function handleChangeStatus($id, $key, $value)
2433
	{
2434
		if (empty($this->columns[$key])) {
2435
			throw new DataGridException("ColumnStatus[$key] does not exist");
2436
		}
2437
2438
		$this->columns[$key]->onChange($id, $value);
2439
	}
2440
2441
2442
	/**
2443
	 * Redraw just one row via ajax
2444
	 * @param  int   $id
2445
	 * @param  mixed $primary_where_column
2446
	 * @return void
2447
	 */
2448
	public function redrawItem($id, $primary_where_column = NULL)
2449
	{
2450
		$this->snippets_set = TRUE;
2451
2452
		$this->redraw_item = [($primary_where_column ?: $this->primary_key) => $id];
2453
2454
		$this->redrawControl('items');
2455
2456
		$this->getPresenter()->payload->_datagrid_url = $this->refresh_url;
2457
2458
		$this->onRedraw();
2459
	}
2460
2461
2462
	/**
2463
	 * Tell datagrid to display all columns
2464
	 * @return void
2465
	 */
2466
	public function handleShowAllColumns()
2467
	{
2468
		$this->deleteSessionData('_grid_hidden_columns');
2469
		$this->saveSessionData('_grid_hidden_columns_manipulated', TRUE);
2470
2471
		$this->redrawControl();
2472
2473
		$this->onRedraw();
2474
	}
2475
2476
2477
	/**
2478
	 * Tell datagrid to display default columns
2479
	 * @return void
2480
	 */
2481
	public function handleShowDefaultColumns()
2482
	{
2483
		$this->deleteSessionData('_grid_hidden_columns');
2484
		$this->saveSessionData('_grid_hidden_columns_manipulated', FALSE);
2485
2486
		$this->redrawControl();
2487
2488
		$this->onRedraw();
2489
	}
2490
2491
2492
	/**
2493
	 * Reveal particular column
2494
	 * @param  string $column
2495
	 * @return void
2496
	 */
2497
	public function handleShowColumn($column)
2498
	{
2499
		$columns = $this->getSessionData('_grid_hidden_columns');
2500
2501
		if (!empty($columns)) {
2502
			$pos = array_search($column, $columns);
2503
2504
			if ($pos !== FALSE) {
2505
				unset($columns[$pos]);
2506
			}
2507
		}
2508
2509
		$this->saveSessionData('_grid_hidden_columns', $columns);
2510
		$this->saveSessionData('_grid_hidden_columns_manipulated', TRUE);
2511
2512
		$this->redrawControl();
2513
2514
		$this->onRedraw();
2515
	}
2516
2517
2518
	/**
2519
	 * Notice datagrid to not display particular columns
2520
	 * @param  string $column
2521
	 * @return void
2522
	 */
2523
	public function handleHideColumn($column)
2524
	{
2525
		/**
2526
		 * Store info about hiding a column to session
2527
		 */
2528
		$columns = $this->getSessionData('_grid_hidden_columns');
2529
2530
		if (empty($columns)) {
2531
			$columns = [$column];
2532
		} else if (!in_array($column, $columns)) {
2533
			array_push($columns, $column);
2534
		}
2535
2536
		$this->saveSessionData('_grid_hidden_columns', $columns);
2537
		$this->saveSessionData('_grid_hidden_columns_manipulated', TRUE);
2538
2539
		$this->redrawControl();
2540
2541
		$this->onRedraw();
2542
	}
2543
2544
2545
	public function handleActionCallback($__key, $__id)
2546
	{
2547
		$action = $this->getAction($__key);
2548
2549
		if (!($action instanceof Column\ActionCallback)) {
2550
			throw new DataGridException("Action [$__key] does not exist or is not an callback aciton.");
2551
		}
2552
2553
		$action->onClick($__id);
2554
	}
2555
2556
2557
	/********************************************************************************
2558
	 *                                  PAGINATION                                  *
2559
	 ********************************************************************************/
2560
2561
2562
	/**
2563
	 * Set options of select "items_per_page"
2564
	 * @param array $items_per_page_list
2565
	 * @return static
2566
	 */
2567
	public function setItemsPerPageList(array $items_per_page_list, $include_all = TRUE)
2568
	{
2569
		$this->items_per_page_list = $items_per_page_list;
2570
2571
		if ($include_all) {
2572
			$this->items_per_page_list[] = 'all';
2573
		}
2574
2575
		return $this;
2576
	}
2577
2578
2579
	/**
2580
	 * Set default "items per page" value in pagination select
2581
	 * @param $count
2582
	 * @return static
2583
	 */
2584
	public function setDefaultPerPage($count)
2585
	{
2586
		$this->default_per_page = $count;
2587
2588
		return $this;
2589
	}
2590
2591
2592
	/**
2593
	 * User may set default "items per page" value, apply it
2594
	 * @return void
2595
	 */
2596
	public function findDefaultPerPage()
2597
	{
2598
		if (!empty($this->per_page)) {
2599
			return;
2600
		}
2601
2602
		if (!empty($this->default_per_page)) {
2603
			$this->per_page = $this->default_per_page;
2604
		}
2605
2606
		$this->saveSessionData('_grid_per_page', $this->per_page);
2607
	}
2608
2609
2610
	/**
2611
	 * Paginator factory
2612
	 * @return Components\DataGridPaginator\DataGridPaginator
2613
	 */
2614
	public function createComponentPaginator()
2615
	{
2616
		/**
2617
		 * Init paginator
2618
		 */
2619
		$component = new Components\DataGridPaginator\DataGridPaginator(
2620
			$this->getTranslator(),
2621
			static::$icon_prefix
2622
		);
2623
		$paginator = $component->getPaginator();
2624
2625
		$paginator->setPage($this->page);
2626
		$paginator->setItemsPerPage($this->getPerPage());
2627
2628
		return $component;
2629
	}
2630
2631
2632
	/**
2633
	 * Get parameter per_page
2634
	 * @return int
2635
	 */
2636
	public function getPerPage()
2637
	{
2638
		$items_per_page_list = $this->getItemsPerPageList();
2639
2640
		$per_page = $this->per_page ?: reset($items_per_page_list);
2641
2642
		if ($per_page !== 'all' && !in_array($this->per_page, $items_per_page_list)) {
2643
			$per_page = reset($items_per_page_list);
2644
		}
2645
2646
		return $per_page;
2647
	}
2648
2649
2650
	/**
2651
	 * Get associative array of items_per_page_list
2652
	 * @return array
2653
	 */
2654
	public function getItemsPerPageList()
2655
	{
2656
		if (empty($this->items_per_page_list)) {
2657
			$this->setItemsPerPageList([10, 20, 50], TRUE);
2658
		}
2659
2660
		$list = array_flip($this->items_per_page_list);
2661
2662
		foreach ($list as $key => $value) {
2663
			$list[$key] = $key;
2664
		}
2665
2666
		if (array_key_exists('all', $list)) {
2667
			$list['all'] = $this->getTranslator()->translate('ublaboo_datagrid.all');
2668
		}
2669
2670
		return $list;
2671
	}
2672
2673
2674
	/**
2675
	 * Order Grid to "be paginated"
2676
	 * @param bool $do
2677
	 * @return static
2678
	 */
2679
	public function setPagination($do)
2680
	{
2681
		$this->do_paginate = (bool) $do;
2682
2683
		return $this;
2684
	}
2685
2686
2687
	/**
2688
	 * Tell whether Grid is paginated
2689
	 * @return bool
2690
	 */
2691
	public function isPaginated()
2692
	{
2693
		return $this->do_paginate;
2694
	}
2695
2696
2697
	/**
2698
	 * Return current paginator class
2699
	 * @return NULL|Components\DataGridPaginator\DataGridPaginator
2700
	 */
2701
	public function getPaginator()
2702
	{
2703
		if ($this->isPaginated() && $this->getPerPage() !== 'all') {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison !== seems to always evaluate to true as the types of $this->getPerPage() (integer) and 'all' (string) can never be identical. Maybe you want to use a loose comparison != instead?
Loading history...
2704
			return $this['paginator'];
2705
		}
2706
2707
		return NULL;
2708
	}
2709
2710
2711
	/********************************************************************************
2712
	 *                                     I18N                                     *
2713
	 ********************************************************************************/
2714
2715
2716
	/**
2717
	 * Set datagrid translator
2718
	 * @param Nette\Localization\ITranslator $translator
2719
	 * @return static
2720
	 */
2721
	public function setTranslator(Nette\Localization\ITranslator $translator)
2722
	{
2723
		$this->translator = $translator;
2724
2725
		return $this;
2726
	}
2727
2728
2729
	/**
2730
	 * Get translator for datagrid
2731
	 * @return Nette\Localization\ITranslator
2732
	 */
2733
	public function getTranslator()
2734
	{
2735 1
		if (!$this->translator) {
2736 1
			$this->translator = new Localization\SimpleTranslator;
2737
		}
2738
2739 1
		return $this->translator;
2740
	}
2741
2742
2743
	/********************************************************************************
2744
	 *                                 COLUMNS ORDER                                *
2745
	 ********************************************************************************/
2746
2747
2748
	/**
2749
	 * Set order of datagrid columns
2750
	 * @param array $order
2751
	 * @return static
2752
	 */
2753
	public function setColumnsOrder($order)
2754
	{
2755
		$new_order = [];
2756
2757
		foreach ($order as $key) {
2758
			if (isset($this->columns[$key])) {
2759
				$new_order[$key] = $this->columns[$key];
2760
			}
2761
		}
2762
2763
		if (sizeof($new_order) === sizeof($this->columns)) {
2764
			$this->columns = $new_order;
2765
		} else {
2766
			throw new DataGridException('When changing columns order, you have to specify all columns');
2767
		}
2768
2769
		return $this;
2770
	}
2771
2772
2773
	/**
2774
	 * Columns order may be different for export and normal grid
2775
	 * @param array $order
2776
	 */
2777
	public function setColumnsExportOrder($order)
2778
	{
2779
		$this->columns_export_order = (array) $order;
2780
	}
2781
2782
2783
	/********************************************************************************
2784
	 *                                SESSION & URL                                 *
2785
	 ********************************************************************************/
2786
2787
2788
	/**
2789
	 * Find some unique session key name
2790
	 * @return string
2791
	 */
2792
	public function getSessionSectionName()
2793
	{
2794 1
		return $this->getPresenter()->getName().':'.$this->getUniqueId();
2795
	}
2796
2797
2798
	/**
2799
	 * Should datagrid remember its filters/pagination/etc using session?
2800
	 * @param bool $remember
2801
	 * @return static
2802
	 */
2803
	public function setRememberState($remember = TRUE)
2804
	{
2805
		$this->remember_state = (bool) $remember;
2806
2807
		return $this;
2808
	}
2809
2810
2811
	/**
2812
	 * Should datagrid refresh url using history API?
2813
	 * @param bool $refresh
2814
	 * @return static
2815
	 */
2816
	public function setRefreshUrl($refresh = TRUE)
2817
	{
2818
		$this->refresh_url = (bool) $refresh;
2819
2820
2821
		return $this;
2822
	}
2823
2824
2825
	/**
2826
	 * Get session data if functionality is enabled
2827
	 * @param  string $key
2828
	 * @return mixed
2829
	 */
2830
	public function getSessionData($key = NULL, $default_value = NULL)
2831
	{
2832 1
		if (!$this->remember_state) {
2833
			return $key ? $default_value : [];
2834
		}
2835
2836 1
		return ($key ? $this->grid_session->{$key} : $this->grid_session) ?: $default_value;
2837
	}
2838
2839
2840
	/**
2841
	 * Save session data - just if it is enabled
2842
	 * @param  string $key
2843
	 * @param  mixed  $value
2844
	 * @return void
2845
	 */
2846
	public function saveSessionData($key, $value)
2847
	{
2848 1
		if ($this->remember_state) {
2849 1
			$this->grid_session->{$key} = $value;
2850
		}
2851 1
	}
2852
2853
2854
	/**
2855
	 * Delete session data
2856
	 * @return void
2857
	 */
2858
	public function deleteSessionData($key)
2859
	{
2860
		unset($this->grid_session->{$key});
2861
	}
2862
2863
2864
	/**
2865
	 * Delete session data
2866
	 * @return void
2867
	 * @deprecated
2868
	 */
2869
	public function deleteSesssionData($key)
2870
	{
2871
		@trigger_error('deleteSesssionData is deprecated, use deleteSessionData instead', E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2872
		return $this->deleteSessionData($key);
2873
	}
2874
2875
2876
	/********************************************************************************
2877
	 *                                  ITEM DETAIL                                 *
2878
	 ********************************************************************************/
2879
2880
2881
	/**
2882
	 * Get items detail parameters
2883
	 * @return array
2884
	 */
2885
	public function getItemsDetail()
2886
	{
2887
		return $this->items_detail;
2888
	}
2889
2890
2891
	/**
2892
	 * Items can have thair detail - toggled
2893
	 * @param mixed $detail callable|string|bool
2894
	 * @param bool|NULL $primary_where_column
2895
	 * @return Column\ItemDetail
2896
	 */
2897
	public function setItemsDetail($detail = TRUE, $primary_where_column = NULL)
2898
	{
2899
		if ($this->isSortable()) {
2900
			throw new DataGridException('You can not use both sortable datagrid and items detail.');
2901
		}
2902
2903
		$this->items_detail = new Column\ItemDetail(
2904
			$this,
2905
			$primary_where_column ?: $this->primary_key
0 ignored issues
show
Bug introduced by
It seems like $primary_where_column ?: $this->primary_key can also be of type boolean; however, Ublaboo\DataGrid\Column\ItemDetail::__construct() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
2906
		);
2907
2908
		if (is_string($detail)) {
2909
			/**
2910
			 * Item detail will be in separate template
2911
			 */
2912
			$this->items_detail->setType('template');
2913
			$this->items_detail->setTemplate($detail);
2914
2915
		} else if (is_callable($detail)) {
2916
			/**
2917
			 * Item detail will be rendered via custom callback renderer
2918
			 */
2919
			$this->items_detail->setType('renderer');
2920
			$this->items_detail->setRenderer($detail);
2921
2922
		} else if (TRUE === $detail) {
2923
			/**
2924
			 * Item detail will be rendered probably via block #detail
2925
			 */
2926
			$this->items_detail->setType('block');
2927
2928
		} else {
2929
			throw new DataGridException(
2930
				'::setItemsDetail() can be called either with no parameters or with parameter = template path or callable renderer.'
2931
			);
2932
		}
2933
2934
		return $this->items_detail;
2935
	}
2936
2937
2938
	/**
2939
	 * @param callable $callable_set_container
2940
	 * @return static
2941
	 */
2942
	public function setItemsDetailForm(callable $callable_set_container)
2943
	{
2944
		if ($this->items_detail instanceof Column\ItemDetail) {
2945
			$this->items_detail->setForm(
2946
				new Utils\ItemDetailForm($callable_set_container)
2947
			);
2948
2949
			return $this;
2950
		}
2951
2952
		throw new DataGridException('Please set the ItemDetail first.');
2953
	}
2954
2955
2956
	/**
2957
	 * @return Nette\Forms\Container|NULL
2958
	 */
2959
	public function getItemDetailForm()
2960
	{
2961
		if ($this->items_detail instanceof Column\ItemDetail) {
2962
			return $this->items_detail->getForm();
2963
		}
2964
2965
		return NULL;
2966
	}
2967
2968
2969
	/********************************************************************************
2970
	 *                                ROW PRIVILEGES                                *
2971
	 ********************************************************************************/
2972
2973
2974
	/**
2975
	 * @param  callable $condition
2976
	 * @return void
2977
	 */
2978
	public function allowRowsGroupAction(callable $condition)
2979
	{
2980
		$this->row_conditions['group_action'] = $condition;
2981
	}
2982
2983
2984
	/**
2985
	 * @param  callable $condition
2986
	 * @return void
2987
	 */
2988
	public function allowRowsInlineEdit(callable $condition)
2989
	{
2990
		$this->row_conditions['inline_edit'] = $condition;
2991
	}
2992
2993
2994
	/**
2995
	 * @param  string   $key
2996
	 * @param  callable $condition
2997
	 * @return void
2998
	 */
2999
	public function allowRowsAction($key, callable $condition)
3000
	{
3001
		$this->row_conditions['action'][$key] = $condition;
3002
	}
3003
3004
3005
	/**
3006
	 * @param  string      $name
3007
	 * @param  string|null $key
3008
	 * @return bool|callable
3009
	 */
3010
	public function getRowCondition($name, $key = NULL)
3011
	{
3012
		if (!isset($this->row_conditions[$name])) {
3013
			return FALSE;
3014
		}
3015
3016
		$condition = $this->row_conditions[$name];
3017
3018
		if (!$key) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $key of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
3019
			return $condition;
3020
		}
3021
3022
		return isset($condition[$key]) ? $condition[$key] : FALSE;
3023
	}
3024
3025
3026
	/********************************************************************************
3027
	 *                               COLUMN CALLBACK                                *
3028
	 ********************************************************************************/
3029
3030
3031
	/**
3032
	 * @param  string   $key
3033
	 * @param  callable $callback
3034
	 * @return void
3035
	 */
3036
	public function addColumnCallback($key, callable $callback)
3037
	{
3038
		$this->column_callbacks[$key] = $callback;
3039
	}
3040
3041
3042
	/**
3043
	 * @param  string $key
3044
	 * @return callable|null
3045
	 */
3046
	public function getColumnCallback($key)
3047
	{
3048
		return empty($this->column_callbacks[$key]) ? NULL : $this->column_callbacks[$key];
3049
	}
3050
3051
3052
	/********************************************************************************
3053
	 *                                 INLINE EDIT                                  *
3054
	 ********************************************************************************/
3055
3056
3057
	/**
3058
	 * @return InlineEdit
3059
	 */
3060
	public function addInlineEdit($primary_where_column = NULL)
3061
	{
3062
		$this->inlineEdit = new InlineEdit($this, $primary_where_column ?: $this->primary_key);
3063
3064
		return $this->inlineEdit;
3065
	}
3066
3067
3068
	/**
3069
	 * @return InlineEdit|null
3070
	 */
3071
	public function getInlineEdit()
3072
	{
3073
		return $this->inlineEdit;
3074
	}
3075
3076
3077
	/**
3078
	 * @param  mixed $id
3079
	 * @return void
3080
	 */
3081
	public function handleInlineEdit($id)
3082
	{
3083
		if ($this->inlineEdit) {
3084
			$this->inlineEdit->setItemId($id);
3085
3086
			$primary_where_column = $this->inlineEdit->getPrimaryWhereColumn();
3087
3088
			$this['filter']['inline_edit']->addHidden('_id', $id);
3089
			$this['filter']['inline_edit']->addHidden('_primary_where_column', $primary_where_column);
3090
3091
			if ($this->getPresenter()->isAjax()) {
3092
				$this->getPresenter()->payload->_datagrid_inline_editing = TRUE;
3093
			}
3094
3095
			$this->redrawItem($id, $primary_where_column);
3096
		}
3097
	}
3098
3099
3100
	/********************************************************************************
3101
	 *                                  INLINE ADD                                  *
3102
	 ********************************************************************************/
3103
3104
3105
	/**
3106
	 * @return InlineEdit
3107
	 */
3108
	public function addInlineAdd()
3109
	{
3110
		$this->inlineAdd = new InlineEdit($this);
3111
3112
		$this->inlineAdd
3113
			->setTitle('ublaboo_datagrid.add')
3114
			->setIcon('plus')
3115
			->setClass('btn btn-xs btn-default');
3116
3117
		return $this->inlineAdd;
3118
	}
3119
3120
3121
	/**
3122
	 * @return InlineEdit|null
3123
	 */
3124
	public function getInlineAdd()
3125
	{
3126
		return $this->inlineAdd;
3127
	}
3128
3129
3130
	/********************************************************************************
3131
	 *                               HIDEABLE COLUMNS                               *
3132
	 ********************************************************************************/
3133
3134
3135
	/**
3136
	 * Can datagrid hide colums?
3137
	 * @return bool
3138
	 */
3139
	public function canHideColumns()
3140
	{
3141
		return (bool) $this->can_hide_columns;
3142
	}
3143
3144
3145
	/**
3146
	 * Order Grid to set columns hideable.
3147
	 * @return static
3148
	 */
3149
	public function setColumnsHideable()
3150
	{
3151
		$this->can_hide_columns = TRUE;
3152
3153
		return $this;
3154
	}
3155
3156
3157
	/********************************************************************************
3158
	 *                                COLUMNS SUMMARY                               *
3159
	 ********************************************************************************/
3160
3161
3162
	/**
3163
	 * Will datagrid show summary in the end?
3164
	 * @return bool
3165
	 */
3166
	public function hasColumnsSummary()
3167
	{
3168 1
		return $this->columnsSummary instanceof ColumnsSummary;
3169
	}
3170
3171
3172
	/**
3173
	 * Set columns to be summarized in the end.
3174
	 * @param array    $columns
3175
	 * @param callable $rowCallback
3176
	 * @return \Ublaboo\DataGrid\ColumnsSummary
3177
	 */
3178
	public function setColumnsSummary(array $columns, $rowCallback = NULL)
3179
	{
3180
		if ($this->hasSomeAggregationFunction()) {
3181
			throw new DataGridException('You can use either ColumnsSummary or AggregationFunctions');
3182
		}
3183
3184
		if (!empty($rowCallback)) {
3185
			if (!is_callable($rowCallback)) {
3186
				throw new \InvalidArgumentException('Row summary callback must be callable');
3187
			}
3188
		}
3189
3190
		$this->columnsSummary = new ColumnsSummary($this, $columns, $rowCallback);
3191
3192
		return $this->columnsSummary;
3193
	}
3194
3195
3196
	/**
3197
	 * @return ColumnsSummary|NULL
3198
	 */
3199
	public function getColumnsSummary()
3200
	{
3201
		return $this->columnsSummary;
3202
	}
3203
3204
3205
	/********************************************************************************
3206
	 *                                   INTERNAL                                   *
3207
	 ********************************************************************************/
3208
3209
3210
	/**
3211
	 * Tell grid filters to by submitted automatically
3212
	 * @param bool $auto
3213
	 */
3214
	public function setAutoSubmit($auto = TRUE)
3215
	{
3216
		$this->auto_submit = (bool) $auto;
3217
3218
		return $this;
3219
	}
3220
3221
3222
	/**
3223
	 * @return bool
3224
	 */
3225
	public function hasAutoSubmit()
3226
	{
3227
		return $this->auto_submit;
3228
	}
3229
3230
3231
	/**
3232
	 * Submit button when no auto-submitting is used
3233
	 * @return Filter\SubmitButton
3234
	 */
3235
	public function getFilterSubmitButton()
3236
	{
3237
		if ($this->hasAutoSubmit()) {
3238
			throw new DataGridException(
3239
				'DataGrid has auto-submit. Turn it off before setting filter submit button.'
3240
			);
3241
		}
3242
3243
		if ($this->filter_submit_button === NULL) {
3244
			$this->filter_submit_button = new Filter\SubmitButton($this);
3245
		}
3246
3247
		return $this->filter_submit_button;
3248
	}
3249
3250
3251
	/********************************************************************************
3252
	 *                                   INTERNAL                                   *
3253
	 ********************************************************************************/
3254
3255
3256
	/**
3257
	 * Get count of columns
3258
	 * @return int
3259
	 */
3260
	public function getColumnsCount()
3261
	{
3262
		$count = sizeof($this->getColumns());
3263
3264
		if (!empty($this->actions)
3265
			|| $this->isSortable()
3266
			|| $this->getItemsDetail()
3267
			|| $this->getInlineEdit()
3268
			|| $this->getInlineAdd()) {
3269
			$count++;
3270
		}
3271
3272
		if ($this->hasGroupActions()) {
3273
			$count++;
3274
		}
3275
3276
		return $count;
3277
	}
3278
3279
3280
	/**
3281
	 * Get primary key of datagrid data source
3282
	 * @return string
3283
	 */
3284
	public function getPrimaryKey()
3285
	{
3286 1
		return $this->primary_key;
3287
	}
3288
3289
3290
	/**
3291
	 * Get set of set columns
3292
	 * @return Column\IColumn[]
3293
	 */
3294
	public function getColumns()
3295
	{
3296 1
		$return = $this->columns;
3297
3298
		try {
3299 1
			$this->getParent();
3300
3301 1
			if (!$this->getSessionData('_grid_hidden_columns_manipulated', FALSE)) {
3302 1
				$columns_to_hide = [];
3303
3304 1
				foreach ($this->columns as $key => $column) {
3305
					if ($column->getDefaultHide()) {
3306
						$columns_to_hide[] = $key;
3307
					}
3308
				}
3309
3310 1
				if (!empty($columns_to_hide)) {
3311
					$this->saveSessionData('_grid_hidden_columns', $columns_to_hide);
3312
					$this->saveSessionData('_grid_hidden_columns_manipulated', TRUE);
3313
				}
3314
			}
3315
3316 1
			$hidden_columns = $this->getSessionData('_grid_hidden_columns', []);
3317
3318 1
			foreach ($hidden_columns as $column) {
3319
				if (!empty($this->columns[$column])) {
3320
					$this->columns_visibility[$column] = [
3321
						'visible' => FALSE
3322
					];
3323
3324 1
					unset($return[$column]);
3325
				}
3326
			}
3327
3328
		} catch (DataGridHasToBeAttachedToPresenterComponentException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
3329
		}
3330
3331 1
		return $return;
3332
	}
3333
3334
3335
	public function getColumnsVisibility()
3336
	{
3337 1
		$return = $this->columns_visibility;
3338
3339 1
		foreach ($this->columns_visibility as $key => $column) {
3340
			$return[$key]['column'] = $this->columns[$key];
3341
		}
3342
3343 1
		return $return;
3344
	}
3345
3346
3347
	/**
3348
	 * @return PresenterComponent
3349
	 */
3350
	public function getParent(): ?Nette\ComponentModel\IContainer
3351
	{
3352 1
		$parent = parent::getParent();
3353
3354 1
		if (!($parent instanceof PresenterComponent)) {
0 ignored issues
show
Bug introduced by
The class Nette\Application\UI\PresenterComponent does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
3355
			throw new DataGridHasToBeAttachedToPresenterComponentException(
3356
				"DataGrid is attached to: '" . get_class($parent) . "', but instance of PresenterComponent is needed."
3357
			);
3358
		}
3359
3360 1
		return $parent;
3361
	}
3362
3363
3364
	/**
3365
	 * @return string
3366
	 */
3367
	public function getSortableParentPath()
3368
	{
3369
		return $this->getParent()->lookupPath(Nette\Application\IPresenter::class, FALSE);
3370
	}
3371
3372
3373
	/**
3374
	 * Some of datagrid columns is hidden by default
3375
	 * @param bool $default_hide
3376
	 */
3377
	public function setSomeColumnDefaultHide($default_hide)
3378
	{
3379
		$this->some_column_default_hide = $default_hide;
3380
	}
3381
3382
3383
	/**
3384
	 * Are some of columns hidden bydefault?
3385
	 */
3386
	public function hasSomeColumnDefaultHide()
3387
	{
3388
		return $this->some_column_default_hide;
3389
	}
3390
3391
3392
	/**
3393
	 * Simply refresh url
3394
	 * @return void
3395
	 */
3396
	public function handleRefreshState()
3397
	{
3398
		$this->findSessionValues();
3399
		$this->findDefaultFilter();
3400
		$this->findDefaultSort();
3401
		$this->findDefaultPerPage();
3402
3403
		$this->getPresenter()->payload->_datagrid_url = $this->refresh_url;
3404
		$this->redrawControl('non-existing-snippet');
3405
	}
3406
3407
}
3408