Passed
Push — 1.11.x ( 023fbb...1fab9d )
by Julito
12:47 queued 10s
created

SortableTable::__construct()   F

Complexity

Conditions 22
Paths > 20000

Size

Total Lines 106
Code Lines 65

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 22
eloc 65
nc 25600
nop 8
dl 0
loc 106
rs 0
c 2
b 1
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 System\Session::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
    public function toArray()
347
    {
348
        $headers = array_column($this->getHeaders(), 'label');
349
350
        return array_merge([$headers], $this->get_table_data());
351
    }
352
353
    /**
354
     * Displays the table, complete with navigation buttons to browse through
355
     * the data-pages.
356
     */
357
    public function return_table()
358
    {
359
        $empty_table = false;
360
        $content = $this->get_table_html();
361
        if ($this->get_total_number_of_items() == 0) {
362
            $cols = $this->getColCount();
363
            $this->setCellAttributes(
364
                1,
365
                0,
366
                'style="font-style: italic;text-align:center;" colspan='.$cols
367
            );
368
            $message_empty = api_xml_http_response_encode(get_lang('TheListIsEmpty'));
369
            $this->setCellContents(1, 0, $message_empty);
370
            $empty_table = true;
371
        }
372
        $html = '';
373
        if (!$empty_table) {
374
            $table_id = 'form_'.$this->table_name.'_id';
375
            $form = $this->get_page_select_form();
376
            $nav = $this->get_navigation_html();
377
378
            // Only show pagination info when there are items to paginate
379
            if ($this->get_total_number_of_items() > $this->default_items_per_page) {
380
                $html = '<div class="table-well">';
381
                $html .= '<table class="data_table_pagination">';
382
                $html .= '<tr>';
383
                $html .= '<td style="width:25%;">';
384
                $html .= $form;
385
                $html .= '</td>';
386
                $html .= '<td style="text-align:center;">';
387
                $html .= $this->get_table_title();
388
                $html .= '</td>';
389
                $html .= '<td style="text-align:right;width:25%;">';
390
                $html .= $nav;
391
                $html .= '</td>';
392
                $html .= '</tr>';
393
                $html .= '</table>';
394
                $html .= '</div>';
395
            }
396
397
            if (count($this->form_actions) > 0) {
398
                $params = $this->get_sortable_table_param_string().'&amp;'.$this->get_additional_url_paramstring();
399
                $html .= '<form id ="'.$table_id.'" class="form-search" method="post" action="'.api_get_self().'?'.$params.'" name="form_'.$this->table_name.'">';
400
            }
401
        }
402
403
        $html .= '<div class="table-responsive">'.$content.'</div>';
404
405
        if (!$empty_table) {
406
            if (!empty($this->additional_parameters)) {
407
                foreach ($this->additional_parameters as $key => $value) {
408
                    $html .= '<input type="hidden" name ="'.Security::remove_XSS($key).'" value ="'.Security::remove_XSS($value).'" />';
409
                }
410
            }
411
            $html .= '<input type="hidden" name="action">';
412
            $html .= '<table style="width:100%;">';
413
            $html .= '<tr>';
414
            $html .= '<td>';
415
416
            if (count($this->form_actions) > 0) {
417
                $html .= '<div class="btn-toolbar">';
418
                $html .= '<div class="btn-group">';
419
                $html .= '<a class="btn btn-default" href="?'.$params.'&amp;'.$this->param_prefix.'selectall=1" onclick="javascript: setCheckbox(true, \''.$table_id.'\'); return false;">'.get_lang('SelectAll').'</a>';
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...
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...
420
                $html .= '<a class="btn btn-default" href="?'.$params.'" onclick="javascript: setCheckbox(false, \''.$table_id.'\'); return false;">'.get_lang('UnSelectAll').'</a> ';
421
                $html .= '</div>';
422
                $html .= '<div class="btn-group">
423
                            <button class="btn btn-default" onclick="javascript:return false;">'.get_lang('Actions').'</button>
424
                            <button class="btn btn-default dropdown-toggle" data-toggle="dropdown">
425
                                <span class="caret"></span>
426
                            </button>';
427
                $html .= '<ul class="dropdown-menu">';
428
                foreach ($this->form_actions as $action => &$label) {
429
                    $html .= '<li><a data-action ="'.$action.'" href="#" onclick="javascript:action_click(this, \''.$table_id.'\');">'.$label.'</a></li>';
430
                }
431
                $html .= '</ul>';
432
                $html .= '</div>'; //btn-group
433
                $html .= '</div>'; //toolbar
434
            } else {
435
                $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...
436
            }
437
438
            $html .= '</td>';
439
            // Pagination
440
            if ($this->get_total_number_of_items() > $this->default_items_per_page) {
441
                $html .= '<td style="text-align:right;">';
442
                $html .= $nav;
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...
443
                $html .= '</td>';
444
            } else {
445
                $html .= '<td> ';
446
                $html .= '</td>';
447
            }
448
449
            $html .= '</tr>';
450
            $html .= '</table>';
451
            if (count($this->form_actions) > 0) {
452
                $html .= '</form>';
453
            }
454
        }
455
456
        return $html;
457
    }
