Passed
Push — 1.11.x ( bce6cd...c146d9 )
by Angel Fernando Quiroz
12:25
created

main/inc/lib/sortable_table.class.php (1 issue)

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