Passed
Push — 1.11.x ( bf1941...7f58bf )
by Angel Fernando Quiroz
11:07 queued 27s
created

SortableTable::set_other_tables()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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