458
459
    /**
460
     * This function shows the content of a table in a grid.
461
     * Should not be use to edit information (edit/delete rows) only.
462
     */
463
    public function display_grid()
464
    {
465
        $empty_table = false;
466
        if ($this->get_total_number_of_items() == 0) {
467
            $message_empty = api_xml_http_response_encode(get_lang('TheListIsEmpty'));
468
            $this->setCellContents(1, 0, $message_empty);
469
            $empty_table = true;
470
        }
471
        $html = '';
472
        if (!$empty_table) {
473
            $form = $this->get_page_select_form();
474
            $nav = $this->get_navigation_html();
475
476
            // @todo This style css must be moved to default.css only for dev
477
            echo '<style>
478
                    .main-grid { width:100%;}
479
                    .sub-header { width:100%; padding-top: 10px; padding-right: 10px; padding-left: 10px; height:30px;}
480
                    .grid_container { width:100%;}
481
                    .grid_item { height: 120px; width:98px;  float:left; padding:5px; margin:8px;}
482
                    .grid_element_0 { width:100px; height: 100px; float:left; text-align:center; margin-bottom:5px;}
483
                    .grid_element_1 { width:100px; float:left; text-align:center;margin-bottom:5px;}
484
                    .grid_element_2 { width:150px; float:left;}
485
486
                    .grid_selectbox { width:30%; float:left;}
487
                    .grid_title     { width:30%; float:left;}
488
                    .grid_nav         { }
489
            </style>';
490
491
            // @todo  This also must be moved
492
            // Show only navigations if there are more than 1 page
493
            $my_pager = $this->get_pager();
494
            $html .= '<div class="main-grid">';
495
            if ($my_pager->numPages() > 1) {
496
                $html .= '<div class="sub-header">';
497
                $html .= '<div class="grid_selectbox">'.$form.'</div>';
498
                $html .= '<div class="grid_title">'.$this->get_table_title().'</div>';
499
                $html .= '<div class="grid_nav">'.$nav.'</div>';
500
                $html .= '</div>';
501
            }
502
503
            $html .= '<div class="clear"></div>';
504
            if (count($this->form_actions) > 0) {
505
                $params = $this->get_sortable_table_param_string().'&amp;'.$this->get_additional_url_paramstring();
506
                $html .= '<form method="post" action="'.api_get_self().'?'.$params.'" name="form_'.$this->table_name.'">';
507
            }
508
        }
509
        // Getting the items of the table
510
        $items = $this->get_clean_html(false); //no sort
511
512
        // Generation of style classes must be improved. Maybe we need a a table name to create style on the fly:
513
        // i.e: .whoisonline_table_grid_container instead of  .grid_container
514
        // where whoisonline is the table's name like drupal's template engine
515
        $html .= '<div class="grid_container">';
516
        if (is_array($items) && count($items) > 0) {
517
            foreach ($items as &$row) {
518
                $html .= '<div class="grid_item">';
519
                $i = 0;
520
                foreach ($row as &$element) {
521
                    $html .= '<div class="grid_element_'.$i.'">'.$element.'</div>';
522
                    $i++;
523
                }
524
                $html .= '</div>';
525
            }
526
        }
527
        $html .= '</div>'; //close grid_container
528
        $html .= '</div>'; //close main grid
529
        $html .= '<div class="clear"></div>';
530
531
        echo $html;
532
    }
