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

SortableTable::__construct()   F

Complexity

Conditions 21
Paths > 20000

Size

Total Lines 116
Code Lines 74

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 21
eloc 74
c 0
b 0
f 0
nc 25600
nop 8
dl 0
loc 116
rs 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use ChamiloSession as Session;
6
7
/**
8
 * This class allows you to display a sortable data-table. It is possible to
9
 * split the data in several pages.
10
 * Using this class you can:
11
 * - automatically create checkboxes of the first table column
12
 *     - a "select all" and "deselect all" link is added
13
 *     - only if you provide a list of actions for the selected items
14
 * - click on the table header to sort the data
15
 * - choose how many items you see per page
16
 * - navigate through all data-pages.
17
 */
18
class SortableTable extends HTML_Table
19
{
20
    /**
21
     * A name for this table.
22
     */
23
    public $table_name;
24
    /**
25
     * The page to display.
26
     */
27
    public $page_nr;
28
    /**
29
     * The column to sort the data.
30
     */
31
    public $column;
32
    /**
33
     * The sorting direction (ASC or DESC).
34
     */
35
    public $direction;
36
    /**
37
     * Number of items to display per page.
38
     */
39
    public $per_page;
40
    /**
41
     * The default number of items to display per page.
42
     */
43
    public $default_items_per_page;
44
    /**
45
     * A prefix for the URL-parameters, can be used on pages with multiple
46
     * SortableTables.
47
     */
48
    public $param_prefix;
49
    /**
50
     * The pager object to split the data in several pages.
51
     */
52
    public $pager;
53
    /**
54
     * The total number of items in the table.
55
     */
56
    public $total_number_of_items;
57
    /**
58
     * The function to get the total number of items.
59
     */
60
    public $get_total_number_function;
61
    /**
62
     * The function to the the data to display.
63
     */
64
    public $get_data_function;
65
    /**
66
     * An array with defined column-filters.
67
     */
68
    public $column_filters;
69
    /**
70
     * A list of actions which will be available through a select list.
71
     */
72
    public $form_actions;
73
    /**
74
     * Additional parameters to pass in the URL.
75
     */
76
    public $additional_parameters;
77
    /**
78
     * Additional attributes for the th-tags.
79
     */
80
    public $th_attributes;
81
    /**
82
     * Additional attributes for the td-tags.
83
     */
84
    public $td_attributes;
85
    /**
86
     * Array with names of the other tables defined on the same page of this
87
     * table.
88
     */
89
    public $other_tables;
90
    /**
91
     * Activates the odd even rows.
92
     * */
93
    public $odd_even_rows_enabled = true;
94
    public $use_jqgrid = false;
95
    public $table_id = null;
96
    public $headers = [];
97
    public $actionButtons = [];
98
    /**
99
     * The array containing all data for this table.
100
     */
101
    public $table_data;
102
    public $hideItemSelector;
103
    // Hide table navigation, better to be use when exporting table to PDF.
104
    public $hideNavigation = false;
105
106
    /**
107
     * @var array Columns to hide
108
     */
109
    private $columnsToHide = [];
110
    private $dataFunctionParams;
111
    private $defaultColumn;
112
    private $defaultItemsPerPage;
113
114
    /**
115
     * Create a new SortableTable.
116
     *
117
     * @param string $table_name                A name for the table (default = 'table')
118
     * @param string $get_total_number_function A user defined function to get
119
     *                                          the total number of items in the table
120
     * @param string $get_data_function         A function to get the data to display on
121
     *                                          the current page
122
     * @param int    $default_column            The default column on which the data should be
123
     *                                          sorted
124
     * @param int    $default_items_per_page    The default number of items to show
125
     *                                          on one page
126
     * @param string $default_order_direction   The default order direction;
127
     *                                          either the constant 'ASC' or 'DESC'
128
     * @param string $table_id
129
     * @param array  $attributes                They are custom attributes of the table
130
     */
131
    public function __construct(
132
        $table_name = 'table',
133
        $get_total_number_function = null,
134
        $get_data_function = null,
135
        $default_column = 1,
136
        $default_items_per_page = 20,
137
        $default_order_direction = 'ASC',
138
        $table_id = null,
139
        $attributes = []
140
    ) {
141
        if (empty($table_id)) {
142
            $table_id = $table_name.uniqid('table', true);
143
        }
144
145
        if (empty($attributes)) {
146
            $attributes = [];
147
            $attributes['class'] = 'table table-hover table-striped table-bordered data_table';
148
            $attributes['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