Completed
Push — master ( c9546d...95f607 )
by Julito
09:41
created

public/main/inc/lib/sortable_table.class.php (4 issues)

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