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

main/gradebook/lib/fe/flatviewtable.class.php (2 issues)

1
<?php
2
/* For licensing terms, see /license.txt */
3
4
set_time_limit(0);
5
6
use CpChart\Cache as pCache;
7
use CpChart\Data as pData;
8
use CpChart\Image as pImage;
9
10
/**
11
 * Class FlatViewTable
12
 * Table to display flat view (all evaluations and links for all students).
13
 *
14
 * @author Stijn Konings
15
 * @author Bert Steppé  - (refactored, optimised)
16
 * @author Julio Montoya Armas - Gradebook Graphics
17
 */
18
class FlatViewTable extends SortableTable
19
{
20
    public $datagen;
21
    private $selectcat;
22
    private $limit_enabled;
23
    private $offset;
24
    private $mainCourseCategory;
25
26
    /**
27
     * @param Category $selectcat
28
     * @param array    $users
29
     * @param array    $evals
30
     * @param array    $links
31
     * @param bool     $limit_enabled
32
     * @param int      $offset
33
     * @param null     $addparams
34
     * @param Category $mainCourseCategory
35
     */
36
    public function __construct(
37
        $selectcat,
38
        $users = [],
39
        $evals = [],
40
        $links = [],
41
        $limit_enabled = false,
42
        $offset = 0,
43
        $addparams = null,
44
        $mainCourseCategory = null
45
    ) {
46
        parent:: __construct(
47
            'flatviewlist',
48
            null,
49
            null,
50
            api_is_western_name_order() ? 1 : 0
51
        );
52
53
        $this->selectcat = $selectcat;
54
        $this->datagen = new FlatViewDataGenerator(
55
            $users,
56
            $evals,
57
            $links,
58
            ['only_subcat' => $this->selectcat->get_id()],
59
            $mainCourseCategory
60
        );
61
62
        $this->limit_enabled = $limit_enabled;
63
        $this->offset = $offset;
64
        if (isset($addparams)) {
65
            $this->set_additional_parameters($addparams);
66
        }
67
68
        // step 2: generate rows: students
69
        $this->datagen->category = $this->selectcat;
70
        $this->mainCourseCategory = $mainCourseCategory;
71
    }
72
73
    /**
74
     * @param bool $value
75
     */
76
    public function setLimitEnabled($value)
77
    {
78
        $this->limit_enabled = (bool) $value;
79
    }
80
81
    /**
82
     * @return Category
83
     */
84
    public function getMainCourseCategory()
85
    {
86
        return $this->mainCourseCategory;
87
    }
88
89
    /**
90
     * Display gradebook graphs.
91
     */
92
    public function display_graph_by_resource()
93
    {
94
        $headerName = $this->datagen->get_header_names();
95
        $total_users = $this->datagen->get_total_users_count();
96
        $customdisplays = ScoreDisplay::instance()->get_custom_score_display_settings();
97
98
        if (empty($customdisplays)) {
99
            echo get_lang('To view graph score rule must be enabled');
100
101
            return '';
102
        }
103
104
        $user_results = $this->datagen->get_data_to_graph2(false);
105
106
        if (empty($user_results) || empty($total_users)) {
107
            echo get_lang('No results found');
108
109
            return '';
110
        }
111
112
        // Removing first name
113
        array_shift($headerName);
114
        // Removing last name
115
        array_shift($headerName);
116
        // Removing username
117
        array_shift($headerName);
118
119
        $pre_result = [];
120
        foreach ($user_results as $result) {
121
            for ($i = 0; $i < count($headerName); $i++) {
122
                if (isset($result[$i + 1])) {
123
                    $pre_result[$i + 3][] = $result[$i + 1];
124
                }
125
            }
126
        }
127
128
        $i = 0;
129
        $resource_list = [];
130
        $pre_result2 = [];
131
        foreach ($pre_result as $key => $res_array) {
132
            rsort($res_array);
133
            $pre_result2[] = $res_array;
134
        }
135
136
        //@todo when a display custom does not exist the order of the color does not match
137
        //filling all the answer that are not responded with 0
138
        rsort($customdisplays);
139
140
        if ($total_users > 0) {
141
            foreach ($pre_result2 as $key => $res_array) {
142
                $key_list = [];
143
                foreach ($res_array as $user_result) {
144
                    $userResult = isset($user_result[1]) ? $user_result[1] : null;
145
                    if (!isset($resource_list[$key][$userResult])) {
146
                        $resource_list[$key][$userResult] = 0;
147
                    }
148
                    $resource_list[$key][$userResult]++;
149
                    $key_list[] = $userResult;
150
                }
151
152
                foreach ($customdisplays as $display) {
153
                    if (!in_array($display['display'], $key_list)) {
154
                        $resource_list[$key][$display['display']] = 0;
155
                    }
156
                }
157
                $i++;
158
            }
159
        }
160
161
        //fixing $resource_list
162
        $max = 0;
163
        $new_list = [];
164
        foreach ($resource_list as $key => $value) {
165
            $new_value = [];
166
            foreach ($customdisplays as $item) {
167
                if ($value[$item['display']] > $max) {
168
                    $max = $value[$item['display']];
169
                }
170
                $new_value[$item['display']] = strip_tags($value[$item['display']]);
171
            }
172
            $new_list[] = $new_value;
173
        }
174
        $resource_list = $new_list;
175
        $i = 1;
176
        // Cache definition
177
        $cachePath = api_get_path(SYS_ARCHIVE_PATH);
178
        foreach ($resource_list as $key => $resource) {
179
            // Reverse array, otherwise we get highest values first
180
            $resource = array_reverse($resource, true);
181
182
            $dataSet = new pData();
183
            $dataSet->addPoints($resource, 'Serie');
184
            $dataSet->addPoints(array_keys($resource), 'Labels');
185
            $header = $headerName[$i - 1];
186
            if (is_array($header) && isset($header['header'])) {
187
                $header = $header['header'];
188
            }
189
            $header = strip_tags(api_html_entity_decode($header));
190
            $dataSet->setSerieDescription('Labels', $header);
191
            $dataSet->setAbscissa('Labels');
192
            $dataSet->setAbscissaName(get_lang('Skills ranking'));
193
            $dataSet->setAxisName(0, get_lang('Learners'));
194
            $palette = [
195
                '0' => ['R' => 186, 'G' => 206, 'B' => 151, 'Alpha' => 100],
196
                '1' => ['R' => 210, 'G' => 148, 'B' => 147, 'Alpha' => 100],
197
                '2' => ['R' => 148, 'G' => 170, 'B' => 208, 'Alpha' => 100],
198
                '3' => ['R' => 221, 'G' => 133, 'B' => 61, 'Alpha' => 100],
199
                '4' => ['R' => 65, 'G' => 153, 'B' => 176, 'Alpha' => 100],
200
                '5' => ['R' => 114, 'G' => 88, 'B' => 144, 'Alpha' => 100],
201
                '6' => ['R' => 138, 'G' => 166, 'B' => 78, 'Alpha' => 100],
202
                '7' => ['R' => 171, 'G' => 70, 'B' => 67, 'Alpha' => 100],
203
                '8' => ['R' => 69, 'G' => 115, 'B' => 168, 'Alpha' => 100],
204
            ];
205
            $myCache = new pCache(['CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)]);
206
            $chartHash = $myCache->getHash($dataSet);
207
            if ($myCache->isInCache($chartHash)) {
208
                $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
209
                $myCache->saveFromCache($chartHash, $imgPath);
210
                $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
211
            } else {
212
                /* Create the pChart object */
213
                $widthSize = 480;
214
                $heightSize = 250;
215
                $myPicture = new pImage($widthSize, $heightSize, $dataSet);
216
217
                /* Turn of Antialiasing */
218
                $myPicture->Antialias = false;
219
220
                /* Add a border to the picture */
221
                $myPicture->drawRectangle(
222
                    0,
223
                    0,
224
                    $widthSize - 1,
225
                    $heightSize - 1,
226
                    [
227
                        'R' => 0,
228
                        'G' => 0,
229
                        'B' => 0,
230
                    ]
231
                );
232
233
                /* Set the default font */
234
                $myPicture->setFontProperties(
235
                    [
236
                        'FontName' => api_get_path(SYS_FONTS_PATH).'opensans/OpenSans-Regular.ttf',
237
                        'FontSize' => 10,
238
                    ]
239
                );
240
                /* Write the chart title */
241
                $myPicture->drawText(
242
                    250,
243
                    30,
244
                    $header,
245
                    [
246
                        'FontSize' => 12,
247
                        'Align' => TEXT_ALIGN_BOTTOMMIDDLE,
248
                    ]
249
                );
250
251
                /* Define the chart area */
252
                $myPicture->setGraphArea(50, 40, $widthSize - 20, $heightSize - 50);
253
254
                /* Draw the scale */
255
                $scaleSettings = [
256
                    'GridR' => 200,
257
                    'GridG' => 200,
258
                    'GridB' => 200,
259
                    'DrawSubTicks' => true,
260
                    'CycleBackground' => true,
261
                    'Mode' => SCALE_MODE_START0,
262
                ];
263
                $myPicture->drawScale($scaleSettings);
264
265
                /* Turn on shadow computing */
266
                $myPicture->setShadow(
267
                    true,
268
                    [
269
                        'X' => 1,
270
                        'Y' => 1,
271
                        'R' => 0,
272
                        'G' => 0,
273
                        'B' => 0,
274
                        'Alpha' => 10,
275
                    ]
276
                );
277
278
                /* Draw the chart */
279
                $myPicture->setShadow(
280
                    true,
281
                    [
282
                        'X' => 1,
283
                        'Y' => 1,
284
                        'R' => 0,
285
                        'G' => 0,
286
                        'B' => 0,
287
                        'Alpha' => 10,
288
                    ]
289
                );
290
                $settings = [
291
                    'OverrideColors' => $palette,
292
                    'Gradient' => false,
293
                    'GradientMode' => GRADIENT_SIMPLE,
294
                    'DisplayPos' => LABEL_POS_TOP,
295
                    'DisplayValues' => true,
296
                    'DisplayR' => 0,
297
                    'DisplayG' => 0,
298
                    'DisplayB' => 0,
299
                    'DisplayShadow' => true,
300
                    'Surrounding' => 10,
301
                ];
302
                $myPicture->drawBarChart($settings);
303
                /* Render the picture (choose the best way) */
304
305
                $myCache->writeToCache($chartHash, $myPicture);
306
                $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
307
                $myCache->saveFromCache($chartHash, $imgPath);
308
                $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
309
            }
310
            echo '<img src="'.$imgPath.'" >';
311
            if (0 == $i % 2 && 0 != $i) {
312
                echo '<br /><br />';
313
            } else {
314
                echo '&nbsp;&nbsp;&nbsp;';
315
            }
316
            $i++;
317
        }
318
    }
319
320
    /**
321
     * Function used by SortableTable to get total number of items in the table.
322
     */
323
    public function get_total_number_of_items()
324
    {
325
        return $this->datagen->get_total_users_count();
326
    }
327
328
    /**
329
     * Function used by SortableTable to generate the data to display.
330
     */
331
    public function get_table_data(
332
        $from = 1,
333
        $per_page = null,
334
        $column = null,
335
        $direction = null,
336
        $sort = null
337
    ) {
338
        $is_western_name_order = api_is_western_name_order();
339
340
        // create page navigation if needed
341
        $totalitems = $this->datagen->get_total_items_count();
342
343
        if ($this->limit_enabled && $totalitems > GRADEBOOK_ITEM_LIMIT) {
344
            $selectlimit = GRADEBOOK_ITEM_LIMIT;
345
        } else {
346
            $selectlimit = $totalitems;
347
        }
348
349
        $header = null;
350
        if ($this->limit_enabled && $totalitems > GRADEBOOK_ITEM_LIMIT) {
351
            $header .= '<table style="width: 100%; text-align: right; margin-left: auto; margin-right: auto;" border="0" cellpadding="2">'
352
                .'<tbody>'
353
                .'<tr>';
354
355
            // previous X
356
            $header .= '<td style="width:100%;">';
357
            if ($this->offset >= GRADEBOOK_ITEM_LIMIT) {
358
                $header .= '<a href="'.api_get_self()
359
                    .'?selectcat='.Security::remove_XSS($_GET['selectcat'])
360
                    .'&offset='.(($this->offset) - GRADEBOOK_ITEM_LIMIT)
361
                    .(isset($_GET['search']) ? '&search='.Security::remove_XSS($_GET['search']) : '').'">'
362
                    .Display::return_icon(
363
                        'action_prev.png',
364
                        get_lang('Previous page'),
365
                        [],
366
                        ICON_SIZE_MEDIUM
367
                    )
368
                    .'</a>';
369
            } else {
370
                $header .= Display::return_icon(
371
                    'action_prev_na.png',
372
                    get_lang('Previous page'),
373
                    [],
374
                    ICON_SIZE_MEDIUM
375
                );
376
            }
377
            $header .= ' ';
378
            // next X
379
            $calcnext = (($this->offset + (2 * GRADEBOOK_ITEM_LIMIT)) > $totalitems) ?
380
                ($totalitems - (GRADEBOOK_ITEM_LIMIT + $this->offset)) : GRADEBOOK_ITEM_LIMIT;
381
382
            if ($calcnext > 0) {
383
                $header .= '<a href="'.api_get_self()
384
                    .'?selectcat='.Security::remove_XSS($_GET['selectcat'])
385
                    .'&offset='.($this->offset + GRADEBOOK_ITEM_LIMIT)
386
                    .(isset($_GET['search']) ? '&search='.Security::remove_XSS($_GET['search']) : '').'">'
387
                    .Display::return_icon('action_next.png', get_lang('Next page'), [], ICON_SIZE_MEDIUM)
388
                    .'</a>';
389
            } else {
390
                $header .= Display::return_icon(
391
                    'action_next_na.png',
392
                    get_lang('Next page'),
393
                    [],
394
                    ICON_SIZE_MEDIUM
395
                );
396
            }
397
            $header .= '</td>';
398
            $header .= '</tbody></table>';
399
            echo $header;
400
        }
401
402
        // retrieve sorting type
403
        if ($is_western_name_order) {
404
            $users_sorting = (0 == $this->column ? FlatViewDataGenerator::FVDG_SORT_FIRSTNAME : FlatViewDataGenerator::FVDG_SORT_LASTNAME);
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $this->column of type integer|mixed|null to 0; this is ambiguous as not only 0 == 0 is true, but null == 0 is true, too. Consider using a strict comparison ===.
Loading history...
405
        } else {
406
            $users_sorting = (0 == $this->column ? FlatViewDataGenerator::FVDG_SORT_LASTNAME : FlatViewDataGenerator::FVDG_SORT_FIRSTNAME);
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $this->column of type integer|mixed|null to 0; this is ambiguous as not only 0 == 0 is true, but null == 0 is true, too. Consider using a strict comparison ===.
Loading history...
407
        }
408
409
        if ('DESC' === $this->direction) {
410
            $users_sorting |= FlatViewDataGenerator::FVDG_SORT_DESC;
411
        } else {
412
            $users_sorting |= FlatViewDataGenerator::FVDG_SORT_ASC;
413
        }
414
415
        // step 1: generate columns: evaluations and links
416
        $header_names = $this->datagen->get_header_names($this->offset, $selectlimit);
417
        $userRowSpan = false;
418
        foreach ($header_names as $item) {
419
            if (is_array($item)) {
420
                $userRowSpan = true;
421
                break;
422
            }
423
        }
424
425
        $thAttributes = '';
426
        if ($userRowSpan) {
427
            $thAttributes = 'rowspan=2';
428
        }
429
430
        $this->set_header(0, $header_names[0], true, $thAttributes);
431
        $this->set_header(1, $header_names[1], true, $thAttributes);
432
433
        $column = 2;
434
        $firstHeader = [];
435
        while ($column < count($header_names)) {
436
            $headerData = $header_names[$column];
437
438
            if (is_array($headerData)) {
439
                $countItems = count($headerData['items']);
440
441
                $this->set_header(
442
                    $column,
443
                    $headerData['header'],
444
                    false,
445
                    'colspan="'.$countItems.'"'
446
                );
447
448
                foreach ($headerData['items'] as $item) {
449
                    $firstHeader[] = '<span class="text-center">'.$item.'</span>';
450
                }
451
            } else {
452
                $this->set_header($column, $headerData, false, $thAttributes);
453
            }
454
            $column++;
455
        }
456
457
        $data_array = $this->datagen->get_data(
458
            $users_sorting,
459
            $from,
460
            $this->per_page,
461
            $this->offset,
462
            $selectlimit
463
        );
464
465
        $table_data = [];
466
467
        if (!empty($firstHeader)) {
468
            $table_data[] = $firstHeader;
469
        }
470
471
        $columnOffset = empty($this->datagen->params['show_official_code']) ? 0 : 1;
472
473
        foreach ($data_array as $user_row) {
474
            $user_id = $user_row[0];
475
            unset($user_row[0]);
476
            $userInfo = api_get_user_info($user_id);
477
            if ($is_western_name_order) {
478
                $user_row[1 + $columnOffset] = $this->build_name_link(
479
                    $user_id,
480
                    $userInfo['firstname']
481
                );
482
                $user_row[2 + $columnOffset] = $this->build_name_link(
483
                    $user_id,
484
                    $userInfo['lastname']
485
                );
486
            } else {
487
                $user_row[1 + $columnOffset] = $this->build_name_link(
488
                    $user_id,
489
                    $userInfo['lastname']
490
                );
491
                $user_row[2 + $columnOffset] = $this->build_name_link(
492
                    $user_id,
493
                    $userInfo['firstname']
494
                );
495
            }
496
            $user_row = array_values($user_row);
497
498
            $table_data[] = $user_row;
499
        }
500
501
        return $table_data;
502
    }
503
504
    /**
505
     * @param $userId
506
     * @param $name
507
     *
508
     * @return string
509
     */
510
    private function build_name_link($userId, $name)
511
    {
512
        return '<a href="user_stats.php?userid='.$userId.'&selectcat='.$this->selectcat->get_id().'&'.api_get_cidreq().'">'.$name.'</a>';
513
    }
514
}
515