533
534
    /**
535
     * This function returns the content of a table in a grid
536
     * Should not be use to edit information (edit/delete rows) only.
537
     *
538
     * @param array      options of visibility
539
     * @param bool       hide navigation optionally
540
     * @param int        content per page when show navigation (optional)
541
     * @param bool       sort data optionally
542
     *
543
     * @return string grid html
544
     */
545
    public function display_simple_grid(
546
        $visibility_options,
547
        $hide_navigation = true,
548
        $per_page = 20,
549
        $sort_data = true,
550
        $grid_class = []
551
    ) {
552
        $empty_table = false;
553
        if ($this->get_total_number_of_items() == 0) {
554
            $message_empty = api_xml_http_response_encode(get_lang('TheListIsEmpty'));
555
            $this->setCellContents(1, 0, $message_empty);
556
            $empty_table = true;
557
        }
558
        $html = '';
559
        if (!$empty_table) {
560
            // If we show the pagination
561
            if (!$hide_navigation) {
562
                $form = '&nbsp;';
563
564
                if ($this->get_total_number_of_items() > $per_page) {
565
                    if ($per_page > 10) {
566
                        $form = $this->get_page_select_form();
567
                    }
568
                    $nav = $this->get_navigation_html();
569
                    // This also must be moved
570
                    $html = '<div class="sub-header">';
571
                    $html .= '<div class="grid_selectbox">'.$form.'</div>';
572
                    $html .= '<div class="grid_title">'.$this->get_table_title().'</div>';
573
                    $html .= '<div class="grid_nav">'.$nav.'</div>';
574
                    $html .= '</div>';
575
                }
576
            }
577
578
            $html .= '<div class="clear"></div>';
579
            if (count($this->form_actions) > 0) {
580
                $params = $this->get_sortable_table_param_string().'&amp;'.$this->get_additional_url_paramstring();
581
                $html .= '<form method="post" action="'.api_get_self().'?'.$params.'" name="form_'.$this->table_name.'">';
582
            }
583
        }
584
585
        if ($hide_navigation) {
586
            $items = $this->table_data; // This is a faster way to get what we want
587
        } else {
588
            // The normal way
589
            $items = $this->get_clean_html($sort_data); // Getting the items of the table
590
        }
591
592
        // Generation of style classes must be improved. Maybe we need a a table name to create style on the fly:
593
        // i.e: .whoisonline_table_grid_container instead of  .grid_container
594
        // where whoisonline is the table's name like drupal's template engine
595
596
        if (is_array($visibility_options)) {
597
            $filter = false; // The 2nd condition of the if will be loaded
598
        } else {
599
            $filter = $visibility_options !== false;
600
        }
601
602
        $item_css_class = $item_css_style = $grid_css_class = $grid_css_style = '';
603
        if (!empty($grid_class)) {
604
            $grid_css_class = $grid_class['main']['class'];
605
            $item_css_class = $grid_class['item']['class'];
606
607
            $grid_css_style = isset($grid_class['main']['style']) ? $grid_class['main']['style'] : null;
608
            $item_css_style = isset($grid_class['item']['style']) ? $grid_class['item']['style'] : null;
609
        }
610
611
        $div = '';
612
        if (is_array($items) && count($items) > 0) {
613
            foreach ($items as &$row) {
614
                $i = 0;
615
                $rows = '';
616
                foreach ($row as &$element) {
617
                    if ($filter ||
618
                        isset($visibility_options[$i]) && $visibility_options[$i]
619
                    ) {
620
                        $rows .= '<div class="'.$this->table_name.'_grid_element_'.$i.'">'.$element.'</div>';
621
                    }
622
                    $i++;
623
                }
624
                $div .= Display::div(
625
                    $rows,
626
                    [
627
                        'class' => $item_css_class.' '.$this->table_name.'_grid_item',
628
                        'style' => $item_css_style,
629
                    ]
630
                );
631
            }
632
        }
633
634
        $html .= Display::div(
635
            $div,
636
            [
637
                'class' => $grid_css_class.' '.$this->table_name.'_grid_container',
638
                'style' => $grid_css_style,
639
            ]
640
        );
641
        $html .= '<div class="clear"></div>';
642
643
        return $html;
644
    }
