SortableTable::get_additional_url_paramstring()   C
last analyzed

Complexity

Conditions 12
Paths 194

Size

Total Lines 38
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 25
nc 194
nop 0
dl 0
loc 38
rs 6.1833
c 0
b 0
f 0

How to fix   Complexity   

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:

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