Completed
Push — master ( 8b2aa0...562606 )
by Julito
09:06
created

SortableTable::__construct()   F

Complexity

Conditions 22
Paths > 20000

Size

Total Lines 106
Code Lines 65

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 22
eloc 65
nc 25600
nop 8
dl 0
loc 106
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use ChamiloSession as Session;
5
6
/**
7
 * This class allows you to display a sortable data-table. It is possible to
8
 * split the data in several pages.
9
 * Using this class you can:
10
 * - automatically create checkboxes of the first table column
11
 *     - a "select all" and "deselect all" link is added
12
 *     - only if you provide a list of actions for the selected items
13
 * - click on the table header to sort the data
14
 * - choose how many items you see per page
15
 * - navigate through all data-pages.
16
 *
17
 * @package chamilo.library
18
 */
19
class SortableTable extends HTML_Table
20
{
21
    /**
22
     * A name for this table.
23
     */
24
    public $table_name;
25
    /**
26
     * The page to display.
27
     */
28
    public $page_nr;
29
    /**
30
     * The column to sort the data.
31
     */
32
    public $column;
33
    /**
34
     * The sorting direction (ASC or DESC).
35
     */
36
    public $direction;
37
    /**
38
     * Number of items to display per page.
39
     */
40
    public $per_page;
41
    /**
42
     * The default number of items to display per page.
43
     */
44
    public $default_items_per_page;
45
    /**
46
     * A prefix for the URL-parameters, can be used on pages with multiple
47
     * SortableTables.
48
     */
49
    public $param_prefix;
50
    /**
51
     * The pager object to split the data in several pages.
52
     */
53
    public $pager;
54
    /**
55
     * The total number of items in the table.
56
     */
57
    public $total_number_of_items;
58
    /**
59
     * The function to get the total number of items.
60
     */
61
    public $get_total_number_function;
62
    /**
63
     * The function to the the data to display.
64
     */
65
    public $get_data_function;
66
    /**
67
     * An array with defined column-filters.
68
     */
69
    public $column_filters;
70
    /**
71
     * A list of actions which will be available through a select list.
72
     */
73
    public $form_actions;
74
    /**
75
     * Additional parameters to pass in the URL.
76
     */
77
    public $additional_parameters;
78
    /**
79
     * Additional attributes for the th-tags.
80
     */
81
    public $th_attributes;
82
    /**
83
     * Additional attributes for the td-tags.
84
     */
85
    public $td_attributes;
86
    /**
87
     * Array with names of the other tables defined on the same page of this
88
     * table.
89
     */
90
    public $other_tables;
91
    /**
92
     * Activates the odd even rows.
93
     * */
94
    public $odd_even_rows_enabled = true;
95
    public $use_jqgrid = false;
96
    public $table_id = null;
97
    public $headers = [];
98
99
    /**
100
     * The array containing all data for this table.
101
     */
102
    public $table_data;
103
    public $hideItemSelector;
104
105
    /**
106
     * @var array Columns to hide
107
     */
108
    private $columnsToHide = [];
109
    private $dataFunctionParams;
110
    private $defaultColumn;
111
    private $defaultItemsPerPage;
112
113
    /**
114
     * Create a new SortableTable.
115
     *
116
     * @param string $table_name                A name for the table (default = 'table')
117
     * @param string $get_total_number_function A user defined function to get
118
     *                                          the total number of items in the table
119
     * @param string $get_data_function         A function to get the data to display on
120
     *                                          the current page
121
     * @param int    $default_column            The default column on which the data should be
122
     *                                          sorted
123
     * @param int    $default_items_per_page    The default number of items to show
124
     *                                          on one page
125
     * @param string $default_order_direction   The default order direction;
126
     *                                          either the constant 'ASC' or 'DESC'
127
     * @param string $table_id
128
     * @param array  $parameters                They are custom attributes of the table
129
     */
130
    public function __construct(
131
        $table_name = 'table',
132
        $get_total_number_function = null,
133
        $get_data_function = null,
134
        $default_column = 1,
135
        $default_items_per_page = 20,
136
        $default_order_direction = 'ASC',
137
        $table_id = null,
138
        $parameters = []
139
    ) {
140
        if (empty($table_id)) {
141
            $table_id = $table_name.uniqid('table', true);
142
        }
143
        if (isset($parameters) && empty($parameters)) {
144
            $parameters = ['class' => 'table table-bordered data_table', 'id' => $table_id];
145
        }
146
147
        $this->table_id = $table_id;
148
        parent::__construct($parameters);
149
        $this->table_name = $table_name;
150
        $this->additional_parameters = [];
151
        $this->param_prefix = $table_name.'_';
152
        $this->defaultColumn = (int) $default_column;
153
        $this->defaultItemsPerPage = $default_items_per_page;
154
        $this->hideItemSelector = false;
155
156
        $defaultRow = api_get_configuration_value('table_default_row');
157
        if (!empty($defaultRow)) {
158
            $this->defaultItemsPerPage = $default_items_per_page = $defaultRow;
159
        }
160
161
        $cleanSessionData = Session::read('clean_sortable_table');
162
        if ($cleanSessionData === true) {
163
            $this->cleanUrlSessionParams();
164
        }
165
166
        // Allow to change paginate in multiples tabs
167
        //Session::erase($this->param_prefix.'per_page');
168
        $this->per_page = Session::read($this->param_prefix.'per_page', $default_items_per_page);
0 ignored issues
show
Bug introduced by
It seems like $default_items_per_page can also be of type boolean and integer; however, parameter $default of ChamiloSession::read() does only seem to accept null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

168
        $this->per_page = Session::read($this->param_prefix.'per_page', /** @scrutinizer ignore-type */ $default_items_per_page);
Loading history...
169
170
        // If per page changed, then reset the page to 1
171
        if (!empty($this->per_page) && isset($_GET[$this->param_prefix.'per_page']) && $this->per_page != $_GET[$this->param_prefix.'per_page']) {
172
            Session::erase($this->param_prefix.'page_nr');
173
            $_GET[$this->param_prefix.'page_nr'] = 1;
174
        }
175
176
        $this->per_page = isset($_GET[$this->param_prefix.'per_page']) ? (int) $_GET[$this->param_prefix.'per_page'] : $this->per_page;
177
178
        if (isset($_GET[$this->param_prefix.'per_page'])) {
179
            Session::erase($this->param_prefix.'page_nr');
180
        }
181
182
        $this->page_nr = Session::read($this->param_prefix.'page_nr', 1);
183
        $this->page_nr = isset($_GET[$this->param_prefix.'page_nr']) ? (int) $_GET[$this->param_prefix.'page_nr'] : $this->page_nr;
184
185
        $this->column = Session::read($this->param_prefix.'column', $default_column);
186
        $this->column = isset($_GET[$this->param_prefix.'column']) ? (int) $_GET[$this->param_prefix.'column'] : $this->column;
187
188
        // Default direction.
189
        if (in_array(strtoupper($default_order_direction), ['ASC', 'DESC'])) {
190
            $this->direction = $default_order_direction;
191
        }
192
193
        $my_session_direction = Session::read($this->param_prefix.'direction');
194
        if (!empty($my_session_direction)) {
195
            if (!in_array($my_session_direction, ['ASC', 'DESC'])) {
196
                $this->direction = 'ASC';
197
            } else {
198
                if ($my_session_direction === 'ASC') {
199
                    $this->direction = 'ASC';
200
                } elseif ($my_session_direction === 'DESC') {
201
                    $this->direction = 'DESC';
202
                }
203
            }
204
        }
205
206
        if (isset($_GET[$this->param_prefix.'direction'])) {
207
            $my_get_direction = $_GET[$this->param_prefix.'direction'];
208
            if (!in_array($my_get_direction, ['ASC', 'DESC'])) {
209
                $this->direction = 'ASC';
210
            } else {
211
                if ($my_get_direction === 'ASC') {
212
                    $this->direction = 'ASC';
213
                } elseif ($my_get_direction === 'DESC') {
214
                    $this->direction = 'DESC';
215
                }
216
            }
217
        }
218
219
        Session::write($this->param_prefix.'per_page', $this->per_page);
220
        Session::write($this->param_prefix.'direction', $this->direction);
221
        Session::write($this->param_prefix.'page_nr', $this->page_nr);
222
        Session::write($this->param_prefix.'column', $this->column);
223
224
        $this->pager = null;
225
        $this->default_items_per_page = $default_items_per_page;
226
        $this->total_number_of_items = -1;
227
        $this->get_total_number_function = $get_total_number_function;
228
        $this->get_data_function = $get_data_function;
229
        $this->column_filters = [];
230
        $this->form_actions = [];
231
        $this->checkbox_name = null;
0 ignored issues
show
Bug Best Practice introduced by
The property checkbox_name does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
232
        $this->td_attributes = [];
233
        $this->th_attributes = [];
234
        $this->other_tables = [];
235
        $this->dataFunctionParams = [];
236
    }
237
238
    /**
239
     * Clean URL params when changing student view.
240
     */
241
    public function cleanUrlSessionParams()
242
    {
243
        Session::erase('clean_sortable_table');
244
245
        $prefix = $this->param_prefix;
246
247
        Session::erase($prefix.'page_nr');
248
        Session::erase($prefix.'column');
249
        Session::erase($prefix.'direction');
250
        Session::erase($prefix.'per_page');
251
252
        $_GET[$this->param_prefix.'per_page'] = $this->default_items_per_page;
253
        $_GET[$this->param_prefix.'page_nr'] = 1;
254
        $_GET[$this->param_prefix.'column'] = $this->defaultColumn;
255
        $_GET[$this->param_prefix.'direction'] = $this->direction;
256
    }
257
258
    /**
259
     * @return array
260
     */
261
    public function getDataFunctionParams()
262
    {
263
        return $this->dataFunctionParams;
264
    }
265
266
    /**
267
     * @param array $dataFunctionParams
268
     *
269
     * @return $this
270
     */
271
    public function setDataFunctionParams($dataFunctionParams)
272
    {
273
        $this->dataFunctionParams = $dataFunctionParams;
274
275
        return $this;
276
    }
277
278
    /**
279
     * Get the Pager object to split the showed data in several pages.
280
     *
281
     * @return Pager_Sliding
282
     */
283
    public function get_pager()
284
    {
285
        if ($this->pager === null) {
286
            $params['mode'] = 'Sliding';
0 ignored issues
show
Comprehensibility Best Practice introduced by
$params was never initialized. Although not strictly required by PHP, it is generally a good practice to add $params = array(); before regardless.
Loading history...
287
            $params['perPage'] = $this->per_page;
288
            $params['totalItems'] = $this->get_total_number_of_items();
289
            $params['urlVar'] = $this->param_prefix.'page_nr';
290
            $params['currentPage'] = $this->page_nr;
291
            $icon_attributes = ['style' => 'vertical-align: middle;'];
292
            $params['prevImg'] = Display:: return_icon(
293
                'action_prev.png',
294
                get_lang('PreviousPage'),
295
                $icon_attributes
296
            );
297
            $params['nextImg'] = Display:: return_icon(
298
                'action_next.png',
299
                get_lang('NextPage'),
300
                $icon_attributes
301
            );
302
            $params['firstPageText'] = Display:: return_icon(
303
                'action_first.png',
304
                get_lang('FirstPage'),
305
                $icon_attributes
306
            );
307
            $params['lastPageText'] = Display:: return_icon(
308
                'action_last.png',
309
                get_lang('LastPage'),
310
                $icon_attributes
311
            );
312
            $params['firstPagePre'] = '';
313
            $params['lastPagePre'] = '';
314
            $params['firstPagePost'] = '';
315
            $params['lastPagePost'] = '';
316
            $params['spacesBeforeSeparator'] = '';
317
            $params['spacesAfterSeparator'] = '';
318
            $query_vars = array_keys($_GET);
319
            $query_vars_needed = [
320
                $this->param_prefix.'column',
321
                $this->param_prefix.'direction',
322
                $this->param_prefix.'per_page',
323
            ];
324
            if (!empty($this->additional_parameters) && count($this->additional_parameters) > 0) {
325
                $query_vars_needed = array_merge(
326
                    $query_vars_needed,
327
                    array_keys($this->additional_parameters)
328
                );
329
            }
330
            $query_vars_exclude = array_diff($query_vars, $query_vars_needed);
331
            $params['excludeVars'] = $query_vars_exclude;
332
            $this->pager = &Pager::factory($params);
333
        }
334
335
        return $this->pager;
336
    }
337
338
    /**
339
     * Display the table.
340
     */
341
    public function display()
342
    {
343
        echo $this->return_table();
344
    }
345
346
    /**
347
     * Displays the table, complete with navigation buttons to browse through
348
     * the data-pages.
349
     */
350
    public function return_table()
351
    {
352
        $empty_table = false;
353
        $content = $this->get_table_html();
354
        if ($this->get_total_number_of_items() == 0) {
355
            $cols = $this->getColCount();
356
            $this->setCellAttributes(
357
                1,
358
                0,
359
                'style="font-style: italic;text-align:center;" colspan='.$cols
360
            );
361
            $message_empty = api_xml_http_response_encode(get_lang('TheListIsEmpty'));
362
            $this->setCellContents(1, 0, $message_empty);
363
            $empty_table = true;
364
        }
365
        $html = '';
366
        if (!$empty_table) {
367
            $table_id = 'form_'.$this->table_name.'_id';
368
            $form = $this->get_page_select_form();
369
            $nav = $this->get_navigation_html();
370
371
            // Only show pagination info when there are items to paginate
372
            if ($this->get_total_number_of_items() > $this->default_items_per_page) {
373
                $html = '<div class="card-action">';
374
                $html .= '<div class="row">';
375
                $html .= '<div class="col-12 col-md-4">';
376
                $html .= '<div class="page-select pb-2 pt-2">'.$form.'</div>';
377
                $html .= '</div>';
378
                $html .= '<div class="col-12 col-md-4">';
379
                $html .= '<div class="page-number pb-2 pt-2">'.$this->get_table_title().'</div>';
380
                $html .= '</div>';
381
                $html .= '<div class="col-12 col-md-4">';
382
                $html .= '<div class="page-nav pb-2 pt-2">'.$nav.'</div>';
383
                $html .= '</div>';
384
                $html .= '</div>';
385
                $html .= '</div>';
386
            }
387
388
            if (count($this->form_actions) > 0) {
389
                $params = $this->get_sortable_table_param_string().'&amp;'.$this->get_additional_url_paramstring();
390
                $html .= '<form id ="'.$table_id.'" class="form-search" method="post" action="'.api_get_self().'?'.$params.'" name="form_'.$this->table_name.'">';
391
            }
392
        }
393
394
        $html .= '<div class="table-responsive">'.$content.'</div>';
395
396
        if (!$empty_table) {
397
            if (!empty($this->additional_parameters)) {
398
                foreach ($this->additional_parameters as $key => $value) {
399
                    $html .= '<input type="hidden" name ="'.Security::remove_XSS($key).'" value ="'.Security::remove_XSS($value).'" />';
400
                }
401
            }
402
            $html .= '<input type="hidden" name="action">';
403
            $html .= '<div class="card-action">';
404
            $html .= '<div class="row">';
405
            $html .= '<div class="col-12 col-md-6">';
406
            $html .= '<div class="page-action pb-2 pt-2">';
407
408
            if (count($this->form_actions) > 0) {
409
                $html .= '<div class="btn-group" role="group">';
410
                $html .= '<a 
411
                    class="btn btn-outline-primary" 
412
                    href="?'.$params.'&amp;'.$this->param_prefix.'selectall=1" 
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $params does not seem to be defined for all execution paths leading up to this point.
Loading history...
413
                    onclick="javascript: setCheckbox(true, \''.$table_id.'\'); return false;">'.get_lang('SelectAll').'</a>';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $table_id does not seem to be defined for all execution paths leading up to this point.
Loading history...
414
                $html .= '<a 
415
                    class="btn btn-outline-primary" 
416
                    href="?'.$params.'" 
417
                    onclick="javascript: setCheckbox(false, \''.$table_id.'\'); return false;">'.get_lang('UnSelectAll').'</a> ';
418
                $html .= '<div class="btn-group" role="group">
419
                            <button 
420
                                id="'.$table_id.'_actions" 
421
                                data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
422
                                class="btn btn-outline-primary dropdown-toggle" 
423
                                onclick="javascript:return false;">'.
424
                                get_lang('Actions').'
425
                            </button>
426
                          ';
427
                $html .= '<div class="dropdown-menu" aria-labelledby="'.$table_id.'_actions" >';
428
                foreach ($this->form_actions as $action => &$label) {
429
                    $html .= '<a 
430
                        class="dropdown-item" 
431
                        data-action ="'.$action.'" 
432
                        href="#" 
433
                        onclick="javascript:action_click(this, \''.$table_id.'\');">'.$label.'</a>';
434
                }
435
                $html .= '</div>';
436
                $html .= '</div>'; //btn-group
437
                $html .= '</div>';
438
            } else {
439
                $html .= $form;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $form does not seem to be defined for all execution paths leading up to this point.
Loading history...
440
            }
441
442
            $html .= '</div>';
443
            $html .= '</div>';
444
            // Pagination
445
            if ($this->get_total_number_of_items() > $this->default_items_per_page) {
446
                $html .= '<div class="col-12 col-md-6">';
447
                $html .= '<div class="page-nav pb-2 pt-2">'.$nav.'</div>';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $nav does not seem to be defined for all execution paths leading up to this point.
Loading history...
448
                $html .= '</div>';
449
            }
450
451
            $html .= '</div>';
452
            $html .= '</div>';
453
            if (count($this->form_actions) > 0) {
454
                $html .= '</form>';
455
            }
456
        }
457
458
        return $html;
459
    }
460
461
    /**
462
     * This function shows the content of a table in a grid.
463
     * Should not be use to edit information (edit/delete rows) only.
464
     */
465
    public function display_grid()
466
    {
467
        $empty_table = false;
468
        if ($this->get_total_number_of_items() == 0) {
469
            $message_empty = api_xml_http_response_encode(get_lang('TheListIsEmpty'));
470
            $this->setCellContents(1, 0, $message_empty);
471
            $empty_table = true;
472
        }
473
        $html = '';
474
        if (!$empty_table) {
475
            $form = $this->get_page_select_form();
476
            $nav = $this->get_navigation_html();
477
478
            // @todo This style css must be moved to default.css only for dev
479
            echo '<style>
480
                    .main-grid { width:100%;}
481
                    .sub-header { width:100%; padding-top: 10px; padding-right: 10px; padding-left: 10px; height:30px;}
482
                    .grid_container { width:100%;}
483
                    .grid_item { height: 120px; width:98px;  float:left; padding:5px; margin:8px;}
484
                    .grid_element_0 { width:100px; height: 100px; float:left; text-align:center; margin-bottom:5px;}
485
                    .grid_element_1 { width:100px; float:left; text-align:center;margin-bottom:5px;}
486
                    .grid_element_2 { width:150px; float:left;}
487
488
                    .grid_selectbox { width:30%; float:left;}
489
                    .grid_title     { width:30%; float:left;}
490
                    .grid_nav         { }
491
            </style>';
492
493
            // @todo  This also must be moved
494
            // Show only navigations if there are more than 1 page
495
            $my_pager = $this->get_pager();
496
            $html .= '<div class="main-grid">';
497
            if ($my_pager->numPages() > 1) {
498
                $html .= '<div class="sub-header">';
499
                $html .= '<div class="grid_selectbox">'.$form.'</div>';
500
                $html .= '<div class="grid_title">'.$this->get_table_title().'</div>';
501
                $html .= '<div class="grid_nav">'.$nav.'</div>';
502
                $html .= '</div>';
503
            }
504
505
            $html .= '<div class="clear"></div>';
506
            if (count($this->form_actions) > 0) {
507
                $params = $this->get_sortable_table_param_string().'&amp;'.$this->get_additional_url_paramstring();
508
                $html .= '<form method="post" action="'.api_get_self().'?'.$params.'" name="form_'.$this->table_name.'">';
509
            }
510
        }
511
        // Getting the items of the table
512
        $items = $this->get_clean_html(false); //no sort
513
514
        // Generation of style classes must be improved. Maybe we need a a table name to create style on the fly:
515
        // i.e: .whoisonline_table_grid_container instead of  .grid_container
516
        // where whoisonline is the table's name like drupal's template engine
517
        $html .= '<div class="grid_container">';
518
        if (is_array($items) && count($items) > 0) {
519
            foreach ($items as &$row) {
520
                $html .= '<div class="grid_item">';
521
                $i = 0;
522
                foreach ($row as &$element) {
523
                    $html .= '<div class="grid_element_'.$i.'">'.$element.'</div>';
524
                    $i++;
525
                }
526
                $html .= '</div>';
527
            }
528
        }
529
        $html .= '</div>'; //close grid_container
530
        $html .= '</div>'; //close main grid
531
        $html .= '<div class="clear"></div>';
532
533
        echo $html;
534
    }
535
536
    /**
537
     * This function returns the content of a table in a grid
538
     * Should not be use to edit information (edit/delete rows) only.
539
     *
540
     * @param array      options of visibility
541
     * @param bool       hide navigation optionally
542
     * @param int        content per page when show navigation (optional)
543
     * @param bool       sort data optionally
544
     *
545
     * @return string grid html
546
     */
547
    public function display_simple_grid(
548
        $visibility_options,
549
        $hide_navigation = true,
550
        $per_page = 20,
551
        $sort_data = true,
552
        $grid_class = []
553
    ) {
554
        $empty_table = false;
555
        if ($this->get_total_number_of_items() == 0) {
556
            $message_empty = api_xml_http_response_encode(get_lang('TheListIsEmpty'));
557
            $this->setCellContents(1, 0, $message_empty);
558
            $empty_table = true;
559
        }
560
        $html = '';
561
        if (!$empty_table) {
562
            // If we show the pagination
563
            if (!$hide_navigation) {
564
                $form = '&nbsp;';
565
566
                if ($this->get_total_number_of_items() > $per_page) {
567
                    if ($per_page > 10) {
568
                        $form = $this->get_page_select_form();
569
                    }
570
                    $nav = $this->get_navigation_html();
571
                    // This also must be moved
572
                    $html = '<div class="sub-header">';
573
                    $html .= '<div class="grid_selectbox">'.$form.'</div>';
574
                    $html .= '<div class="grid_title">'.$this->get_table_title().'</div>';
575
                    $html .= '<div class="grid_nav">'.$nav.'</div>';
576
                    $html .= '</div>';
577
                }
578
            }
579
580
            $html .= '<div class="clear"></div>';
581
            if (count($this->form_actions) > 0) {
582
                $params = $this->get_sortable_table_param_string().'&amp;'.$this->get_additional_url_paramstring();
583
                $html .= '<form method="post" action="'.api_get_self().'?'.$params.'" name="form_'.$this->table_name.'">';
584
            }
585
        }
586
587
        if ($hide_navigation) {
588
            $items = $this->table_data; // This is a faster way to get what we want
589
        } else {
590
            // The normal way
591
            $items = $this->get_clean_html($sort_data); // Getting the items of the table
592
        }
593
594
        // Generation of style classes must be improved. Maybe we need a a table name to create style on the fly:
595
        // i.e: .whoisonline_table_grid_container instead of  .grid_container
596
        // where whoisonline is the table's name like drupal's template engine
597
598
        if (is_array($visibility_options)) {
599
            $filter = false; // The 2nd condition of the if will be loaded
600
        } else {
601
            $filter = $visibility_options !== false;
602
        }
603
604
        $item_css_class = $item_css_style = $grid_css_class = $grid_css_style = '';
605
        if (!empty($grid_class)) {
606
            $grid_css_class = $grid_class['main']['class'];
607
            $item_css_class = $grid_class['item']['class'];
608
609
            $grid_css_style = isset($grid_class['main']['style']) ? $grid_class['main']['style'] : null;
610
            $item_css_style = isset($grid_class['item']['style']) ? $grid_class['item']['style'] : null;
611
        }
612
613
        $div = '';
614
        if (is_array($items) && count($items) > 0) {
615
            foreach ($items as &$row) {
616
                $i = 0;
617
                $rows = '';
618
                foreach ($row as &$element) {
619
                    if ($filter ||
620
                        isset($visibility_options[$i]) && $visibility_options[$i]
621
                    ) {
622
                        $rows .= '<div class="'.$this->table_name.'_grid_element_'.$i.'">'.$element.'</div>';
623
                    }
624
                    $i++;
625
                }
626
                $div .= Display::div(
627
                    $rows,
628
                    [
629
                        'class' => $item_css_class.' '.$this->table_name.'_grid_item',
630
                        'style' => $item_css_style,
631
                    ]
632
                );
633
            }
634
        }
635
636
        $html .= Display::div(
637
            $div,
638
            [
639
                'class' => $grid_css_class.' '.$this->table_name.'_grid_container',
640
                'style' => $grid_css_style,
641
            ]
642
        );
643
        $html .= '<div class="clear"></div>';
644
645
        return $html;
646
    }
647
648
    /**
649
     * Get the HTML-code with the navigational buttons to browse through the
650
     * data-pages.
651
     */
652
    public function get_navigation_html()
653
    {
654
        $pager = $this->get_pager();
655
        $pager_links = $pager->getLinks();
656
        $nav = $pager_links['first'].' '.$pager_links['back'];
657
        $nav .= '<div class="btn btn-outline-secondary">'.$pager->getCurrentPageId().' / '.$pager->numPages().' </div>';
658
        $nav .= $pager_links['next'].' '.$pager_links['last'];
659
        $html = Display::tag('div', $nav, ['class' => 'btn-group btn-group-sm', 'role' => 'group']);
660
661
        return $html;
662
    }
663
664
    /**
665
     * Get the HTML-code with the data-table.
666
     */
667
    public function get_table_html()
668
    {
669
        $pager = $this->get_pager();
670
        $offset = $pager->getOffsetByPageId();
671
        $from = $offset[0] - 1;
672
        $table_data = $this->get_table_data($from);
673
        $this->processHeaders();
674
675
        if (is_array($table_data)) {
676
            $count = 1;
677
            foreach ($table_data as &$row) {
678
                $row = $this->filter_data($row);
679
680
                $newRow = [];
681
                if (!empty($this->columnsToHide)) {
682
                    $counter = 0;
683
                    foreach ($row as $index => $rowInfo) {
684
                        if (!isset($this->columnsToHide[$index])) {
685
                            $newRow[$counter] = $rowInfo;
686
                            $counter++;
687
                        }
688
                    }
689
                    $row = $newRow;
690
                }
691
                $this->addRow($row);
692
                if (isset($row['child_of'])) {
693
                    $this->setRowAttributes(
694
                        $count,
695
                        ['class' => 'hidden hidden_'.$row['child_of']],
696
                        true
697
                    );
698
                }
699
                $count++;
700
            }
701
        }
702
703
        if ($this->odd_even_rows_enabled == true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
704
            $this->altRowAttributes(
705
                0,
706
                ['class' => 'row_odd'],
707
                ['class' => 'row_even'],
708
                true
709
            );
710
        }
711
712
        foreach ($this->th_attributes as $column => $attributes) {
713
            $this->setCellAttributes(0, $column, $attributes);
714
        }
715
        foreach ($this->td_attributes as $column => $attributes) {
716
            $this->setColAttributes($column, $attributes);
717
        }
718
719
        return $this->toHTML();
720
    }
721
722
    /**
723
     * This function return the items of the table.
724
     *
725
     * @param bool true for sorting table data or false otherwise
726
     *
727
     * @return array table row items
728
     */
729
    public function get_clean_html($sort = true)
730
    {
731
        $pager = $this->get_pager();
732
        $offset = $pager->getOffsetByPageId();
733
        $from = $offset[0] - 1;
734
        $table_data = $this->get_table_data($from, null, null, null, $sort);
735
        $new_table_data = [];
736
        if (is_array($table_data)) {
737
            foreach ($table_data as $index => &$row) {
738
                $row = $this->filter_data($row);
739
                $new_table_data[] = $row;
740
            }
741
        }
742
743
        return $new_table_data;
744
    }
745
746
    /**
747
     * Get the HTML-code which represents a form to select how many items a page
748
     * should contain.
749
     */
750
    public function get_page_select_form()
751
    {
752
        $total_number_of_items = $this->get_total_number_of_items();
753
        if ($total_number_of_items <= $this->default_items_per_page) {
754
            return '';
755
        }
756
757
        if ($this->hideItemSelector === true) {
758
            return '';
759
        }
760
        $result[] = '<form method="GET" action="'.api_get_self().'" style="display:inline;">';
0 ignored issues
show
Comprehensibility Best Practice introduced by
$result was never initialized. Although not strictly required by PHP, it is generally a good practice to add $result = array(); before regardless.
Loading history...
761
        $param[$this->param_prefix.'direction'] = $this->direction;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$param was never initialized. Although not strictly required by PHP, it is generally a good practice to add $param = array(); before regardless.
Loading history...
762
        $param[$this->param_prefix.'page_nr'] = $this->page_nr;
763
        $param[$this->param_prefix.'column'] = $this->column;
764
765
        if (is_array($this->additional_parameters)) {
766
            $param = array_merge($param, $this->additional_parameters);
767
        }
768
769
        foreach ($param as $key => &$value) {
770
            $result[] = '<input type="hidden" name="'.$key.'" value="'.$value.'"/>';
771
        }
772
        $result[] = '<select style="width: auto;" class="form-control" name="'.$this->param_prefix.'per_page" onchange="javascript: this.form.submit();">';
773
        $list = [10, 20, 50, 100, 500, 1000];
774
775
        $rowList = api_get_configuration_value('table_row_list');
776
        if (!empty($rowList) && isset($rowList['options'])) {
777
            $list = $rowList['options'];
778
        }
779
780
        foreach ($list as $nr) {
781
            if ($total_number_of_items <= $nr) {
782
                break;
783
            }
784
            $result[] = '<option value="'.$nr.'" '.($nr == $this->per_page ? 'selected="selected"' : '').'>'.$nr.'</option>';
785
        }
786
787
        $result[] = '<option value="'.$total_number_of_items.'" '.($total_number_of_items == $this->per_page ? 'selected="selected"' : '').'>'.api_ucfirst(get_lang('All')).'</option>';
788
        //}
789
        $result[] = '</select>';
790
        $result[] = '<noscript>';
791
        $result[] = '<button class="btn btn-success" type="submit">'.get_lang('Save').'</button>';
792
        $result[] = '</noscript>';
793
        $result[] = '</form>';
794
        $result = implode("\n", $result);
795
796
        return $result;
797
    }
798
799
    /**
800
     * Get the table title.
801
     */
802
    public function get_table_title()
803
    {
804
        $pager = $this->get_pager();
805
        $showed_items = $pager->getOffsetByPageId();
806
807
        return $showed_items[0].' - '.$showed_items[1].' / '.$this->get_total_number_of_items();
808
    }
809
810
    /**
811
     * @return array
812
     */
813
    public function getHeaders()
814
    {
815
        return $this->headers;
816
    }
817
818
    /**
819
     * Process headers.
820
     */
821
    public function processHeaders()
822
    {
823
        $counter = 0;
824
        foreach ($this->headers as $column => $columnInfo) {
825
            $label = $columnInfo['label'];
826
            $sortable = $columnInfo['sortable'];
827
            $th_attributes = $columnInfo['th_attributes'];
828
            $td_attributes = $columnInfo['td_attributes'];
829
830
            if (!empty($this->columnsToHide)) {
831
                if (isset($this->columnsToHide[$column])) {
832
                    continue;
833
                }
834
            }
835
836
            $column = $counter;
837
            $param['direction'] = 'ASC';
838
            if ($this->column == $column && $this->direction == 'ASC') {
839
                $param['direction'] = 'DESC';
840
            }
841
842
            $param['page_nr'] = $this->page_nr;
843
            $param['per_page'] = $this->per_page;
844
            $param['column'] = $column;
845
            $link = $label;
846
            if ($sortable) {
847
                $link = '<a href="'.api_get_self().'?'.api_get_cidreq().'&amp;';
848
                foreach ($param as $key => &$value) {
849
                    $link .= $this->param_prefix.$key.'='.urlencode($value).'&amp;';
850
                }
851
                $link .= $this->get_additional_url_paramstring();
852
                $link .= '">'.$label.'</a>';
853
                if ($this->column == $column) {
854
                    $link .= $this->direction == 'ASC' ? ' &#8595;' : ' &#8593;';
855
                }
856
            }
857
            $this->setHeaderContents(0, $column, $link);
858
859
            if (!is_null($td_attributes)) {
860
                $this->td_attributes[$column] = $td_attributes;
861
            }
862
            if (!is_null($th_attributes)) {
863
                $this->th_attributes[$column] = $th_attributes;
864
            }
865
866
            $counter++;
867
        }
868
    }
869
870
    /**
871
     * Set the header-label.
872
     *
873
     * @param int    $column        The column number
874
     * @param string $label         The label
875
     * @param bool   $sortable      Is the table sortable by this column? (defatult
876
     *                              = true)
877
     * @param string $th_attributes Additional attributes for the th-tag of the
878
     *                              table header
879
     * @param string $td_attributes Additional attributes for the td-tags of the
880
     *                              column
881
     */
882
    public function set_header(
883
        $column,
884
        $label,
885
        $sortable = true,
886
        $th_attributes = ['class' => 'th-header'],
887
        $td_attributes = null
888
    ) {
889
        $this->headers[$column] = [
890
            'label' => $label,
891
            'sortable' => $sortable,
892
            'th_attributes' => $th_attributes,
893
            'td_attributes' => $td_attributes,
894
        ];
895
    }
896
897
    /**
898
     * Get the parameter-string with additional parameters to use in the URLs
899
     * generated by this SortableTable.
900
     */
901
    public function get_additional_url_paramstring()
902
    {
903
        $param_string_parts = [];
904
        if (is_array($this->additional_parameters) && count($this->additional_parameters) > 0) {
905
            foreach ($this->additional_parameters as $key => &$value) {
906
                $param_string_parts[] = urlencode($key).'='.urlencode($value);
907
            }
908
        }
909
        $result = implode('&amp;', $param_string_parts);
910
        foreach ($this->other_tables as $index => &$tablename) {
911
            $param = [];
912
            if (isset($_GET[$tablename.'_direction'])) {
913
                //$param[$tablename.'_direction'] = $_GET[$tablename.'_direction'];
914
                $my_get_direction = $_GET[$tablename.'_direction'];
915
                if (!in_array($my_get_direction, ['ASC', 'DESC'])) {
916
                    $param[$tablename.'_direction'] = 'ASC';
917
                } else {
918
                    $param[$tablename.'_direction'] = $my_get_direction;
919
                }
920
            }
921
            if (isset($_GET[$tablename.'_page_nr'])) {
922
                $param[$tablename.'_page_nr'] = intval($_GET[$tablename.'_page_nr']);
923
            }
924
            if (isset($_GET[$tablename.'_per_page'])) {
925
                $param[$tablename.'_per_page'] = intval($_GET[$tablename.'_per_page']);
926
            }
927
            if (isset($_GET[$tablename.'_column'])) {
928
                $param[$tablename.'_column'] = intval($_GET[$tablename.'_column']);
929
            }
930
            $param_string_parts = [];
931
            foreach ($param as $key => &$value) {
932
                $param_string_parts[] = urlencode($key).'='.urlencode($value);
933
            }
934
            if (count($param_string_parts) > 0) {
935
                $result .= '&amp;'.implode('&amp;', $param_string_parts);
936
            }
937
        }
938
939
        return $result;
940
    }
941
942
    /**
943
     * Get the parameter-string with the SortableTable-related parameters to use
944
     * in URLs.
945
     */
946
    public function get_sortable_table_param_string()
947
    {
948
        $param[$this->param_prefix.'direction'] = $this->direction;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$param was never initialized. Although not strictly required by PHP, it is generally a good practice to add $param = array(); before regardless.
Loading history...
949
        $param[$this->param_prefix.'page_nr'] = $this->page_nr;
950
        $param[$this->param_prefix.'per_page'] = $this->per_page;
951
        $param[$this->param_prefix.'column'] = $this->column;
952
        $param_string_parts = [];
953
        foreach ($param as $key => &$value) {
954
            $param_string_parts[] = urlencode($key).'='.urlencode($value);
955
        }
956
        $res = implode('&amp;', $param_string_parts);
957
958
        return $res;
959
    }
960
961
    /**
962
     * Add a filter to a column. If another filter was already defined for the
963
     * given column, it will be overwritten.
964
     *
965
     * @param int    $column   The number of the column
966
     * @param string $function The name of the filter-function. This should be a
967
     *                         function wich requires 1 parameter and returns the filtered value.
968
     */
969
    public function set_column_filter($column, $function)
970
    {
971
        $this->column_filters[$column] = $function;
972
    }
973
974
    /**
975
     * List of columns to hide.
976
     *
977
     * @param int $column
978
     */
979
    public function setHideColumn($column)
980
    {
981
        $this->columnsToHide[$column] = $column;
982
    }
983
984
    /**
985
     * Define a list of actions which can be performed on the table-date.
986
     * If you define a list of actions, the first column of the table will be
987
     * converted into checkboxes.
988
     *
989
     * @param array  $actions       A list of actions. The key is the name of the
990
     *                              action. The value is the label to show in the select-box
991
     * @param string $checkbox_name The name of the generated checkboxes. The
992
     *                              value of the checkbox will be the value of the first column.
993
     */
994
    public function set_form_actions($actions, $checkbox_name = 'id')
995
    {
996
        $this->form_actions = $actions;
997
        $this->checkbox_name = $checkbox_name;
0 ignored issues
show
Bug Best Practice introduced by
The property checkbox_name does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
998
    }
999
1000
    /**
1001
     * Define a list of additional parameters to use in the generated URLs
1002
     * <code>$parameters['action'] = 'test'; will be convert in
1003
     * <input type="hidden" name="action" value="test"></code>.
1004
     *
1005
     * @param array $parameters
1006
     */
1007
    public function set_additional_parameters($parameters)
1008
    {
1009
        $this->additional_parameters = $parameters;
1010
    }
1011
1012
    /**
1013
     * Set other tables on the same page.
1014
     * If you have other sortable tables on the page displaying this sortable
1015
     * tables, you can define those other tables with this function. If you
1016
     * don't define the other tables, there sorting and pagination will return
1017
     * to their default state when sorting this table.
1018
     *
1019
     * @param array $tablenames an array of table names
1020
     */
1021
    public function set_other_tables($tablenames)
1022
    {
1023
        $this->other_tables = $tablenames;
1024
    }
1025
1026
    /**
1027
     * Transform all data in a table-row, using the filters defined by the
1028
     * function set_column_filter(...) defined elsewhere in this class.
1029
     * If you've defined actions, the first element of the given row will be
1030
     * converted into a checkbox.
1031
     *
1032
     * @param array $row a row from the table
1033
     *
1034
     * @return array
1035
     */
1036
    public function filter_data($row)
1037
    {
1038
        $url_params = $this->get_sortable_table_param_string().'&'.$this->get_additional_url_paramstring();
1039
        foreach ($this->column_filters as $column => &$function) {
1040
            $firstParam = isset($row[$column]) ? $row[$column] : 0;
1041
            $row[$column] = call_user_func($function, $firstParam, $url_params, $row);
1042
        }
1043
        if (count($this->form_actions) > 0) {
1044
            if (strlen($row[0]) > 0) {
1045
                $row[0] = '<div class="checkbox" ><label><input type="checkbox" name="'.$this->checkbox_name.'[]" value="'.$row[0].'"';
1046
                if (isset($_GET[$this->param_prefix.'selectall'])) {
1047
                    $row[0] .= ' checked="checked"';
1048
                }
1049
                $row[0] .= '/><span class="checkbox-material"><span class="check"></span></span></label></div>';
1050
            }
1051
        }
1052
        if (is_array($row)) {
1053
            foreach ($row as &$value) {
1054
                if (empty($value)) {
1055
                    $value = '-';
1056
                }
1057
            }
1058
        }
1059
1060
        return $row;
1061
    }
1062
1063
    /**
1064
     * Get the total number of items. This function calls the function given as
1065
     * 2nd argument in the constructor of a SortableTable. Make sure your
1066
     * function has the same parameters as defined here.
1067
     */
1068
    public function get_total_number_of_items()
1069
    {
1070
        if ($this->total_number_of_items == -1 && !is_null($this->get_total_number_function)) {
1071
            $this->total_number_of_items = call_user_func(
1072
                $this->get_total_number_function,
1073
                $this->getDataFunctionParams()
1074
            );
1075
        }
1076
1077
        return $this->total_number_of_items;
1078
    }
1079
1080
    /**
1081
     * @param int $value
1082
     */
1083
    public function setTotalNumberOfItems($value)
1084
    {
1085
        $this->total_number_of_items = (int) $value;
1086
    }
1087
1088
    /**
1089
     * Get the data to display.  This function calls the function given as
1090
     * 2nd argument in the constructor of a SortableTable. Make sure your
1091
     * function has the same parameters as defined here.
1092
     *
1093
     * @param int    $from      index of the first item to return
1094
     * @param int    $per_page  The number of items to return
1095
     * @param int    $column    The number of the column on which the data should be
1096
     * @param bool   $sort      Whether to sort or not
1097
     *                          sorted
1098
     * @param string $direction In which order should the data be sorted (ASC
1099
     *                          or DESC)
1100
     *
1101
     * @return array
1102
     */
1103
    public function get_table_data(
1104
        $from = null,
1105
        $per_page = null,
1106
        $column = null,
1107
        $direction = null,
1108
        $sort = null
1109
    ) {
1110
        $data = [];
1111
        if ($this->get_data_function !== null) {
1112
            $data = call_user_func(
1113
                $this->get_data_function,
1114
                $from,
1115
                $this->per_page,
1116
                $this->column,
1117
                $this->direction,
1118
                $this->dataFunctionParams
1119
            );
1120
        }
1121
1122
        return $data;
1123
    }
1124
1125
    /**
1126
     * @param array $data
1127
     */
1128
    public function setTableData($data)
1129
    {
1130
        $this->table_data = $data;
1131
    }
1132
}
1133