645
646
    /**
647
     * Get the HTML-code with the navigational buttons to browse through the
648
     * data-pages.
649
     */
650
    public function get_navigation_html()
651
    {
652
        $pager = $this->get_pager();
653
        $pager_links = $pager->getLinks();
654
        $nav = $pager_links['first'].' '.$pager_links['back'];
655
        $nav .= ' '.$pager->getCurrentPageId().' / '.$pager->numPages().' ';
656
        $nav .= $pager_links['next'].' '.$pager_links['last'];
657
658
        return $nav;
659
    }
660
661
    /**
662
     * Get the HTML-code with the data-table.
663
     */
664
    public function get_table_html()
665
    {
666
        $pager = $this->get_pager();
667
        $offset = $pager->getOffsetByPageId();
668
        $from = $offset[0] - 1;
669
        $table_data = $this->get_table_data($from);
670
        $this->processHeaders();
671
672
        if (is_array($table_data)) {
673
            $count = 1;
674
            foreach ($table_data as &$row) {
675
                $row = $this->filter_data($row);
676
677
                $newRow = [];
678
                if (!empty($this->columnsToHide)) {
679
                    $counter = 0;
680
                    foreach ($row as $index => $rowInfo) {
681
                        if (!isset($this->columnsToHide[$index])) {
682
                            $newRow[$counter] = $rowInfo;
683
                            $counter++;
684
                        }
685
                    }
686
                    $row = $newRow;
687
                }
688
                $this->addRow($row);
689
                if (isset($row['child_of'])) {
690
                    $this->setRowAttributes(
691
                        $count,
692
                        ['class' => 'hidden hidden_'.$row['child_of']],
693
                        true
694
                    );
695
                }
696
                $count++;
697
            }
698
        }
699
700
        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...
701
            $this->altRowAttributes(
702
                0,
703
                ['class' => 'row_odd'],
704
                ['class' => 'row_even'],
705
                true
706
            );
707
        }
708
709
        foreach ($this->th_attributes as $column => $attributes) {
710
            $this->setCellAttributes(0, $column, $attributes);
711
        }
712
        foreach ($this->td_attributes as $column => $attributes) {
713
            $this->setColAttributes($column, $attributes);
714
        }
715
716
        return $this->toHTML();
717
    }
718
719
    /**
720
     * This function return the items of the table.
721
     *
722
     * @param bool true for sorting table data or false otherwise
723
     *
724
     * @return array table row items
725
     */
726
    public function get_clean_html($sort = true)
727
    {
728
        $pager = $this->get_pager();
729
        $offset = $pager->getOffsetByPageId();
730
        $from = $offset[0] - 1;
731
        $table_data = $this->get_table_data($from, null, null, null, $sort);
732
        $new_table_data = [];
733
        if (is_array($table_data)) {
734
            foreach ($table_data as $index => &$row) {
735
                $row = $this->filter_data($row);
736
                $new_table_data[] = $row;
737
            }
738
        }
739
740
        return $new_table_data;
741
    }
742
743
    /**
744
     * Get the HTML-code which represents a form to select how many items a page
745
     * should contain.
746
     */
747
    public function get_page_select_form()
