SortableTable::__construct()   F
last analyzed

Complexity

Conditions 21
Paths > 20000

Size

Total Lines 116
Code Lines 74

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 21
eloc 74
c 0
b 0
f 0
nc 25600
nop 8
dl 0
loc 116
rs 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
3
/* For licensing terms, see /license.txt */
4
5
use ChamiloSession as Session;
6
7
/**
8
 * This class allows you to display a sortable data-table. It is possible to
9
 * split the data in several pages.
10
 * Using this class you can:
11
 * - automatically create checkboxes of the first table column
12
 *     - a "select all" and "deselect all" link is added
13
 *     - only if you provide a list of actions for the selected items
14
 * - click on the table header to sort the data
15
 * - choose how many items you see per page
16
 * - navigate through all data-pages.
17
 */
18
class SortableTable extends HTML_Table
19
{
20
    /**
21
     * A name for this table.
22
     */
23
    public $table_name;
24
    /**
25
     * The page to display.
26
     */
27
    public $page_nr;
28
    /**
29
     * The column to sort the data.
30
     */
31
    public $column;
32
    /**
33
     * The sorting direction (ASC or DESC).
34
     */
35
    public $direction;
36
    /**
37
     * Number of items to display per page.
38
     */
39
    public $per_page;
40
    /**
41
     * The default number of items to display per page.
42
     */
43
    public $default_items_per_page;
44
    /**
45
     * A prefix for the URL-parameters, can be used on pages with multiple
46
     * SortableTables.
47
     */
48
    public $param_prefix;
49
    /**
50
     * The pager object to split the data in several pages.
51
     */
52
    public $pager;
53
    /**
54
     * The total number of items in the table.
55
     */
56
    public $total_number_of_items;
57
    /**
58
     * The function to get the total number of items.
59
     */
60
    public $get_total_number_function;
61
    /**
62
     * The function to the the data to display.
63
     */
64
    public $get_data_function;
65
    /**
66
     * An array with defined column-filters.
67
     */
68
    public $column_filters;
69
    /**
70
     * A list of actions which will be available through a select list.
71
     */
72
    public $form_actions;
73
    /**
74
     * Additional parameters to pass in the URL.
75
     */
76
    public $additional_parameters;
77
    /**
78
     * Additional attributes for the th-tags.
79
     */
80
    public $th_attributes;
81
    /**
82
     * Additional attributes for the td-tags.
83
     */
84
    public $td_attributes;
85
    /**
86
     * Array with names of the other tables defined on the same page of this
87
     * table.
88
     */
89
    public $other_tables;
90
    /**
91
     * Activates the odd even rows.
92
     * */
93
    public $odd_even_rows_enabled = true;
94
    public $use_jqgrid = false;
95
    public $table_id = null;
96
    public $headers = [];
97
    public $actionButtons = [];
98
    /**
99
     * The array containing all data for this table.
100
     */
101
    public $table_data;
102
    public $hideItemSelector;
103
    // Hide table navigation, better to be use when exporting table to PDF.
104
    public $hideNavigation = false;
105
106
    /**
107
     * @var array Columns to hide
108
     */
109
    private $columnsToHide = [];
110
    private $dataFunctionParams;
111
    private $defaultColumn;
112
    private $defaultItemsPerPage;
113
114
    /**
115
     * Create a new SortableTable.
116
     *
117
     * @param string $table_name                A name for the table (default = 'table')
118
     * @param string $get_total_number_function A user defined function to get
119
     *                                          the total number of items in the table
120
     * @param string $get_data_function         A function to get the data to display on
121
     *                                          the current page
122
     * @param int    $default_column            The default column on which the data should be
123
     *                                          sorted
124
     * @param int    $default_items_per_page    The default number of items to show
125
     *                                          on one page
126
     * @param string $default_order_direction   The default order direction;
127
     *                                          either the constant 'ASC' or 'DESC'
128
     * @param string $table_id
129
     * @param array  $attributes                They are custom attributes of the table
130
     */
131
    public function __construct(
132
        $table_name = 'table',
133
        $get_total_number_function = null,
134
        $get_data_function = null,
135
        $default_column = 1,
136
        $default_items_per_page = 20,
137
        $default_order_direction = 'ASC',
138
        $table_id = null,
139
        $attributes = []
140
    ) {
141
        if (empty($table_id)) {
142
            $table_id = $table_name.uniqid('table', true);
143
        }
144
145
        if (empty($attributes)) {
146
            $attributes = [];
147
            $attributes['class'] = 'table table-hover table-striped table-bordered data_table';
148
            //$attributes['class'] = 'q-table';
149
            $attributes['id'] = $table_id;
150
        }
151
152
        $this->table_id = $table_id;
153
        parent::__construct($attributes);
154
        $this->table_name = $table_name;
155
        $this->additional_parameters = [];
156
        $this->param_prefix = $table_name.'_';
157
        $this->defaultColumn = (int) $default_column;
158
        $this->defaultItemsPerPage = $default_items_per_page;
159
        $this->hideItemSelector = false;
160
161
        $defaultRow = (int) api_get_setting('platform.table_default_row');
162
        if ($defaultRow > 0) {
163
            $this->defaultItemsPerPage = $default_items_per_page = $defaultRow;
164
        }
165
166
        $cleanSessionData = Session::read('clean_sortable_table');
167
        if (true === $cleanSessionData) {
168
            $this->cleanUrlSessionParams();
169
        }
170
        // Allow to change paginate in multiples tabs
171
        $this->per_page = Session::read($this->param_prefix.'per_page', $default_items_per_page);
172
173
        // If per page changed, then reset the page to 1
174
        if (!empty($this->per_page) && isset($_GET[$this->param_prefix.'per_page']) &&
175
            $this->per_page != $_GET[$this->param_prefix.'per_page']
176
        ) {
177
            Session::erase($this->param_prefix.'page_nr');
178
            $_GET[$this->param_prefix.'page_nr'] = 1;
179
        }
180
181
        $this->per_page = isset($_GET[$this->param_prefix.'per_page'])
182
            ? (int) $_GET[$this->param_prefix.'per_page']
183
            : $this->per_page;
184
185
        if (isset($_GET[$this->param_prefix.'per_page'])) {
186
            Session::erase($this->param_prefix.'page_nr');
187
        }
188
189
        $this->page_nr = Session::read($this->param_prefix.'page_nr', 1);
190
        $this->page_nr = isset($_GET[$this->param_prefix.'page_nr'])
191
            ? (int) $_GET[$this->param_prefix.'page_nr']
192
            : $this->page_nr;
193
194
        $this->column = Session::read($this->param_prefix.'column', $default_column);
195
        $this->column = isset($_GET[$this->param_prefix.'column'])
196
            ? (int) $_GET[$this->param_prefix.'column']
197
            : $this->column;
198
199
        // Default direction.
200
        if (in_array(strtoupper($default_order_direction), ['ASC', 'DESC'])) {
201
            $this->direction = $default_order_direction;
202
        }
203
204
        $my_session_direction = Session::read($this->param_prefix.'direction');
205
        if (!empty($my_session_direction)) {
206
            if (!in_array($my_session_direction, ['ASC', 'DESC'])) {
207
                $this->direction = 'ASC';
208
            } else {
209
                if ('ASC' === $my_session_direction) {
210
                    $this->direction = 'ASC';
211
                } elseif ('DESC' === $my_session_direction) {
212
                    $this->direction = 'DESC';
213
                }
214
            }
215
        }
216
217
        if (isset($_GET[$this->param_prefix.'direction'])) {
218
            $my_get_direction = $_GET[$this->param_prefix.'direction'];
219
            if (!in_array($my_get_direction, ['ASC', 'DESC'])) {
220
                $this->direction = 'ASC';
221
            } else {
222
                if ('ASC' === $my_get_direction) {
223
                    $this->direction = 'ASC';
224
                } elseif ('DESC' === $my_get_direction) {
225
                    $this->direction = 'DESC';
226
                }
227
            }
228
        }
229
230
        Session::write($this->param_prefix.'per_page', $this->per_page);
231
        Session::write($this->param_prefix.'direction', $this->direction);
232
        Session::write($this->param_prefix.'page_nr', $this->page_nr);
233
        Session::write($this->param_prefix.'column', $this->column);
234
235
        $this->pager = null;
236
        $this->default_items_per_page = $default_items_per_page;
237
        $this->total_number_of_items = -1;
238
        $this->get_total_number_function = $get_total_number_function;
239
        $this->get_data_function = $get_data_function;
240
        $this->column_filters = [];
241
        $this->form_actions = [];
242
        $this->checkbox_name = null;
243
        $this->td_attributes = [];
244
        $this->th_attributes = [];
245
        $this->other_tables = [];
246
        $this->dataFunctionParams = [];
247
    }
248
249
    /**
250
     * Clean URL params when changing student view.
251
     */
252
    public function cleanUrlSessionParams()
253
    {
254
        Session::erase('clean_sortable_table');
255
256
        $prefix = $this->param_prefix;
257
258
        Session::erase($prefix.'page_nr');
259
        Session::erase($prefix.'column');
260
        Session::erase($prefix.'direction');
261
        Session::erase($prefix.'per_page');
262
263
        $_GET[$this->param_prefix.'per_page'] = $this->default_items_per_page;
264
        $_GET[$this->param_prefix.'page_nr'] = 1;
265
        $_GET[$this->param_prefix.'column'] = $this->defaultColumn;
266
        $_GET[$this->param_prefix.'direction'] = $this->direction;
267
    }
268
269
    /**
270
     * @return array
271
     */
272
    public function getDataFunctionParams()
273
    {
274
        return $this->dataFunctionParams;
275
    }
276
277
    /**
278
     * @param array $dataFunctionParams
279
     *
280
     * @return $this
281
     */
282
    public function setDataFunctionParams($dataFunctionParams)
283
    {
284
        $this->dataFunctionParams = $dataFunctionParams;
285
286
        return $this;
287
    }
288
289
    /**
290
     * Get the Pager object to split the showed data in several pages.
291
     *
292
     * @return Pager_Sliding
293
     */
294
    public function get_pager()
295
    {
296
        if (null === $this->pager) {
297
            $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...
298
            $params['perPage'] = $this->per_page;
299
            $params['totalItems'] = $this->get_total_number_of_items();
300
            $params['urlVar'] = $this->param_prefix.'page_nr';
301
            $params['currentPage'] = $this->page_nr;
302
            $icon_attributes = ['style' => 'vertical-align: middle;'];
303
            $params['prevImg'] = Display::getMdiIcon('step-backward');
304
            $params['nextImg'] = Display::getMdiIcon('step-forward');
305
            $params['firstPageText'] = Display::getMdiIcon('step-backward-2');
306
            $params['lastPageText'] = Display::getMdiIcon('step-forward-2');
307
            $params['firstPagePre'] = '';
308
            $params['lastPagePre'] = '';
309
            $params['firstPagePost'] = '';
310
            $params['lastPagePost'] = '';
311
            $params['spacesBeforeSeparator'] = '';
312
            $params['spacesAfterSeparator'] = '';
313
            $params['curPageLinkClassName'] = 'ch-pager';
314
            $query_vars = array_keys($_GET);
315
            $query_vars_needed = [
316
                $this->param_prefix.'column',
317
                $this->param_prefix.'direction',
318
                $this->param_prefix.'per_page',
319
            ];
320
            if (!empty($this->additional_parameters) && count($this->additional_parameters) > 0) {
321
                $query_vars_needed = array_merge(
322
                    $query_vars_needed,
323
                    array_keys($this->additional_parameters)
324
                );
325
            }
326
            $query_vars_exclude = array_diff($query_vars, $query_vars_needed);
327
            $params['excludeVars'] = $query_vars_exclude;
328
            $this->pager = &Pager::factory($params);
329
        }
330
331
        return $this->pager;
332
    }
333
334
    /**
335
     * Display the table.
336
     */
337
    public function display()
338
    {
339
        echo $this->return_table();
340
    }
341
342
    public function toArray()
343
    {
344
        $headers = array_column($this->getHeaders(), 'label');
345
346
        return array_merge([$headers], $this->table_data);
347
    }
348
349
    /**
350
     * Displays the table, complete with navigation buttons to browse through
351
     * the data-pages.
352
     */
353
    public function return_table()
354
    {
355
        $empty_table = false;
356
        $content = $this->get_table_html();
357
        if (0 == $this->get_total_number_of_items()) {
358
            $cols = $this->getColCount();
359
            $this->setCellAttributes(
360
                1,
361
                0,
362
                'style="font-style: italic;text-align:center;" colspan='.$cols
363
            );
364
            $message_empty = api_xml_http_response_encode(get_lang('Empty'));
365
            $this->setCellContents(1, 0, $message_empty);
366
            $empty_table = true;
367
        }
368
369
        if ($empty_table) {
370
            return '';
371
        }
372
373
        $params = $this->get_sortable_table_param_string().'&amp;'.$this->get_additional_url_paramstring();
374
        $table_id = 'form_'.$this->table_name.'_id';
375
        $html = '';
376
        $form = '';
377
        $nav = '';
378
        if (false === $this->hideNavigation) {
379
            // Only show pagination if there are more than 1 page.
380
            if ($this->get_pager()->numPages() > 1) {
381
                $form = $this->get_page_select_form();
382
                $nav = $this->get_navigation_html();
383
                $html = '<div class="q-card sortable-buttons-actions">';
384
                $html .= '<div class="flex flex-row justify-between pager-bar">';
385
                $html .= '<div class="col">';
386
                $html .= '<div class="pb-2 pt-2 pager-select">'.$form.'</div>';
387
                $html .= '</div>';
388
                $html .= '<div class="col">';
389
                $html .= '<div class="row justify-center pager-counter">'.$this->get_table_title().'</div>';
390
                $html .= '</div>';
391
                $html .= '<div class="col">';
392
                $html .= '<div class="row justify-end pager-jumper">'.$nav.'</div>';
393
                $html .= '</div>';
394
                $html .= '</div>';
395
                $html .= '</div>';
396
            }
397
        }
398
399
        if (count($this->form_actions) > 0) {
400
            $params = $this->get_sortable_table_param_string().'&amp;'.$this->get_additional_url_paramstring();
401
            $html .= '<form
402
                id ="'.$table_id.'"
403
                class="form-search"
404
                method="post"
405
                action="'.api_get_self().'?'.$params.'"
406
                name="form_'.$this->table_name.'">';
407
        }
408
409
        //$html .= '<div class="table-responsive">'.$content.'</div>';
410
        $html .= '<div class="sortable-container">';
411
        $html .= $content.'</div>';
412
413
        if (!empty($this->additional_parameters)) {
414
            foreach ($this->additional_parameters as $key => $value) {
415
                $html .= '<input type="hidden" name="'.Security::remove_XSS($key).'" value ="'
416
                    .Security::remove_XSS($value).'" />';
417
            }
418
        }
419
        $html .= '<input type="hidden" name="action">';
420
        $html .= '<div class="flex q-card p-2 mb-4 sortable-buttons-actions">';
421
        $html .= '<div class="flex w-full items-center justify-between">';
422
423
        if (count($this->actionButtons) > 0) {
424
            $html .= '<div class="btn-toolbar flex space-x-2">';
425
            $html .= '<div class="btn-group">';
426
427
            foreach ($this->actionButtons as $action => $data) {
428
                $label = $data['label'];
429
                $icon = $data['icon'];
430
                $html .= '<a class="btn btn-default" href="?'.$params.'&action_table='.$action.'">'.$icon.'&nbsp;'.$label.'</a>';
431
            }
432
            $html .= '</div>';
433
            $html .= '</div>';
434
        }
435
436
        if (count($this->form_actions) > 0) {
437
            $html .= '<div class="flex space-x-2">';
438
            $html .= '<a class="btn btn--action mr-2" href="?'.$params.'&amp;'.$this->param_prefix.'selectall=1" onclick="javascript: setCheckbox(true, \''.$table_id.'\'); return false;">'
439
                .get_lang('Select all').'</a>';
440
            $html .= '<a class="btn btn--action mr-2" href="?'.$params.'" onclick="javascript: setCheckbox(false, \''.$table_id.'\'); return false;">'
441
                .get_lang('Deselect all').'</a>';
442
443
            $items = [];
444
            foreach ($this->form_actions as $action => $label) {
445
                $items[] = [
446
                    'href' => 'javascript:void();',
447
                    'onclick' => "javascript:action_click(this, '$table_id');",
448
                    'title' => $label,
449
                    'data-action' => $action,
450
                    'data-confirm' => addslashes(api_htmlentities(get_lang("Please confirm your choice"))),
451
                ];
452
            }
453
            $html .= Display::groupButtonWithDropDown(get_lang('Action'), $items);
454
        } else {
455
            $html .= $form;
456
        }
457
458
        $html .= '</div>';
459
460
        // Pagination
461
        if ($this->get_total_number_of_items() > $this->default_items_per_page) {
462
            $html .= '<div class="flex justify-end mt-4 w-full">';
463
            $html .= '<div class="page-nav pb-2 pt-2">'.$nav.'</div>';
464
            $html .= '</div>';
465
        }
466
467
        $html .= '</div>'; // btn-group
468
        $html .= '</div>'; // sortable-buttons-actions
469
        if (count($this->form_actions) > 0) {
470
            $html .= '</form>';
471
        }
472
473
        return $html;
474
    }
475
476
    /**
477
     * This function shows the content of a table in a grid.
478
     * Should not be use to edit information (edit/delete rows) only.
479
     */
480
    public function display_grid()
481
    {
482
        $empty_table = false;
483
        if (0 == $this->get_total_number_of_items()) {
484
            $message_empty = api_xml_http_response_encode(get_lang('Empty'));
485
            $this->setCellContents(1, 0, $message_empty);
486
            $empty_table = true;
487
        }
488
        $html = '';
489
        if (!$empty_table) {
490
            $form = $this->get_page_select_form();
491
            $nav = $this->get_navigation_html();
492
493
            // @todo This style css must be moved to default.css only for dev
494
            echo '<style>
495
                    .main-grid { width:100%;}
496
                    .sub-header { width:100%; padding-top: 10px; padding-right: 10px; padding-left: 10px; height:30px;}
497
                    .grid_container { width:100%;}
498
                    .grid_item { height: 120px; width:98px;  float:left; padding:5px; margin:8px;}
499
                    .grid_element_0 { width:100px; height: 100px; float:left; text-align:center; margin-bottom:5px;}
500
                    .grid_element_1 { width:100px; float:left; text-align:center;margin-bottom:5px;}
501
                    .grid_element_2 { width:150px; float:left;}
502
                    .grid_selectbox { width:30%; float:left;}
503
                    .grid_title     { width:30%; float:left;}
504
                    .grid_nav         { }
505
            </style>';
506
507
            // @todo  This also must be moved
508
            // Show only navigations if there are more than 1 page
509
            $my_pager = $this->get_pager();
510
            $html .= '<div class="main-grid">';
511
            if ($my_pager->numPages() > 1) {
512
                $html .= '<div class="sub-header">';
513
                $html .= '<div class="grid_selectbox">'.$form.'</div>';
514
                $html .= '<div class="grid_title">'.$this->get_table_title().'</div>';
515
                $html .= '<div class="grid_nav">'.$nav.'</div>';
516
                $html .= '</div>';
517
            }
518
519
            $html .= '<div class="clear"></div>';
520
            if (count($this->form_actions) > 0) {
521
                $params = $this->get_sortable_table_param_string().'&amp;'.$this->get_additional_url_paramstring();
522
                $html .= '<form method="post" action="'.api_get_self().'?'.$params
523
                    .'" name="form_'.$this->table_name.'">';
524
            }
525
        }
526
        // Getting the items of the table
527
        $items = $this->get_clean_html(false); //no sort
528
529
        // Generation of style classes must be improved. Maybe we need a a table name to create style on the fly:
530
        // i.e: .whoisonline_table_grid_container instead of  .grid_container
531
        // where whoisonline is the table's name like drupal's template engine
532
        $html .= '<div class="grid_container">';
533
        if (is_array($items) && count($items) > 0) {
534
            foreach ($items as &$row) {
535
                $html .= '<div class="grid_item">';
536
                $i = 0;
537
                foreach ($row as &$element) {
538
                    $html .= '<div class="grid_element_'.$i.'">'.$element.'</div>';
539
                    $i++;
540
                }
541
                $html .= '</div>';
542
            }
543
        }
544
        $html .= '</div>'; //close grid_container
545
        $html .= '</div>'; //close main grid
546
        $html .= '<div class="clear"></div>';
547
548
        echo $html;
549
    }
550
551
    /**
552
     * This function returns the content of a table in a grid
553
     * Should not be use to edit information (edit/delete rows) only.
554
     *
555
     * @param array      options of visibility
556
     * @param bool       hide navigation optionally
557
     * @param int        content per page when show navigation (optional)
558
     * @param bool       sort data optionally
559
     *
560
     * @return string grid html
561
     */
562
    public function display_simple_grid(
563
        $visibility_options,
564
        $hide_navigation = true,
565
        $per_page = 20,
566
        $sort_data = true,
567
        $grid_class = []
568
    ) {
569
        $empty_table = false;
570
        if (0 == $this->get_total_number_of_items()) {
571
            $message_empty = api_xml_http_response_encode(get_lang('Empty'));
572
            $this->setCellContents(1, 0, $message_empty);
573
            $empty_table = true;
574
        }
575
        $html = '';
576
        if (!$empty_table) {
577
            // If we show the pagination
578
            if (!$hide_navigation) {
579
                $form = '&nbsp;';
580
581
                if ($this->get_total_number_of_items() > $per_page) {
582
                    if ($per_page > 10) {
583
                        $form = $this->get_page_select_form();
584
                    }
585
                    $nav = $this->get_navigation_html();
586
                    // This also must be moved
587
                    $html = '<div class="sub-header">';
588
                    $html .= '<div class="grid_selectbox">'.$form.'</div>';
589
                    $html .= '<div class="grid_title">'.$this->get_table_title().'</div>';
590
                    $html .= '<div class="grid_nav">'.$nav.'</div>';
591
                    $html .= '</div>';
592
                }
593
            }
594
595
            $html .= '<div class="clear"></div>';
596
            if (count($this->form_actions) > 0) {
597
                $params = $this->get_sortable_table_param_string().'&amp;'.$this->get_additional_url_paramstring();
598
                $html .= '<form method="post" action="'.api_get_self().'?'.$params
599
                    .'" name="form_'.$this->table_name.'">';
600
            }
601
        }
602
603
        if ($hide_navigation) {
604
            $items = $this->table_data; // This is a faster way to get what we want
605
        } else {
606
            // The normal way
607
            $items = $this->get_clean_html($sort_data); // Getting the items of the table
608
        }
609
610
        // Generation of style classes must be improved. Maybe we need a a table name to create style on the fly:
611
        // i.e: .whoisonline_table_grid_container instead of  .grid_container
612
        // where whoisonline is the table's name like drupal's template engine
613
614
        if (is_array($visibility_options)) {
615
            $filter = false; // The 2nd condition of the if will be loaded
616
        } else {
617
            $filter = false !== $visibility_options;
618
        }
619
620
        $item_css_class = $item_css_style = $grid_css_class = $grid_css_style = '';
621
        if (!empty($grid_class)) {
622
            $grid_css_class = $grid_class['main']['class'];
623
            $item_css_class = $grid_class['item']['class'];
624
625
            $grid_css_style = isset($grid_class['main']['style']) ? $grid_class['main']['style'] : null;
626
            $item_css_style = isset($grid_class['item']['style']) ? $grid_class['item']['style'] : null;
627
        }
628
629
        $div = '';
630
        if (is_array($items) && count($items) > 0) {
631
            foreach ($items as &$row) {
632
                $i = 0;
633
                $rows = '';
634
                foreach ($row as &$element) {
635
                    if ($filter || isset($visibility_options[$i]) && $visibility_options[$i]
636
                    ) {
637
                        $rows .= '<div class="'.$this->table_name.'_grid_element_'.$i.'">'.$element.'</div>';
638
                    }
639
                    $i++;
640
                }
641
                $div .= Display::div(
642
                    $rows,
643
                    [
644
                        'class' => $item_css_class.' '.$this->table_name.'_grid_item',
645
                        'style' => $item_css_style,
646
                    ]
647
                );
648
            }
649
        }
650
651
        $html .= Display::div(
652
            $div,
653
            [
654
                'class' => $grid_css_class.' '.$this->table_name.'_grid_container',
655
                'style' => $grid_css_style,
656
            ]
657
        );
658
        $html .= '<div class="clear"></div>';
659
660
        return $html;
661
    }
662
663
    /**
664
     * Get the HTML-code with the navigational buttons to browse through the
665
     * data-pages.
666
     */
667
    public function get_navigation_html()
668
    {
669
        $pager = $this->get_pager();
670
        $pager_links = $pager->getLinks();
671
        $nav = $pager_links['first'].' '.$pager_links['back'];
672
        $nav .= ' '.$pager->getCurrentPageId().' / '.$pager->numPages().' ';
673
        $nav .= $pager_links['next'].' '.$pager_links['last'];
674
675
        return $nav;
676
    }
677
678
    /**
679
     * Get the HTML-code with the data-table.
680
     */
681
    public function get_table_html()
682
    {
683
        $pager = $this->get_pager();
684
        $offset = $pager->getOffsetByPageId();
685
        $from = $offset[0] - 1;
686
        $table_data = $this->get_table_data($from, $this->per_page, $this->column);
687
        $this->processHeaders();
688
        if (is_array($table_data)) {
689
            $count = 1;
690
            foreach ($table_data as &$row) {
691
                $row = $this->filter_data($row);
692
                $newRow = [];
693
                if (!empty($this->columnsToHide)) {
694
                    $counter = 0;
695
                    foreach ($row as $index => $rowInfo) {
696
                        if (!isset($this->columnsToHide[$index])) {
697
                            $newRow[$counter] = $rowInfo;
698
                            $counter++;
699
                        }
700
                    }
701
                    $row = $newRow;
702
                }
703
                $this->addRow($row);
704
                if (isset($row['child_of'])) {
705
                    $this->setRowAttributes(
706
                        $count,
707
                        ['class' => 'hidden hidden_'.$row['child_of']],
708
                        true
709
                    );
710
                }
711
                $count++;
712
            }
713
        }
714
715
        if (true == $this->odd_even_rows_enabled) {
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...
716
            $this->altRowAttributes(
717
                0,
718
                ['class' => 'row_odd'],
719
                ['class' => 'row_even'],
720
                true
721
            );
722
        }
723
724
        foreach ($this->th_attributes as $column => $attributes) {
725
            $this->setCellAttributes(0, $column, $attributes);
726
        }
727
        foreach ($this->td_attributes as $column => $attributes) {
728
            $this->setColAttributes($column, $attributes);
729
        }
730
731
        return $this->toHTML();
732
    }
733
734
    /**
735
     * This function return the items of the table.
736
     *
737
     * @param bool true for sorting table data or false otherwise
738
     *
739
     * @return array table row items
740
     */
741
    public function get_clean_html($sort = true)
742
    {
743
        $pager = $this->get_pager();
744
        $offset = $pager->getOffsetByPageId();
745
        $from = $offset[0] - 1;
746
        $table_data = $this->get_table_data($from, null, null, null, $sort);
747
        $new_table_data = [];
748
        if (is_array($table_data)) {
749
            foreach ($table_data as $index => &$row) {
750
                $row = $this->filter_data($row);
751
                $new_table_data[] = $row;
752
            }
753
        }
754
755
        return $new_table_data;
756
    }
757
758
    /**
759
     * Get the HTML-code which represents a form to select how many items a page
760
     * should contain.
761
     */
762
    public function get_page_select_form(): string
763
    {
764
        $total_number_of_items = $this->get_total_number_of_items();
765
        if ($total_number_of_items <= $this->default_items_per_page) {
766
            return '';
767
        }
768
769
        if (true === $this->hideItemSelector) {
770
            return '';
771
        }
772
773
        $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...
774
        $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...
775
        $param[$this->param_prefix.'page_nr'] = $this->page_nr;
776
        $param[$this->param_prefix.'column'] = $this->column;
777
778
        if (is_array($this->additional_parameters)) {
779
            $param = array_merge($param, $this->additional_parameters);
780
        }
781
782
        foreach ($param as $key => $value) {
783
            $result[] = '<input type="hidden" name="'.$key.'" value="'.$value.'"/>';
784
        }
785
        $result[] = '<select style="width: auto;" class="form-control" name="'.$this->param_prefix
786
            .'per_page" onchange="javascript: this.form.submit();">';
787
        $list = [10, 20, 50, 100, 500, 1000];
788
789
        $rowList = api_get_setting('platform.table_row_list', true);
790
        if (is_array($rowList) && isset($rowList['options'])) {
791
            $list = $rowList['options'];
792
        }
793
794
        foreach ($list as $nr) {
795
            if ($total_number_of_items <= $nr) {
796
                break;
797
            }
798
            $result[] = '<option value="'.$nr.'" '.($nr == $this->per_page ? 'selected="selected"' : '').'>'.$nr
799
                .'</option>';
800
        }
801
802
        $result[] = '<option value="'.$total_number_of_items.'" '
803
            .($total_number_of_items == $this->per_page ? 'selected="selected"' : '')
804
            .'>'.api_ucfirst(get_lang('All')).'</option>';
805
806
        $result[] = '</select>';
807
        $result[] = '<noscript>';
808
        $result[] = '<button class="btn btn--success" type="submit">'.get_lang('Save').'</button>';
809
        $result[] = '</noscript>';
810
        $result[] = '</form>';
811
812
        return implode("\n", $result);
813
    }
814
815
    /**
816
     * Get the table title.
817
     */
818
    public function get_table_title()
819
    {
820
        $pager = $this->get_pager();
821
        $showed_items = $pager->getOffsetByPageId();
822
823
        return $showed_items[0].' - '.$showed_items[1].' / '.$this->get_total_number_of_items();
824
    }
825
826
    /**
827
     * @return array
828
     */
829
    public function getHeaders()
830
    {
831
        return $this->headers;
832
    }
833
834
    /**
835
     * Process headers.
836
     */
837
    public function processHeaders()
838
    {
839
        $counter = 0;
840
        foreach ($this->headers as $column => $columnInfo) {
841
            $label = $columnInfo['label'];
842
            $sortable = $columnInfo['sortable'];
843
            $th_attributes = $columnInfo['th_attributes'];
844
            $td_attributes = $columnInfo['td_attributes'];
845
846
            if (!empty($this->columnsToHide)) {
847
                if (isset($this->columnsToHide[$column])) {
848
                    continue;
849
                }
850
            }
851
852
            $column = $counter;
853
            $param['direction'] = 'ASC';
854
            if ($this->column == $column && 'ASC' == $this->direction) {
855
                $param['direction'] = 'DESC';
856
            }
857
858
            $param['page_nr'] = $this->page_nr;
859
            $param['per_page'] = $this->per_page;
860
            $param['column'] = $column;
861
            $link = $label;
862
            if ($sortable) {
863
                $link = '<a href="'.api_get_self().'?'.api_get_cidreq().'&amp;';
864
                foreach ($param as $key => &$value) {
865
                    $link .= $this->param_prefix.$key.'='.urlencode($value).'&amp;';
866
                }
867
                $link .= $this->get_additional_url_paramstring();
868
                $link .= '">'.$label.'</a>';
869
                if ($this->column == $column) {
870
                    $link .= 'ASC' == $this->direction ? ' &#8595;' : ' &#8593;';
871
                }
872
            }
873
            $this->setHeaderContents(0, $column, $link);
874
875
            if (!is_null($td_attributes)) {
876
                $this->td_attributes[$column] = $td_attributes;
877
            }
878
            if (!is_null($th_attributes)) {
879
                $this->th_attributes[$column] = $th_attributes;
880
            }
881
882
            $counter++;
883
        }
884
    }
885
886
    /**
887
     * Set the header-label.
888
     *
889
     * @param int    $column        The column number
890
     * @param string $label         The label
891
     * @param bool   $sortable      Is the table sortable by this column? (defatult
892
     *                              = true)
893
     * @param array  $th_attributes Additional attributes for the th-tag of the
894
     *                              table header
895
     * @param string $td_attributes Additional attributes for the td-tags of the
896
     *                              column
897
     */
898
    public function set_header(
899
        $column,
900
        $label,
901
        $sortable = true,
902
        $th_attributes = ['class' => 'th-header'],
903
        $td_attributes = null
904
    ) {
905
        $this->headers[$column] = [
906
            'label' => $label,
907
            'sortable' => $sortable,
908
            'th_attributes' => $th_attributes,
909
            'td_attributes' => $td_attributes,
910
        ];
911
    }
912
913
    /**
914
     * Get the parameter-string with additional parameters to use in the URLs
915
     * generated by this SortableTable.
916
     */
917
    public function get_additional_url_paramstring()
918
    {
919
        $param_string_parts = [];
920
        if (is_array($this->additional_parameters) && count($this->additional_parameters) > 0) {
921
            foreach ($this->additional_parameters as $key => $value) {
922
                $param_string_parts[] = urlencode($key).'='.urlencode($value);
923
            }
924
        }
925
        $result = implode('&amp;', $param_string_parts);
926
        foreach ($this->other_tables as $index => &$tablename) {
927
            $param = [];
928
            if (isset($_GET[$tablename.'_direction'])) {
929
                $my_get_direction = $_GET[$tablename.'_direction'];
930
                if (!in_array($my_get_direction, ['ASC', 'DESC'])) {
931
                    $param[$tablename.'_direction'] = 'ASC';
932
                } else {
933
                    $param[$tablename.'_direction'] = $my_get_direction;
934
                }
935
            }
936
            if (isset($_GET[$tablename.'_page_nr'])) {
937
                $param[$tablename.'_page_nr'] = intval($_GET[$tablename.'_page_nr']);
938
            }
939
            if (isset($_GET[$tablename.'_per_page'])) {
940
                $param[$tablename.'_per_page'] = intval($_GET[$tablename.'_per_page']);
941
            }
942
            if (isset($_GET[$tablename.'_column'])) {
943
                $param[$tablename.'_column'] = intval($_GET[$tablename.'_column']);
944
            }
945
            $param_string_parts = [];
946
            foreach ($param as $key => &$value) {
947
                $param_string_parts[] = urlencode($key).'='.urlencode($value);
948
            }
949
            if (count($param_string_parts) > 0) {
950
                $result .= '&amp;'.implode('&amp;', $param_string_parts);
951
            }
952
        }
953
954
        return $result;
955
    }
956
957
    /**
958
     * Get the parameter-string with the SortableTable-related parameters to use
959
     * in URLs.
960
     */
961
    public function get_sortable_table_param_string()
962
    {
963
        $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...
964
        $param[$this->param_prefix.'page_nr'] = $this->page_nr;
965
        $param[$this->param_prefix.'per_page'] = $this->per_page;
966
        $param[$this->param_prefix.'column'] = $this->column;
967
        $param_string_parts = [];
968
        foreach ($param as $key => &$value) {
969
            $param_string_parts[] = urlencode($key).'='.urlencode($value);
970
        }
971
        $res = implode('&amp;', $param_string_parts);
972
973
        return $res;
974
    }
975
976
    /**
977
     * Add a filter to a column. If another filter was already defined for the
978
     * given column, it will be overwritten.
979
     *
980
     * @param int    $column   The number of the column
981
     * @param string $function The name of the filter-function. This should be a
982
     *                         function wich requires 1 parameter and returns the filtered value.
983
     */
984
    public function set_column_filter($column, $function)
985
    {
986
        $this->column_filters[$column] = $function;
987
    }
988
989
    /**
990
     * List of columns to hide.
991
     *
992
     * @param int $column
993
     */
994
    public function setHideColumn($column)
995
    {
996
        $this->columnsToHide[$column] = $column;
997
    }
998
999
    /**
1000
     * Define a list of actions which can be performed on the table-date.
1001
     * If you define a list of actions, the first column of the table will be
1002
     * converted into checkboxes.
1003
     *
1004
     * @param array  $actions       A list of actions. The key is the name of the
1005
     *                              action. The value is the label to show in the select-box
1006
     * @param string $checkbox_name The name of the generated checkboxes. The
1007
     *                              value of the checkbox will be the value of the first column.
1008
     */
1009
    public function set_form_actions($actions, $checkbox_name = 'id')
1010
    {
1011
        $this->form_actions = $actions;
1012
        $this->checkbox_name = $checkbox_name;
1013
    }
1014
1015
    /**
1016
     * Define a list of additional parameters to use in the generated URLs
1017
     * <code>$parameters['action'] = 'test'; will be convert in
1018
     * <input type="hidden" name="action" value="test"></code>.
1019
     *
1020
     * @param array $parameters
1021
     */
1022
    public function set_additional_parameters(array $parameters)
1023
    {
1024
        $this->additional_parameters = $parameters;
1025
    }
1026
1027
    /**
1028
     * Set other tables on the same page.
1029
     * If you have other sortable tables on the page displaying this sortable
1030
     * tables, you can define those other tables with this function. If you
1031
     * don't define the other tables, there sorting and pagination will return
1032
     * to their default state when sorting this table.
1033
     *
1034
     * @param array $tablenames an array of table names
1035
     */
1036
    public function set_other_tables($tablenames)
1037
    {
1038
        $this->other_tables = $tablenames;
1039
    }
1040
1041
    /**
1042
     * Transform all data in a table-row, using the filters defined by the
1043
     * function set_column_filter(...) defined elsewhere in this class.
1044
     * If you've defined actions, the first element of the given row will be
1045
     * converted into a checkbox.
1046
     *
1047
     * @param array $row a row from the table
1048
     *
1049
     * @return array
1050
     */
1051
    public function filter_data($row)
1052
    {
1053
        $url_params = $this->get_sortable_table_param_string().'&'.$this->get_additional_url_paramstring();
1054
        foreach ($this->column_filters as $column => &$function) {
1055
            $firstParam = isset($row[$column]) ? $row[$column] : 0;
1056
            $row[$column] = call_user_func($function, $firstParam, $url_params, $row);
1057
        }
1058
        if (count($this->form_actions) > 0) {
1059
            if (strlen($row[0]) > 0) {
1060
                $row[0] = '<div class="checkbox" ><label><input type="checkbox" name="'.$this->checkbox_name.'[]" value="'.$row[0].'"';
1061
                if (isset($_GET[$this->param_prefix.'selectall'])) {
1062
                    $row[0] .= ' checked="checked"';
1063
                }
1064
                $row[0] .= '/><span class="checkbox-material"><span class="check"></span></span></label></div>';
1065
            }
1066
        }
1067
        if (is_array($row)) {
1068
            foreach ($row as &$value) {
1069
                if (empty($value)) {
1070
                    $value = '-';
1071
                }
1072
            }
1073
        }
1074
1075
        return $row;
1076
    }
1077
1078
    /**
1079
     * Get the total number of items. This function calls the function given as
1080
     * 2nd argument in the constructor of a SortableTable. Make sure your
1081
     * function has the same parameters as defined here.
1082
     */
1083
    public function get_total_number_of_items()
1084
    {
1085
        if (-1 == $this->total_number_of_items && !is_null($this->get_total_number_function)) {
1086
            $this->total_number_of_items = call_user_func(
1087
                $this->get_total_number_function,
1088
                $this->getDataFunctionParams()
1089
            );
1090
        }
1091
1092
        return $this->total_number_of_items;
1093
    }
1094
1095
    /**
1096
     * @param int $value
1097
     */
1098
    public function setTotalNumberOfItems($value)
1099
    {
1100
        $this->total_number_of_items = (int) $value;
1101
    }
1102
1103
    /**
1104
     * Get the data to display.  This function calls the function given as
1105
     * 2nd argument in the constructor of a SortableTable. Make sure your
1106
     * function has the same parameters as defined here.
1107
     *
1108
     * @param int    $from      index of the first item to return
1109
     * @param int    $perPage   The number of items to return
1110
     * @param int    $column    The number of the column on which the data should be
1111
     * @param bool   $sort      Whether to sort or not
1112
     *                          sorted
1113
     * @param string $direction In which order should the data be sorted (ASC
1114
     *                          or DESC)
1115
     *
1116
     * @return array
1117
     */
1118
    public function get_table_data(
1119
        $from = null,
1120
        $perPage = null,
1121
        $column = null,
1122
        $direction = null,
1123
        $sort = null
1124
    ) {
1125
        $data = [];
1126
        if (null !== $this->get_data_function) {
1127
            $data = call_user_func(
1128
                $this->get_data_function,
1129
                $from,
1130
                $this->per_page,
1131
                $this->column,
1132
                $this->direction,
1133
                $this->dataFunctionParams
1134
            );
1135
        }
1136
1137
        return $data;
1138
    }
1139
1140
    /**
1141
     * @param array $data
1142
     */
1143
    public function setTableData($data)
1144
    {
1145
        $this->table_data = $data;
1146
    }
1147
}
1148