748
    {
749
        $total_number_of_items = $this->get_total_number_of_items();
750
        if ($total_number_of_items <= $this->default_items_per_page) {
751
            return '';
752
        }
753
754
        if ($this->hideItemSelector === true) {
755
            return '';
756
        }
757
        $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...
758
        $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...
759
        $param[$this->param_prefix.'page_nr'] = $this->page_nr;
760
        $param[$this->param_prefix.'column'] = $this->column;
761
762
        if (is_array($this->additional_parameters)) {
763
            $param = array_merge($param, $this->additional_parameters);
764
        }
765
766
        foreach ($param as $key => &$value) {
767
            $result[] = '<input type="hidden" name="'.$key.'" value="'.$value.'"/>';
768
        }
769
        $result[] = '<select style="width: auto;" class="form-control" name="'.$this->param_prefix.'per_page" onchange="javascript: this.form.submit();">';
770
        $list = [10, 20, 50, 100, 500, 1000];
771
772
        $rowList = api_get_configuration_value('table_row_list');
773
        if (!empty($rowList) && isset($rowList['options'])) {
774
            $list = $rowList['options'];
775
        }
776
777
        foreach ($list as $nr) {
778
            if ($total_number_of_items <= $nr) {
779
                break;
780
            }
781
            $result[] = '<option value="'.$nr.'" '.($nr == $this->per_page ? 'selected="selected"' : '').'>'.$nr.'</option>';
782
        }
783
784
        $result[] = '<option value="'.$total_number_of_items.'" '.($total_number_of_items == $this->per_page ? 'selected="selected"' : '').'>'.api_ucfirst(get_lang('All')).'</option>';
785
        //}
786
        $result[] = '</select>';
787
        $result[] = '<noscript>';
788
        $result[] = '<button class="btn btn-success" type="submit">'.get_lang('Save').'</button>';
789
        $result[] = '</noscript>';
790
        $result[] = '</form>';
791
        $result = implode("\n", $result);
792
793
        return $result;
794
    }
795
796
    /**
797
     * Get the table title.
798
     */
799
    public function get_table_title()
800
    {
801
        $pager = $this->get_pager();
802
        $showed_items = $pager->getOffsetByPageId();
803
804
        return $showed_items[0].' - '.$showed_items[1].' / '.$this->get_total_number_of_items();
805
    }
806
807
    /**
808
     * @return array
809
     */
810
    public function getHeaders()
811
    {
812
        return $this->headers;
813
    }
814
815
    /**
816
     * Process headers.
817
     */
818
    public function processHeaders()
819
    {
820
        $counter = 0;
821
        foreach ($this->headers as $column => $columnInfo) {
822
            $label = $columnInfo['label'];
823
            $sortable = $columnInfo['sortable'];
824
            $th_attributes = $columnInfo['th_attributes'];
825
            $td_attributes = $columnInfo['td_attributes'];
826
827
            if (!empty($this->columnsToHide)) {
828
                if (isset($this->columnsToHide[$column])) {
829
                    continue;
830
                }
831
            }
832
833
            $column = $counter;
834
            $param['direction'] = 'ASC';
835
            if ($this->column == $column && $this->direction == 'ASC') {
836
                $param['direction'] = 'DESC';
837
            }
838
839
            $param['page_nr'] = $this->page_nr;
840
            $param['per_page'] = $this->per_page;
841
            $param['column'] = $column;
842
            $link = $label;
843
            if ($sortable) {
844
                $link = '<a href="'.api_get_self().'?'.api_get_cidreq().'&amp;';
845
                foreach ($param as $key => &$value) {
846
                    $link .= $this->param_prefix.$key.'='.urlencode($value).'&amp;';
847
                }
848
                $link .= $this->get_additional_url_paramstring();
849
                $link .= '">'.$label.'</a>';
850
                if ($this->column == $column) {
851
                    $link .= $this->direction == 'ASC' ? ' &#8595;' : ' &#8593;';
852
                }
853
            }
854
            $this->setHeaderContents(0, $column, $link);
855
856
            if (!is_null($td_attributes)) {
857
                $this->td_attributes[$column] = $td_attributes;
858
            }
859
            if (!is_null($th_attributes)) {
860
                $this->th_attributes[$column] = $th_attributes;
861
            }
862
863
            $counter++;
864
        }
865
    }
866
867
    /**
868
     * Set the header-label.
869
     *
870
     * @param int    $column        The column number
871
     * @param string $label         The label
872
     * @param bool   $sortable      Is the table sortable by this column? (defatult
873
     *                              = true)
874
     * @param string $th_attributes Additional attributes for the th-tag of the
875
     *                              table header
876
     * @param string $td_attributes Additional attributes for the td-tags of the
877
     *                              column
878
     */
879
    public function set_header(
880
        $column,
881
        $label,
882
        $sortable = true,
883
        $th_attributes = ['class' => 'th-header'],
884
        $td_attributes = null
885
    ) {
886
        $this->headers[$column] = [
887
            'label' => $label,
888
            'sortable' => $sortable,
889
            'th_attributes' => $th_attributes,
890
            'td_attributes' => $td_attributes,
891
        ];
892
    }
893
894
    /**
895
     * Get the parameter-string with additional parameters to use in the URLs
896
     * generated by this SortableTable.
897
     */
898
    public function get_additional_url_paramstring()
899
    {
900
        $param_string_parts = [];
901
        if (is_array($this->additional_parameters) && count($this->additional_parameters) > 0) {
902
            foreach ($this->additional_parameters as $key => &$value) {
903
                $param_string_parts[] = urlencode($key).'='.urlencode($value);
904
            }
905
        }
906
        $result = implode('&amp;', $param_string_parts);
907
        foreach ($this->other_tables as $index => &$tablename) {
908
            $param = [];
909
            if (isset($_GET[$tablename.'_direction'])) {
910
                //$param[$tablename.'_direction'] = $_GET[$tablename.'_direction'];
911
                $my_get_direction = $_GET[$tablename.'_direction'];
912
                if (!in_array($my_get_direction, ['ASC', 'DESC'])) {
913
                    $param[$tablename.'_direction'] = 'ASC';
914
                } else {
915
                    $param[$tablename.'_direction'] = $my_get_direction;
916
                }
917
            }
918
            if (isset($_GET[$tablename.'_page_nr'])) {
919
                $param[$tablename.'_page_nr'] = intval($_GET[$tablename.'_page_nr']);
920
            }
921
            if (isset($_GET[$tablename.'_per_page'])) {
922
                $param[$tablename.'_per_page'] = intval($_GET[$tablename.'_per_page']);
923
            }
924
            if (isset($_GET[$tablename.'_column'])) {
925
                $param[$tablename.'_column'] = intval($_GET[$tablename.'_column']);
926
            }
927
            $param_string_parts = [];
928
            foreach ($param as $key => &$value) {
929
                $param_string_parts[] = urlencode($key).'='.urlencode($value);
930
            }
931
            if (count($param_string_parts) > 0) {
932
                $result .= '&amp;'.implode('&amp;', $param_string_parts);
933
            }
934
        }
935
936
        return $result;
937
    }
938
939
    /**
940
     * Get the parameter-string with the SortableTable-related parameters to use
941
     * in URLs.
942
     */
943
    public function get_sortable_table_param_string()
944
    {
945
        $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...
946
        $param[$this->param_prefix.'page_nr'] = $this->page_nr;
947
        $param[$this->param_prefix.'per_page'] = $this->per_page;
948
        $param[$this->param_prefix.'column'] = $this->column;
949
        $param_string_parts = [];
950
        foreach ($param as $key => &$value) {
951
            $param_string_parts[] = urlencode($key).'='.urlencode($value);
952
        }
953
        $res = implode('&amp;', $param_string_parts);
954
955
        return $res;
956
    }
957
958
    /**
959
     * Add a filter to a column. If another filter was already defined for the
960
     * given column, it will be overwritten.
961
     *
962
     * @param int    $column   The number of the column
963
     * @param string $function The name of the filter-function. This should be a
964
     *                         function wich requires 1 parameter and returns the filtered value.
965
     */
966
    public function set_column_filter($column, $function)
967
    {
968
        $this->column_filters[$column] = $function;
969
    }
970
971
    /**
972
     * List of columns to hide.
973
     *
974
     * @param int $column
975
     */
976
    public function setHideColumn($column)
977
    {
978
        $this->columnsToHide[$column] = $column;
979
    }
980
981
    /**
982
     * Define a list of actions which can be performed on the table-date.
983
     * If you define a list of actions, the first column of the table will be
984
     * converted into checkboxes.
985
     *
986
     * @param array  $actions       A list of actions. The key is the name of the
987
     *                              action. The value is the label to show in the select-box
988
     * @param string $checkbox_name The name of the generated checkboxes. The
989
     *                              value of the checkbox will be the value of the first column.
990
     */
991
    public function set_form_actions($actions, $checkbox_name = 'id')
992
    {
993
        $this->form_actions = $actions;
994
        $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...
995
    }
996
997
    /**
998
     * Define a list of additional parameters to use in the generated URLs
999
     * <code>$parameters['action'] = 'test'; will be convert in
1000
     * <input type="hidden" name="action" value="test"></code>.
1001
     *
1002
     * @param array $parameters
1003
     */
1004
    public function set_additional_parameters($parameters)
1005
    {
1006
        $this->additional_parameters = $parameters;
1007
    }
1008
1009
    /**
1010
     * Set other tables on the same page.
1011
     * If you have other sortable tables on the page displaying this sortable
1012
     * tables, you can define those other tables with this function. If you
1013
     * don't define the other tables, there sorting and pagination will return
1014
     * to their default state when sorting this table.
1015
     *
1016
     * @param array $tablenames an array of table names
1017
     */
1018
    public function set_other_tables($tablenames)
1019
    {
1020
        $this->other_tables = $tablenames;
1021
    }
1022
1023
    /**
1024
     * Transform all data in a table-row, using the filters defined by the
1025
     * function set_column_filter(...) defined elsewhere in this class.
1026
     * If you've defined actions, the first element of the given row will be
1027
     * converted into a checkbox.
1028
     *
1029
     * @param array $row a row from the table
1030
     *
1031
     * @return array
1032
     */
1033
    public function filter_data($row)
1034
    {
1035
        $url_params = $this->get_sortable_table_param_string().'&'.$this->get_additional_url_paramstring();
1036
        foreach ($this->column_filters as $column => &$function) {
1037
            $firstParam = isset($row[$column]) ? $row[$column] : 0;
1038
            $row[$column] = call_user_func($function, $firstParam, $url_params, $row);
1039
        }
1040
        if (count($this->form_actions) > 0) {
1041
            if (strlen($row[0]) > 0) {
1042
                $row[0] = '<input type="checkbox" name="'.$this->checkbox_name.'[]" value="'.$row[0].'"';
1043
                if (isset($_GET[$this->param_prefix.'selectall'])) {
1044
                    $row[0] .= ' checked="checked"';
1045
                }
1046
                $row[0] .= '/>';
1047
            }
1048
        }
1049
        if (is_array($row)) {
1050
            foreach ($row as &$value) {
1051
                if (empty($value)) {
1052
                    $value = '-';
1053
                }
1054
            }
1055
        }
1056
1057
        return $row;
1058
    }
1059
1060
    /**
1061
     * Get the total number of items. This function calls the function given as
1062
     * 2nd argument in the constructor of a SortableTable. Make sure your
1063
     * function has the same parameters as defined here.
1064
     */
1065
    public function get_total_number_of_items()
1066
    {
1067
        if ($this->total_number_of_items == -1 && !is_null($this->get_total_number_function)) {
1068
            $this->total_number_of_items = call_user_func(
1069
                $this->get_total_number_function,
1070
                $this->getDataFunctionParams()
1071
            );
1072
        }
1073
1074
        return $this->total_number_of_items;
1075
    }
1076
1077
    /**
1078
     * @param int $value
1079
     */
1080
    public function setTotalNumberOfItems($value)
1081
    {
1082
        $this->total_number_of_items = (int) $value;
1083
    }
1084
1085
    /**
1086
     * Get the data to display.  This function calls the function given as
1087
     * 2nd argument in the constructor of a SortableTable. Make sure your
1088
     * function has the same parameters as defined here.
1089
     *
1090
     * @param int    $from      index of the first item to return
1091
     * @param int    $per_page  The number of items to return
1092
     * @param int    $column    The number of the column on which the data should be
1093
     * @param bool   $sort      Whether to sort or not
1094
     *                          sorted
1095
     * @param string $direction In which order should the data be sorted (ASC
1096
     *                          or DESC)
1097
     *
1098
     * @return array
1099
     */
1100
    public function get_table_data(
1101
        $from = null,
1102
        $per_page = null,
1103
        $column = null,
1104
        $direction = null,
1105
        $sort = null
1106
    ) {
1107
        $data = [];
1108
        if ($this->get_data_function !== null) {
1109
            $data = call_user_func(
1110
                $this->get_data_function,
1111
                $from,
1112
                $this->per_page,
1113
                $this->column,
1114
                $this->direction,
1115
                $this->dataFunctionParams
1116
            );
1117
        }
1118
1119
        return $data;
1120
    }
1121
1122
    /**
1123
     * @param array $data
1124
     */
1125
    public function setTableData($data)
1126
    {
1127
        $this->table_data = $data;
1128
    }
1129
}
1130