Passed
Pull Request — 1.11.x (#4635)
by Angel Fernando Quiroz
08:17
created

TrackingCourseLog::countStudentInCourse()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
c 0
b 0
f 0
dl 0
loc 5
rs 10
cc 1
nc 1
nop 0
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Component\Utils\ChamiloApi;
6
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
7
use ChamiloSession as Session;
8
9
class TrackingCourseLog
10
{
11
    public const HIDE_COURSE_REPORT_GRAPH_SHOWN = 0;
12
    public const HIDE_COURSE_REPORT_GRAPH_HIDDEN = 1;
13
    public const HIDE_COURSE_REPORT_GRAPH_CLICK_SHOW = 2;
14
15
    /**
16
     * @return mixed
17
     */
18
    public static function countItemResources()
19
    {
20
        $sessionId = api_get_session_id();
21
        $courseId = api_get_course_int_id();
22
23
        $tableItemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
24
        $tableUser = Database::get_main_table(TABLE_MAIN_USER);
25
26
        $sql = "SELECT count(tool) AS total_number_of_items
27
                FROM $tableItemProperty track_resource, $tableUser user
28
                WHERE
29
                    track_resource.c_id = $courseId AND
30
                    track_resource.insert_user_id = user.user_id AND
31
                    session_id ".(empty($sessionId) ? ' IS NULL ' : " = $sessionId ");
32
33
        if (isset($_GET['keyword'])) {
34
            $keyword = Database::escape_string(trim($_GET['keyword']));
35
            $sql .= " AND (
36
                        user.username LIKE '%".$keyword."%' OR
37
                        lastedit_type LIKE '%".$keyword."%' OR
38
                        tool LIKE '%".$keyword."%'
39
                    )";
40
        }
41
42
        $sql .= " AND tool IN (
43
                    'document',
44
                    'learnpath',
45
                    'quiz',
46
                    'glossary',
47
                    'link',
48
                    'course_description',
49
                    'announcement',
50
                    'thematic',
51
                    'thematic_advance',
52
                    'thematic_plan'
53
                )";
54
        $res = Database::query($sql);
55
        $obj = Database::fetch_object($res);
56
57
        return $obj->total_number_of_items;
58
    }
59
60
    public static function getItemResourcesData($from, $numberOfItems, $column, $direction): array
61
    {
62
        $sessionId = api_get_session_id();
63
        $courseId = api_get_course_int_id();
64
65
        $tableItemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
66
        $tableUser = Database::get_main_table(TABLE_MAIN_USER);
67
        $tableSession = Database::get_main_table(TABLE_MAIN_SESSION);
68
        $column = (int) $column;
69
        $direction = !in_array(strtolower(trim($direction)), ['asc', 'desc']) ? 'asc' : $direction;
70
71
        $sql = "SELECT
72
                    tool as col0,
73
                    lastedit_type as col1,
74
                    ref as ref,
75
                    user.username as col3,
76
                    insert_date as col6,
77
                    visibility as col7,
78
                    user.user_id as user_id
79
                FROM $tableItemProperty track_resource, $tableUser user
80
                WHERE
81
                  track_resource.c_id = $courseId AND
82
                  track_resource.insert_user_id = user.user_id AND
83
                  session_id ".(empty($sessionId) ? ' IS NULL ' : " = $sessionId ");
84
85
        if (isset($_GET['keyword'])) {
86
            $keyword = Database::escape_string(trim($_GET['keyword']));
87
            $sql .= " AND (
88
                        user.username LIKE '%".$keyword."%' OR
89
                        lastedit_type LIKE '%".$keyword."%' OR
90
                        tool LIKE '%".$keyword."%'
91
                     ) ";
92
        }
93
94
        $sql .= " AND tool IN (
95
                    'document',
96
                    'learnpath',
97
                    'quiz',
98
                    'glossary',
99
                    'link',
100
                    'course_description',
101
                    'announcement',
102
                    'thematic',
103
                    'thematic_advance',
104
                    'thematic_plan'
105
                )";
106
107
        if ($column == 0) {
108
            $column = '0';
109
        }
110
        if ($column != '' && $direction != '') {
111
            if ($column != 2 && $column != 4) {
112
                $sql .= " ORDER BY col$column $direction";
113
            }
114
        } else {
115
            $sql .= " ORDER BY col6 DESC ";
116
        }
117
118
        $from = intval($from);
119
        if ($from) {
120
            $numberOfItems = intval($numberOfItems);
121
            $sql .= " LIMIT $from, $numberOfItems ";
122
        }
123
124
        $res = Database::query($sql);
125
        $resources = [];
126
        $thematicTools = ['thematic', 'thematic_advance', 'thematic_plan'];
127
        while ($row = Database::fetch_array($res)) {
128
            $ref = $row['ref'];
129
            $tableName = self::getToolNameTable($row['col0']);
130
            $tableTool = Database::get_course_table($tableName['table_name']);
131
132
            $id = $tableName['id_tool'];
133
            $recorset = false;
134
135
            if (in_array($row['col0'], ['thematic_plan', 'thematic_advance'])) {
136
                $tblThematic = Database::get_course_table(TABLE_THEMATIC);
137
                $sql = "SELECT thematic_id FROM $tableTool
138
                        WHERE c_id = $courseId AND id = $ref";
139
                $rsThematic = Database::query($sql);
140
                if (Database::num_rows($rsThematic)) {
141
                    $rowThematic = Database::fetch_array($rsThematic);
142
                    $thematicId = $rowThematic['thematic_id'];
143
144
                    $sql = "SELECT session.id, session.name, user.username
145
                            FROM $tblThematic t, $tableSession session, $tableUser user
146
                            WHERE
147
                              t.c_id = $courseId AND
148
                              t.session_id = session.id AND
149
                              session.id_coach = user.user_id AND
150
                              t.id = $thematicId";
151
                    $recorset = Database::query($sql);
152
                }
153
            } else {
154
                $sql = "SELECT session.id, session.name, user.username
155
                          FROM $tableTool tool, $tableSession session, $tableUser user
156
                          WHERE
157
                              tool.c_id = $courseId AND
158
                              tool.session_id = session.id AND
159
                              session.id_coach = user.user_id AND
160
                              tool.$id = $ref";
161
                $recorset = Database::query($sql);
162
            }
163
164
            if (!empty($recorset)) {
165
                $obj = Database::fetch_object($recorset);
166
167
                $nameSession = '';
168
                $coachName = '';
169
                if (!empty($obj)) {
170
                    $nameSession = $obj->name;
171
                    $coachName = $obj->username;
172
                }
173
174
                $urlTool = api_get_path(WEB_CODE_PATH).$tableName['link_tool'];
175
176
                if ($row['col6'] != 2) {
177
                    if (in_array($row['col0'], $thematicTools)) {
178
                        $expThematicTool = explode('_', $row['col0']);
179
                        $thematicTooltitle = '';
180
                        if (is_array($expThematicTool)) {
181
                            foreach ($expThematicTool as $exp) {
182
                                $thematicTooltitle .= api_ucfirst($exp);
183
                            }
184
                        } else {
185
                            $thematicTooltitle = api_ucfirst($row['col0']);
186
                        }
187
188
                        $row[0] = '<a href="'.$urlTool.'?'.api_get_cidreq().'&action=thematic_details">'.get_lang(
189
                                $thematicTooltitle
190
                            ).'</a>';
191
                    } else {
192
                        $row[0] = '<a href="'.$urlTool.'?'.api_get_cidreq().'">'.get_lang(
193
                                'Tool'.api_ucfirst($row['col0'])
194
                            ).'</a>';
195
                    }
196
                } else {
197
                    $row[0] = api_ucfirst($row['col0']);
198
                }
199
                $row[1] = get_lang($row[1]);
200
                $row[6] = api_convert_and_format_date($row['col6'], null, date_default_timezone_get());
201
                $row[5] = '';
202
                //@todo Improve this code please
203
                switch ($tableName['table_name']) {
204
                    case 'document':
205
                        $sql = "SELECT tool.title as title FROM $tableTool tool
206
                                WHERE c_id = $courseId AND id = $ref";
207
                        $rsDocument = Database::query($sql);
208
                        $objDocument = Database::fetch_object($rsDocument);
209
                        if ($objDocument) {
210
                            $row[5] = $objDocument->title;
211
                        }
212
                        break;
213
                    case 'quiz':
214
                    case 'course_description':
215
                    case 'announcement':
216
                        $sql = "SELECT title FROM $tableTool
217
                                WHERE c_id = $courseId AND id = $ref";
218
                        $rsDocument = Database::query($sql);
219
                        $objDocument = Database::fetch_object($rsDocument);
220
                        if ($objDocument) {
221
                            $row[5] = $objDocument->title;
222
                        }
223
                        break;
224
                    case 'glossary':
225
                        $sql = "SELECT name FROM $tableTool
226
                                WHERE c_id = $courseId AND glossary_id = $ref";
227
                        $rsDocument = Database::query($sql);
228
                        $objDocument = Database::fetch_object($rsDocument);
229
                        if ($objDocument) {
230
                            $row[5] = $objDocument->name;
231
                        }
232
                        break;
233
                    case 'lp':
234
                        $sql = "SELECT name
235
                                FROM $tableTool WHERE c_id = $courseId AND id = $ref";
236
                        $rsDocument = Database::query($sql);
237
                        $objDocument = Database::fetch_object($rsDocument);
238
                        $row[5] = $objDocument->name;
239
                        break;
240
                    case 'thematic_plan':
241
                    case 'thematic':
242
                        $rs = Database::query("SELECT title FROM $tableTool WHERE c_id = $courseId AND id = $ref");
243
                        if (Database::num_rows($rs) > 0) {
244
                            $obj = Database::fetch_object($rs);
245
                            if ($obj) {
246
                                $row[5] = $obj->title;
247
                            }
248
                        }
249
                        break;
250
                    case 'thematic_advance':
251
                        $rs = Database::query("SELECT content FROM $tableTool WHERE c_id = $courseId AND id = $ref");
252
                        if (Database::num_rows($rs) > 0) {
253
                            $obj = Database::fetch_object($rs);
254
                            if ($obj) {
255
                                $row[5] = $obj->content;
256
                            }
257
                        }
258
                        break;
259
                    default:
260
                        break;
261
                }
262
263
                $row2 = $nameSession;
264
                if (!empty($coachName)) {
265
                    $row2 .= '<br />'.get_lang('Coach').': '.$coachName;
266
                }
267
                $row[2] = $row2;
268
                if (!empty($row['col3'])) {
269
                    $userInfo = api_get_user_info($row['user_id']);
270
                    $row['col3'] = Display::url(
271
                        $row['col3'],
272
                        $userInfo['profile_url']
273
                    );
274
                    $row[3] = $row['col3'];
275
276
                    $ip = Tracking::get_ip_from_user_event(
277
                        $row['user_id'],
278
                        $row['col6'],
279
                        true
280
                    );
281
                    if (empty($ip)) {
282
                        $ip = get_lang('Unknown');
283
                    }
284
                    $row[4] = $ip;
285
                }
286
287
                $resources[] = $row;
288
            }
289
        }
290
291
        return $resources;
292
    }
293
294
    public static function getToolNameTable(string $tool): array
295
    {
296
        $linkTool = '';
297
        $idTool = '';
298
299
        switch ($tool) {
300
            case 'document':
301
                $tableName = TABLE_DOCUMENT;
302
                $linkTool = 'document/document.php';
303
                $idTool = 'id';
304
                break;
305
            case 'learnpath':
306
                $tableName = TABLE_LP_MAIN;
307
                $linkTool = 'lp/lp_controller.php';
308
                $idTool = 'id';
309
                break;
310
            case 'quiz':
311
                $tableName = TABLE_QUIZ_TEST;
312
                $linkTool = 'exercise/exercise.php';
313
                $idTool = 'iid';
314
                break;
315
            case 'glossary':
316
                $tableName = TABLE_GLOSSARY;
317
                $linkTool = 'glossary/index.php';
318
                $idTool = 'glossary_id';
319
                break;
320
            case 'link':
321
                $tableName = TABLE_LINK;
322
                $linkTool = 'link/link.php';
323
                $idTool = 'id';
324
                break;
325
            case 'course_description':
326
                $tableName = TABLE_COURSE_DESCRIPTION;
327
                $linkTool = 'course_description/';
328
                $idTool = 'id';
329
                break;
330
            case 'announcement':
331
                $tableName = TABLE_ANNOUNCEMENT;
332
                $linkTool = 'announcements/announcements.php';
333
                $idTool = 'id';
334
                break;
335
            case 'thematic':
336
                $tableName = TABLE_THEMATIC;
337
                $linkTool = 'course_progress/index.php';
338
                $idTool = 'id';
339
                break;
340
            case 'thematic_advance':
341
                $tableName = TABLE_THEMATIC_ADVANCE;
342
                $linkTool = 'course_progress/index.php';
343
                $idTool = 'id';
344
                break;
345
            case 'thematic_plan':
346
                $tableName = TABLE_THEMATIC_PLAN;
347
                $linkTool = 'course_progress/index.php';
348
                $idTool = 'id';
349
                break;
350
            default:
351
                $tableName = $tool;
352
                break;
353
        }
354
355
        return [
356
            'table_name' => $tableName,
357
            'link_tool' => $linkTool,
358
            'id_tool' => $idTool,
359
        ];
360
    }
361
362
    public static function getAdditionalProfileExtraFields(): array
363
    {
364
        $additionalProfileField = $_GET['additional_profile_field'] ?? [];
365
366
        $additionalExtraFieldsInfo = [];
367
368
        $objExtraField = new ExtraField('user');
369
370
        foreach ($additionalProfileField as $fieldId) {
371
            $additionalExtraFieldsInfo[$fieldId] = $objExtraField->getFieldInfoByFieldId($fieldId);
372
        }
373
374
        return $additionalExtraFieldsInfo;
375
    }
376
377
    public static function displayAdditionalProfileFields(array $exclude = [], $formAction = null): string
378
    {
379
        $formAction = $formAction ?: 'courseLog.php';
380
381
        // getting all the extra profile fields that are defined by the platform administrator
382
        $extraFields = UserManager::get_extra_fields(0, 50);
383
384
        // creating the form
385
        $return = '<form action="'.$formAction.'" method="get" name="additional_profile_field_form"
386
            id="additional_profile_field_form">';
387
        // the select field with the additional user profile fields, this is where we select the field of which we want to see
388
        // the information the users have entered or selected.
389
        $return .= '<div class="form-group">';
390
        $return .= '<select class="chzn-select" name="additional_profile_field[]" multiple>';
391
        $return .= '<option value="-">'.get_lang('SelectFieldToAdd').'</option>';
392
        $extraFieldsToShow = 0;
393
        foreach ($extraFields as $field) {
394
            // exclude extra profile fields by id
395
            if (in_array($field[3], $exclude)) {
396
                continue;
397
            }
398
            // show only extra fields that are visible + and can be filtered, added by J.Montoya
399
            if ($field[6] == 1 && $field[8] == 1) {
400
                if (isset($_GET['additional_profile_field']) && in_array($field[0], $_GET['additional_profile_field'])) {
401
                    $selected = 'selected="selected"';
402
                } else {
403
                    $selected = '';
404
                }
405
                $extraFieldsToShow++;
406
                $return .= '<option value="'.$field[0].'" '.$selected.'>'.$field[3].'</option>';
407
            }
408
        }
409
        $return .= '</select>';
410
        $return .= '</div>';
411
412
        // the form elements for the $_GET parameters (because the form is passed through GET
413
        foreach ($_GET as $key => $value) {
414
            if ($key != 'additional_profile_field') {
415
                $return .= '<input type="hidden" name="'.Security::remove_XSS($key).'" value="'.Security::remove_XSS(
416
                        $value
417
                    ).'" />';
418
            }
419
        }
420
        // the submit button
421
        $return .= '<div class="form-group">';
422
        $return .= '<button class="save btn btn-primary" type="submit">'
423
            .get_lang('AddAdditionalProfileField').'</button>';
424
        $return .= '</div>';
425
        $return .= '</form>';
426
427
        return $extraFieldsToShow > 0 ? $return : '';
428
    }
429
430
    /**
431
     * This function gets all the information of a certrain ($field_id)
432
     * additional profile field for a specific list of users is more efficent
433
     * than get_addtional_profile_information_of_field() function
434
     * It gets the information of all the users so that it can be displayed
435
     * in the sortable table or in the csv or xls export.
436
     *
437
     * @param int   field id
438
     * @param array list of user ids
439
     *
440
     * @author     Julio Montoya <[email protected]>
441
     *
442
     * @since      Nov 2009
443
     *
444
     * @version    1.8.6.2
445
     */
446
    public static function getAdditionalProfileInformationOfFieldByUser($fieldId, $users): array
447
    {
448
        // Database table definition
449
        $tableUser = Database::get_main_table(TABLE_MAIN_USER);
450
        $tableUserFieldValues = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
451
        $extraField = Database::get_main_table(TABLE_EXTRA_FIELD);
452
        $resultExtraField = UserManager::get_extra_field_information($fieldId);
453
        $return = [];
454
        if (!empty($users)) {
455
            if ($resultExtraField['field_type'] == UserManager::USER_FIELD_TYPE_TAG) {
456
                foreach ($users as $user_id) {
457
                    $userResult = UserManager::get_user_tags($user_id, $fieldId);
458
                    $tagList = [];
459
                    foreach ($userResult as $item) {
460
                        $tagList[] = $item['tag'];
461
                    }
462
                    $return[$user_id][] = implode(', ', $tagList);
463
                }
464
            } else {
465
                $newUserArray = [];
466
                foreach ($users as $user_id) {
467
                    $newUserArray[] = "'".$user_id."'";
468
                }
469
                $users = implode(',', $newUserArray);
470
                $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
471
                // Selecting only the necessary information NOT ALL the user list
472
                $sql = "SELECT user.user_id, v.value
473
                        FROM $tableUser user
474
                        INNER JOIN $tableUserFieldValues v
475
                        ON (user.user_id = v.item_id)
476
                        INNER JOIN $extraField f
477
                        ON (f.id = v.field_id)
478
                        WHERE
479
                            f.extra_field_type = $extraFieldType AND
480
                            v.field_id=".intval($fieldId)." AND
481
                            user.user_id IN ($users)";
482
483
                $result = Database::query($sql);
484
                while ($row = Database::fetch_array($result)) {
485
                    // get option value for field type double select by id
486
                    if (!empty($row['value'])) {
487
                        if ($resultExtraField['field_type'] ==
488
                            ExtraField::FIELD_TYPE_DOUBLE_SELECT
489
                        ) {
490
                            $idDoubleSelect = explode(';', $row['value']);
491
                            if (is_array($idDoubleSelect)) {
492
                                $value1 = $resultExtraField['options'][$idDoubleSelect[0]]['option_value'];
493
                                $value2 = $resultExtraField['options'][$idDoubleSelect[1]]['option_value'];
494
                                $row['value'] = ($value1.';'.$value2);
495
                            }
496
                        }
497
498
                        if ($resultExtraField['field_type'] == ExtraField::FIELD_TYPE_SELECT_WITH_TEXT_FIELD) {
499
                            $parsedValue = explode('::', $row['value']);
500
501
                            if ($parsedValue) {
502
                                $value1 = $resultExtraField['options'][$parsedValue[0]]['display_text'];
503
                                $value2 = $parsedValue[1];
504
505
                                $row['value'] = "$value1: $value2";
506
                            }
507
                        }
508
509
                        if ($resultExtraField['field_type'] == ExtraField::FIELD_TYPE_TRIPLE_SELECT) {
510
                            [$level1, $level2, $level3] = explode(';', $row['value']);
511
512
                            $row['value'] = $resultExtraField['options'][$level1]['display_text'].' / ';
513
                            $row['value'] .= $resultExtraField['options'][$level2]['display_text'].' / ';
514
                            $row['value'] .= $resultExtraField['options'][$level3]['display_text'];
515
                        }
516
                    }
517
                    // get other value from extra field
518
                    $return[$row['user_id']][] = $row['value'];
519
                }
520
            }
521
        }
522
523
        return $return;
524
    }
525
526
    /**
527
     * Get number of users for sortable with pagination.
528
     */
529
    public static function getNumberOfUsers(array $conditions): int
530
    {
531
        $conditions['get_count'] = true;
532
533
        return self::getUserData(0, 0, 0, '', $conditions);
534
    }
535
536
    /**
537
     * Get data for users list in sortable with pagination.
538
     */
539
    public static function getUserData(
540
        $from,
541
        $numberOfItems,
542
        $column,
543
        $direction,
544
        array $conditions = [],
545
        bool $exerciseToCheckConfig = true,
546
        bool $displaySessionInfo = false
547
    ) {
548
        $includeInvitedUsers = $conditions['include_invited_users'] ?? false; // include the invited users
549
        $getCount = $conditions['get_count'] ?? false;
550
551
        $csvContent = [];
552
        $course_code = isset($GLOBALS['course_code']) ? Database::escape_string($GLOBALS['course_code']) : api_get_course_id();
553
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
554
        $tblUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
555
        $accessUrlId = api_get_current_access_url_id();
556
557
        // get all users data from a course for sortable with limit
558
        if (!empty($GLOBALS['user_ids']) && is_array($GLOBALS['user_ids'])) {
559
            $GLOBALS['user_ids'] = array_map('intval', $GLOBALS['user_ids']);
560
            $conditionUser = " WHERE user.id IN (".implode(',', $GLOBALS['user_ids']).") ";
561
        } else {
562
            $GLOBALS['user_ids'] = (int) $GLOBALS['user_ids'];
563
            $conditionUser = " WHERE user.id = {$GLOBALS['user_ids']} ";
564
        }
565
566
        if (!empty($_GET['user_keyword'])) {
567
            $keyword = trim(Database::escape_string($_GET['user_keyword']));
568
            $conditionUser .= " AND (
569
                user.firstname LIKE '%".$keyword."%' OR
570
                user.lastname LIKE '%".$keyword."%'  OR
571
                user.username LIKE '%".$keyword."%'  OR
572
                user.email LIKE '%".$keyword."%'
573
             ) ";
574
        }
575
576
        $urlTable = '';
577
        $urlCondition = '';
578
        if (api_is_multiple_url_enabled()) {
579
            $urlTable = " INNER JOIN $tblUrlRelUser as url_users ON (user.id = url_users.user_id)";
580
            $urlCondition = " AND access_url_id = '$accessUrlId'";
581
        }
582
583
        $invitedUsersCondition = '';
584
        if (!$includeInvitedUsers) {
585
            $invitedUsersCondition = " AND user.status != ".INVITEE;
586
        }
587
588
        $select = '
589
                SELECT user.id as user_id,
590
                    user.official_code  as col0,
591
                    user.lastname       as col1,
592
                    user.firstname      as col2,
593
                    user.username       as col3,
594
                    user.email          as col4';
595
        if ($getCount) {
596
            $select = ' SELECT COUNT(distinct(user.id)) as count ';
597
        }
598
599
        $sqlInjectJoins = '';
600
        $where = 'AND 1 = 1 ';
601
        $sqlInjectWhere = '';
602
        if (!empty($conditions)) {
603
            if (isset($conditions['inject_joins'])) {
604
                $sqlInjectJoins = $conditions['inject_joins'];
605
            }
606
            if (isset($conditions['where'])) {
607
                $where = $conditions['where'];
608
            }
609
            if (isset($conditions['inject_where'])) {
610
                $sqlInjectWhere = $conditions['inject_where'];
611
            }
612
            $injectExtraFields = !empty($conditions['inject_extra_fields']) ? $conditions['inject_extra_fields'] : 1;
613
            $injectExtraFields = rtrim($injectExtraFields, ', ');
614
            if (false === $getCount) {
615
                $select .= " , $injectExtraFields";
616
            }
617
        }
618
619
        $sql = "$select
620
                FROM $tblUser as user
621
                $urlTable
622
                $sqlInjectJoins
623
                $conditionUser
624
                $urlCondition
625
                $invitedUsersCondition
626
                $where
627
                $sqlInjectWhere
628
                ";
629
630
        if (!in_array($direction, ['ASC', 'DESC'])) {
631
            $direction = 'ASC';
632
        }
633
634
        $column = $column <= 2 ? (int) $column : 0;
635
        $from = (int) $from;
636
        $numberOfItems = (int) $numberOfItems;
637
638
        if ($getCount) {
639
            $res = Database::query($sql);
640
            $row = Database::fetch_array($res);
641
642
            return $row['count'];
643
        }
644
645
        $sortByFirstName = api_sort_by_first_name();
646
647
        if ($sortByFirstName) {
648
            if ($column == 1) {
649
                $column = 2;
650
            } elseif ($column == 2) {
651
                $column = 1;
652
            }
653
        }
654
655
        $sql .= " ORDER BY col$column $direction ";
656
        $sql .= " LIMIT $from, $numberOfItems";
657
658
        $res = Database::query($sql);
659
        $users = [];
660
661
        $courseInfo = api_get_course_info($course_code);
662
        $courseId = $courseInfo['real_id'];
663
        $courseCode = $courseInfo['code'];
664
665
        $totalSurveys = 0;
666
        $totalExercises = ExerciseLib::get_all_exercises(
667
            $courseInfo,
668
            $GLOBALS['session_id'],
669
            false,
670
            null,
671
            false,
672
            3
673
        );
674
675
        if (empty($GLOBALS['session_id'])) {
676
            $surveyUserList = [];
677
            $surveyList = SurveyManager::get_surveys($course_code, $GLOBALS['session_id']);
678
            if ($surveyList) {
679
                $totalSurveys = count($surveyList);
680
                foreach ($surveyList as $survey) {
681
                    $userList = SurveyManager::get_people_who_filled_survey(
682
                        $survey['survey_id'],
683
                        false,
684
                        $courseId
685
                    );
686
687
                    foreach ($userList as $user_id) {
688
                        isset($surveyUserList[$user_id]) ? $surveyUserList[$user_id]++ : $surveyUserList[$user_id] = 1;
689
                    }
690
                }
691
            }
692
        }
693
694
        $urlBase = api_get_path(WEB_CODE_PATH).'mySpace/myStudents.php?details=true&cidReq='.$courseCode.
695
            '&course='.$course_code.'&origin=tracking_course&id_session='.$GLOBALS['session_id'];
696
697
        Session::write('user_id_list', []);
698
        $userIdList = [];
699
700
        if ($exerciseToCheckConfig) {
701
            $addExerciseOption = api_get_configuration_value('add_exercise_best_attempt_in_report');
702
            $exerciseResultsToCheck = [];
703
            if (!empty($addExerciseOption) && isset($addExerciseOption['courses']) &&
704
                isset($addExerciseOption['courses'][$courseCode])
705
            ) {
706
                foreach ($addExerciseOption['courses'][$courseCode] as $exerciseId) {
707
                    $exercise = new Exercise();
708
                    $exercise->read($exerciseId);
709
                    if ($exercise->iid) {
710
                        $exerciseResultsToCheck[] = $exercise;
711
                    }
712
                }
713
            }
714
        }
715
716
        $lpShowMaxProgress = api_get_configuration_value('lp_show_max_progress_instead_of_average');
717
        if (api_get_configuration_value('lp_show_max_progress_or_average_enable_course_level_redefinition')) {
718
            $lpShowProgressCourseSetting = api_get_course_setting('lp_show_max_or_average_progress', $courseInfo, true);
719
            if (in_array($lpShowProgressCourseSetting, ['max', 'average'])) {
720
                $lpShowMaxProgress = ('max' === $lpShowProgressCourseSetting);
721
            }
722
        }
723
724
        while ($user = Database::fetch_array($res, 'ASSOC')) {
725
            $userIdList[] = $user['user_id'];
726
            $user['official_code'] = $user['col0'];
727
            $user['username'] = $user['col3'];
728
            $user['time'] = api_time_to_hms(
729
                Tracking::get_time_spent_on_the_course(
730
                    $user['user_id'],
731
                    $courseId,
732
                    $GLOBALS['session_id']
733
                )
734
            );
735
736
            $avgStudentScore = Tracking::get_avg_student_score(
737
                $user['user_id'],
738
                $course_code,
739
                [],
740
                $GLOBALS['session_id']
741
            );
742
743
            $averageBestScore = Tracking::get_avg_student_score(
744
                $user['user_id'],
745
                $course_code,
746
                [],
747
                $GLOBALS['session_id'],
748
                false,
749
                false,
750
                true
751
            );
752
753
            $avgStudentProgress = Tracking::get_avg_student_progress(
754
                $user['user_id'],
755
                $course_code,
756
                [],
757
                $GLOBALS['session_id'],
758
                false,
759
                false,
760
                $lpShowMaxProgress
761
            );
762
763
            if (empty($avgStudentProgress)) {
764
                $avgStudentProgress = 0;
765
            }
766
            $user['average_progress'] = $avgStudentProgress.'%';
767
768
            $totalUserExercise = Tracking::get_exercise_student_progress(
769
                $totalExercises,
770
                $user['user_id'],
771
                $courseId,
772
                $GLOBALS['session_id']
773
            );
774
775
            $user['exercise_progress'] = $totalUserExercise;
776
777
            $totalUserExercise = Tracking::get_exercise_student_average_best_attempt(
778
                $totalExercises,
779
                $user['user_id'],
780
                $courseId,
781
                $GLOBALS['session_id']
782
            );
783
784
            $user['exercise_average_best_attempt'] = $totalUserExercise;
785
786
            if (is_numeric($avgStudentScore)) {
787
                $user['student_score'] = $avgStudentScore.'%';
788
            } else {
789
                $user['student_score'] = $avgStudentScore;
790
            }
791
792
            if (is_numeric($averageBestScore)) {
793
                $user['student_score_best'] = $averageBestScore.'%';
794
            } else {
795
                $user['student_score_best'] = $averageBestScore;
796
            }
797
798
            $exerciseResults = [];
799
            if (!empty($exerciseResultsToCheck)) {
800
                foreach ($exerciseResultsToCheck as $exercise) {
801
                    $bestExerciseResult = Event::get_best_attempt_exercise_results_per_user(
802
                        $user['user_id'],
803
                        $exercise->iid,
804
                        $courseId,
805
                        $GLOBALS['session_id'],
806
                        false
807
                    );
808
809
                    $best = null;
810
                    if ($bestExerciseResult) {
811
                        $best = $bestExerciseResult['exe_result'] / $bestExerciseResult['exe_weighting'];
812
                        $best = round($best, 2) * 100;
813
                        $best .= '%';
814
                    }
815
                    $exerciseResults['exercise_'.$exercise->iid] = $best;
816
                }
817
            }
818
819
            $user['count_assignments'] = Tracking::count_student_assignments(
820
                $user['user_id'],
821
                $course_code,
822
                $GLOBALS['session_id']
823
            );
824
            $user['count_messages'] = Tracking::count_student_messages(
825
                $user['user_id'],
826
                $course_code,
827
                $GLOBALS['session_id']
828
            );
829
            $user['first_connection'] = Tracking::get_first_connection_date_on_the_course(
830
                $user['user_id'],
831
                $courseId,
832
                $GLOBALS['session_id'],
833
                false === $GLOBALS['export_csv']
834
            );
835
836
            $user['last_connection'] = Tracking::get_last_connection_date_on_the_course(
837
                $user['user_id'],
838
                $courseInfo,
839
                $GLOBALS['session_id'],
840
                false === $GLOBALS['export_csv']
841
            );
842
843
            $user['lp_finalization_date'] = Tracking::getCourseLpFinalizationDate(
844
                $user['user_id'],
845
                $courseId,
846
                $GLOBALS['session_id'],
847
                false === $GLOBALS['export_csv']
848
            );
849
850
            $user['quiz_finalization_date'] = Tracking::getCourseQuizLastFinalizationDate(
851
                $user['user_id'],
852
                $courseId,
853
                $GLOBALS['session_id'],
854
                false === $GLOBALS['export_csv']
855
            );
856
857
            if ($GLOBALS['export_csv']) {
858
                if (!empty($user['first_connection'])) {
859
                    $user['first_connection'] = api_get_local_time($user['first_connection']);
860
                } else {
861
                    $user['first_connection'] = '-';
862
                }
863
                if (!empty($user['last_connection'])) {
864
                    $user['last_connection'] = api_get_local_time($user['last_connection']);
865
                } else {
866
                    $user['last_connection'] = '-';
867
                }
868
                if (!empty($user['lp_finalization_date'])) {
869
                    $user['lp_finalization_date'] = api_get_local_time($user['lp_finalization_date']);
870
                } else {
871
                    $user['lp_finalization_date'] = '-';
872
                }
873
                if (!empty($user['quiz_finalization_date'])) {
874
                    $user['quiz_finalization_date'] = api_get_local_time($user['quiz_finalization_date']);
875
                } else {
876
                    $user['quiz_finalization_date'] = '-';
877
                }
878
            }
879
880
            if (empty($GLOBALS['session_id'])) {
881
                $user['survey'] = ($surveyUserList[$user['user_id']] ?? 0).' / '.$totalSurveys;
882
            }
883
884
            $url = $urlBase.'&student='.$user['user_id'];
885
886
            $user['link'] = '<center><a href="'.$url.'">
887
                            '.Display::return_icon('2rightarrow.png', get_lang('Details')).'
888
                             </a></center>';
889
890
            // store columns in array $users
891
            $userRow = [];
892
            if ($displaySessionInfo && !empty($GLOBALS['session_id'])) {
893
                $sessionInfo = api_get_session_info($GLOBALS['session_id']);
894
                $userRow['session_name'] = $sessionInfo['name'];
895
                $userRow['session_startdate'] = $sessionInfo['access_start_date'];
896
                $userRow['session_enddate'] = $sessionInfo['access_end_date'];
897
                $userRow['course_name'] = $courseInfo['name'];
898
            }
899
            $userRow['official_code'] = $user['official_code']; //0
900
            if ($sortByFirstName) {
901
                $userRow['firstname'] = $user['col2'];
902
                $userRow['lastname'] = $user['col1'];
903
            } else {
904
                $userRow['lastname'] = $user['col1'];
905
                $userRow['firstname'] = $user['col2'];
906
            }
907
            $userRow['username'] = $user['username'];
908
            $userRow['time'] = $user['time'];
909
            $userRow['average_progress'] = $user['average_progress'];
910
            $userRow['exercise_progress'] = $user['exercise_progress'];
911
            $userRow['exercise_average_best_attempt'] = $user['exercise_average_best_attempt'];
912
            $userRow['student_score'] = $user['student_score'];
913
            $userRow['student_score_best'] = $user['student_score_best'];
914
            if (!empty($exerciseResults)) {
915
                foreach ($exerciseResults as $exerciseId => $bestResult) {
916
                    $userRow[$exerciseId] = $bestResult;
917
                }
918
            }
919
920
            $userRow['count_assignments'] = $user['count_assignments'];
921
            $userRow['count_messages'] = $user['count_messages'];
922
923
            $userGroupManager = new UserGroup();
924
            if ($GLOBALS['export_csv']) {
925
                $userRow['classes'] = implode(
926
                    ',',
927
                    $userGroupManager->getNameListByUser($user['user_id'], UserGroup::NORMAL_CLASS)
928
                );
929
            } else {
930
                $userRow['classes'] = $userGroupManager->getLabelsFromNameList(
931
                    $user['user_id'],
932
                    UserGroup::NORMAL_CLASS
933
                );
934
            }
935
936
            if (empty($GLOBALS['session_id'])) {
937
                $userRow['survey'] = $user['survey'];
938
            } else {
939
                $userSession = SessionManager::getUserSession($user['user_id'], $GLOBALS['session_id']);
940
                $userRow['registered_at'] = '';
941
                if ($userSession) {
942
                    $userRow['registered_at'] = api_get_local_time($userSession['registered_at']);
943
                }
944
            }
945
946
            $userRow['first_connection'] = $user['first_connection'];
947
            $userRow['last_connection'] = $user['last_connection'];
948
949
            $userRow['lp_finalization_date'] = $user['lp_finalization_date'];
950
            $userRow['quiz_finalization_date'] = $user['quiz_finalization_date'];
951
952
            // we need to display an additional profile field
953
            if (isset($_GET['additional_profile_field'])) {
954
                $data = Session::read('additional_user_profile_info');
955
956
                $extraFieldInfo = Session::read('extra_field_info');
957
                foreach ($_GET['additional_profile_field'] as $fieldId) {
958
                    if (isset($data[$fieldId]) && isset($data[$fieldId][$user['user_id']])) {
959
                        if (is_array($data[$fieldId][$user['user_id']])) {
960
                            $userRow[$extraFieldInfo[$fieldId]['variable']] = implode(
961
                                ', ',
962
                                $data[$fieldId][$user['user_id']]
963
                            );
964
                        } else {
965
                            $userRow[$extraFieldInfo[$fieldId]['variable']] = $data[$fieldId][$user['user_id']];
966
                        }
967
                    } else {
968
                        $userRow[$extraFieldInfo[$fieldId]['variable']] = '';
969
                    }
970
                }
971
            }
972
973
            $data = Session::read('default_additional_user_profile_info');
974
            $defaultExtraFieldInfo = Session::read('default_extra_field_info');
975
            if (isset($defaultExtraFieldInfo) && isset($data)) {
976
                foreach ($data as $key => $val) {
977
                    if (isset($val[$user['user_id']])) {
978
                        if (is_array($val[$user['user_id']])) {
979
                            $userRow[$defaultExtraFieldInfo[$key]['variable']] = implode(
980
                                ', ',
981
                                $val[$user['user_id']]
982
                            );
983
                        } else {
984
                            $userRow[$defaultExtraFieldInfo[$key]['variable']] = $val[$user['user_id']];
985
                        }
986
                    } else {
987
                        $userRow[$defaultExtraFieldInfo[$key]['variable']] = '';
988
                    }
989
                }
990
            }
991
992
            if (api_get_setting('show_email_addresses') === 'true') {
993
                $userRow['email'] = $user['col4'];
994
            }
995
996
            $userRow['link'] = $user['link'];
997
998
            if ($GLOBALS['export_csv']) {
999
                unset($userRow['link']);
1000
                $csvContent[] = $userRow;
1001
            }
1002
            $users[] = array_values($userRow);
1003
        }
1004
1005
        if ($GLOBALS['export_csv']) {
1006
            Session::write('csv_content', $csvContent);
1007
        }
1008
1009
        Session::erase('additional_user_profile_info');
1010
        Session::erase('extra_field_info');
1011
        Session::erase('default_additional_user_profile_info');
1012
        Session::erase('default_extra_field_info');
1013
        Session::write('user_id_list', $userIdList);
1014
1015
        return $users;
1016
    }
1017
1018
    /**
1019
     * Get data for users list in sortable with pagination.
1020
     */
1021
    public static function getTotalTimeReport(
1022
        $from,
1023
        $numberOfItems,
1024
        $column,
1025
        $direction,
1026
        bool $includeInvitedUsers = false
1027
    ): array {
1028
        global $user_ids, $course_code, $export_csv, $session_id;
1029
1030
        $course_code = Database::escape_string($course_code);
1031
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
1032
        $tblUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1033
        $accessUrlId = api_get_current_access_url_id();
1034
1035
        // get all users data from a course for sortable with limit
1036
        if (is_array($user_ids)) {
1037
            $user_ids = array_map('intval', $user_ids);
1038
            $conditionUser = " WHERE user.user_id IN (".implode(',', $user_ids).") ";
1039
        } else {
1040
            $user_ids = intval($user_ids);
1041
            $conditionUser = " WHERE user.user_id = $user_ids ";
1042
        }
1043
1044
        $urlTable = null;
1045
        $urlCondition = null;
1046
        if (api_is_multiple_url_enabled()) {
1047
            $urlTable = ", ".$tblUrlRelUser." as url_users";
1048
            $urlCondition = " AND user.user_id = url_users.user_id AND access_url_id='$accessUrlId'";
1049
        }
1050
1051
        $invitedUsersCondition = '';
1052
        if (!$includeInvitedUsers) {
1053
            $invitedUsersCondition = " AND user.status != ".INVITEE;
1054
        }
1055
1056
        $sql = "SELECT  user.user_id as user_id,
1057
                    user.official_code  as col0,
1058
                    user.lastname       as col1,
1059
                    user.firstname      as col2,
1060
                    user.username       as col3
1061
                FROM $tblUser as user $urlTable
1062
                $conditionUser $urlCondition $invitedUsersCondition";
1063
1064
        if (!in_array($direction, ['ASC', 'DESC'])) {
1065
            $direction = 'ASC';
1066
        }
1067
1068
        $column = (int) $column;
1069
        $from = (int) $from;
1070
        $numberOfItems = (int) $numberOfItems;
1071
1072
        $sql .= " ORDER BY col$column $direction ";
1073
        $sql .= " LIMIT $from,$numberOfItems";
1074
1075
        $res = Database::query($sql);
1076
        $users = [];
1077
1078
        $sortByFirstName = api_sort_by_first_name();
1079
        $courseInfo = api_get_course_info($course_code);
1080
        $courseId = $courseInfo['real_id'];
1081
1082
        while ($user = Database::fetch_array($res, 'ASSOC')) {
1083
            $user['official_code'] = $user['col0'];
1084
            $user['lastname'] = $user['col1'];
1085
            $user['firstname'] = $user['col2'];
1086
            $user['username'] = $user['col3'];
1087
1088
            $totalCourseTime = Tracking::get_time_spent_on_the_course(
1089
                $user['user_id'],
1090
                $courseId,
1091
                $session_id
1092
            );
1093
1094
            $user['time'] = api_time_to_hms($totalCourseTime);
1095
            $totalLpTime = Tracking::get_time_spent_in_lp(
1096
                $user['user_id'],
1097
                $course_code,
1098
                [],
1099
                $session_id
1100
            );
1101
1102
            $warning = '';
1103
            if ($totalLpTime > $totalCourseTime) {
1104
                $warning = '&nbsp;'.Display::label(get_lang('TimeDifference'), 'danger');
1105
            }
1106
1107
            $user['total_lp_time'] = api_time_to_hms($totalLpTime).$warning;
1108
1109
            $user['first_connection'] = Tracking::get_first_connection_date_on_the_course(
1110
                $user['user_id'],
1111
                $courseId,
1112
                $session_id
1113
            );
1114
            $user['last_connection'] = Tracking::get_last_connection_date_on_the_course(
1115
                $user['user_id'],
1116
                $courseInfo,
1117
                $session_id,
1118
                $export_csv === false
1119
            );
1120
1121
            $user['link'] = '<center>
1122
                             <a href="../mySpace/myStudents.php?student='.$user['user_id'].'&details=true&course='.$course_code.'&origin=tracking_course&id_session='.$session_id.'">
1123
                             '.Display::return_icon('2rightarrow.png', get_lang('Details')).'
1124
                             </a>
1125
                         </center>';
1126
1127
            // store columns in array $users
1128
            $userRow = [];
1129
            $userRow['official_code'] = $user['official_code']; //0
1130
            if ($sortByFirstName) {
1131
                $userRow['firstname'] = $user['firstname'];
1132
                $userRow['lastname'] = $user['lastname'];
1133
            } else {
1134
                $userRow['lastname'] = $user['lastname'];
1135
                $userRow['firstname'] = $user['firstname'];
1136
            }
1137
            $userRow['username'] = $user['username'];
1138
            $userRow['time'] = $user['time'];
1139
            $userRow['total_lp_time'] = $user['total_lp_time'];
1140
            $userRow['first_connection'] = $user['first_connection'];
1141
            $userRow['last_connection'] = $user['last_connection'];
1142
1143
            $userRow['link'] = $user['link'];
1144
            $users[] = array_values($userRow);
1145
        }
1146
1147
        return $users;
1148
    }
1149
1150
    public static function actionsLeft($current, $sessionId = 0): string
1151
    {
1152
        $usersLink = Display::url(
1153
            Display::return_icon('user.png', get_lang('StudentsTracking'), [], ICON_SIZE_MEDIUM),
1154
            'courseLog.php?'.api_get_cidreq(true, false)
1155
        );
1156
1157
        $groupsLink = Display::url(
1158
            Display::return_icon('group.png', get_lang('GroupReporting'), [], ICON_SIZE_MEDIUM),
1159
            'course_log_groups.php?'.api_get_cidreq()
1160
        );
1161
1162
        $resourcesLink = Display::url(
1163
            Display::return_icon('tools.png', get_lang('ResourcesTracking'), [], ICON_SIZE_MEDIUM),
1164
            'course_log_resources.php?'.api_get_cidreq(true, false)
1165
        );
1166
1167
        $courseLink = Display::url(
1168
            Display::return_icon('course.png', get_lang('CourseTracking'), [], ICON_SIZE_MEDIUM),
1169
            'course_log_tools.php?'.api_get_cidreq(true, false)
1170
        );
1171
1172
        $examLink = Display::url(
1173
            Display::return_icon('quiz.png', get_lang('ExamTracking'), [], ICON_SIZE_MEDIUM),
1174
            api_get_path(WEB_CODE_PATH).'tracking/exams.php?'.api_get_cidreq()
1175
        );
1176
1177
        $eventsLink = Display::url(
1178
            Display::return_icon('security.png', get_lang('EventsReport'), [], ICON_SIZE_MEDIUM),
1179
            api_get_path(WEB_CODE_PATH).'tracking/course_log_events.php?'.api_get_cidreq()
1180
        );
1181
1182
        $lpLink = Display::url(
1183
            Display::return_icon('scorms.png', get_lang('CourseLearningPathsGenericStats'), [], ICON_SIZE_MEDIUM),
1184
            api_get_path(WEB_CODE_PATH).'tracking/lp_report.php?'.api_get_cidreq()
1185
        );
1186
1187
        $attendanceLink = '';
1188
        if (!empty($sessionId)) {
1189
            $attendanceLink = Display::url(
1190
                Display::return_icon('attendance_list.png', get_lang('Logins'), '', ICON_SIZE_MEDIUM),
1191
                api_get_path(WEB_CODE_PATH).'attendance/index.php?'.api_get_cidreq().'&action=calendar_logins'
1192
            );
1193
        }
1194
1195
        switch ($current) {
1196
            case 'users':
1197
                $usersLink = Display::url(
1198
                    Display::return_icon(
1199
                        'user_na.png',
1200
                        get_lang('StudentsTracking'),
1201
                        [],
1202
                        ICON_SIZE_MEDIUM
1203
                    ),
1204
                    '#'
1205
                );
1206
                break;
1207
            case 'groups':
1208
                $groupsLink = Display::url(
1209
                    Display::return_icon('group_na.png', get_lang('GroupReporting'), [], ICON_SIZE_MEDIUM),
1210
                    '#'
1211
                );
1212
                break;
1213
            case 'courses':
1214
                $courseLink = Display::url(
1215
                    Display::return_icon('course_na.png', get_lang('CourseTracking'), [], ICON_SIZE_MEDIUM),
1216
                    '#'
1217
                );
1218
                break;
1219
            case 'resources':
1220
                $resourcesLink = Display::url(
1221
                    Display::return_icon(
1222
                        'tools_na.png',
1223
                        get_lang('ResourcesTracking'),
1224
                        [],
1225
                        ICON_SIZE_MEDIUM
1226
                    ),
1227
                    '#'
1228
                );
1229
                break;
1230
            case 'exams':
1231
                $examLink = Display::url(
1232
                    Display::return_icon('quiz_na.png', get_lang('ExamTracking'), [], ICON_SIZE_MEDIUM),
1233
                    '#'
1234
                );
1235
                break;
1236
            case 'logs':
1237
                $eventsLink = Display::url(
1238
                    Display::return_icon('security_na.png', get_lang('EventsReport'), [], ICON_SIZE_MEDIUM),
1239
                    '#'
1240
                );
1241
                break;
1242
            case 'attendance':
1243
                if (!empty($sessionId)) {
1244
                    $attendanceLink = Display::url(
1245
                        Display::return_icon('attendance_list.png', get_lang('Logins'), '', ICON_SIZE_MEDIUM),
1246
                        '#'
1247
                    );
1248
                }
1249
                break;
1250
            case 'lp':
1251
                $lpLink = Display::url(
1252
                    Display::return_icon(
1253
                        'scorms_na.png',
1254
                        get_lang('CourseLearningPathsGenericStats'),
1255
                        [],
1256
                        ICON_SIZE_MEDIUM
1257
                    ),
1258
                    '#'
1259
                );
1260
                break;
1261
        }
1262
1263
        $items = [
1264
            $usersLink,
1265
            $groupsLink,
1266
            $courseLink,
1267
            $resourcesLink,
1268
            $examLink,
1269
            $eventsLink,
1270
            $lpLink,
1271
            $attendanceLink,
1272
        ];
1273
1274
        return implode('', $items).'&nbsp;';
1275
    }
1276
1277
    public static function getTeachersOrCoachesHtmlHeader(
1278
        string $courseCode,
1279
        int $cId,
1280
        int $sessionId,
1281
        bool $addLinkToPrfile
1282
    ): string {
1283
        $html = '';
1284
1285
        $teacherList = CourseManager::getTeacherListFromCourseCodeToString(
1286
            $courseCode,
1287
            ',',
1288
            $addLinkToPrfile,
1289
            true
1290
        );
1291
1292
        if (!empty($teacherList)) {
1293
            $html .= Display::page_subheader2(get_lang('Teachers'));
1294
            $html .= $teacherList;
1295
        }
1296
1297
        if (!empty($sessionId)) {
1298
            $coaches = CourseManager::get_coachs_from_course_to_string(
1299
                $sessionId,
1300
                $cId,
1301
                ',',
1302
                $addLinkToPrfile,
1303
                true
1304
            );
1305
1306
            if (!empty($coaches)) {
1307
                $html .= Display::page_subheader2(get_lang('Coaches'));
1308
                $html .= $coaches;
1309
            }
1310
        }
1311
1312
        return $html;
1313
    }
1314
1315
    /**
1316
     * @return float|string
1317
     */
1318
    public static function calcBestScoreAverageNotInLP(
1319
        array $exerciseList,
1320
        array $usersInGroup,
1321
        int $cId,
1322
        int $sessionId = 0,
1323
        bool $returnFormatted = false
1324
    ) {
1325
        if (empty($exerciseList) || empty($usersInGroup)) {
1326
            return 0;
1327
        }
1328
1329
        $bestScoreAverageNotInLP = 0;
1330
1331
        foreach ($exerciseList as $exerciseData) {
1332
            foreach ($usersInGroup as $userId) {
1333
                $results = Event::get_best_exercise_results_by_user(
1334
                    $exerciseData['iid'],
1335
                    $cId,
1336
                    $sessionId,
1337
                    $userId
1338
                );
1339
1340
                $scores = array_map(
1341
                    function (array $result) {
1342
                        return empty($result['exe_weighting']) ? 0 : $result['exe_result'] / $result['exe_weighting'];
1343
                    },
1344
                    $results
1345
                );
1346
1347
                $bestScoreAverageNotInLP += $scores ? max($scores) : 0;
1348
            }
1349
        }
1350
1351
        $rounded = round(
1352
            $bestScoreAverageNotInLP / count($exerciseList) * 100 / count($usersInGroup),
1353
            2
1354
        );
1355
1356
        if ($returnFormatted) {
1357
            return sprintf(get_lang('XPercent'), $rounded);
1358
        }
1359
1360
        return $rounded;
1361
    }
1362
1363
    public static function protectIfNotAllowed()
1364
    {
1365
        $courseInfo = api_get_course_info();
1366
1367
        if (empty($courseInfo)) {
1368
            api_not_allowed(true);
1369
        }
1370
1371
        $sessionId = api_get_session_id();
1372
        $isAllowedToTrack = Tracking::isAllowToTrack($sessionId);
1373
1374
        if (!$isAllowedToTrack) {
1375
            api_not_allowed(true);
1376
        }
1377
1378
        $courseCode = $courseInfo['code'];
1379
1380
        // If the user is an HR director (drh)
1381
        if (!api_is_drh()) {
1382
            return;
1383
        }
1384
1385
        // Blocking course for drh
1386
        if (api_drh_can_access_all_session_content()) {
1387
            // If the drh has been configured to be allowed to see all session content, give him access to the session courses
1388
            $coursesFromSession = SessionManager::getAllCoursesFollowedByUser(api_get_user_id(), null);
1389
            $coursesFromSessionCodeList = [];
1390
1391
            if (!empty($coursesFromSession)) {
1392
                foreach ($coursesFromSession as $course) {
1393
                    $coursesFromSessionCodeList[$course['code']] = $course['code'];
1394
                }
1395
            }
1396
1397
            $coursesFollowedList = CourseManager::get_courses_followed_by_drh(api_get_user_id());
1398
1399
            if (!empty($coursesFollowedList)) {
1400
                $coursesFollowedList = array_keys($coursesFollowedList);
1401
            }
1402
1403
            if (!in_array($courseCode, $coursesFollowedList)
1404
                && !in_array($courseCode, $coursesFromSessionCodeList)
1405
            ) {
1406
                api_not_allowed(true);
1407
            }
1408
        } else {
1409
            // If the drh has *not* been configured to be allowed to see all session content,
1410
            // then check if he has also been given access to the corresponding courses
1411
            $coursesFollowedList = CourseManager::get_courses_followed_by_drh(api_get_user_id());
1412
            $coursesFollowedList = array_keys($coursesFollowedList);
1413
1414
            if (!in_array($courseCode, $coursesFollowedList)) {
1415
                api_not_allowed(true);
1416
            }
1417
        }
1418
    }
1419
1420
    public static function returnCourseGraphicalReport(
1421
        array $courseInfo,
1422
        int $sessionId = 0
1423
    ): string {
1424
        if (self::HIDE_COURSE_REPORT_GRAPH_HIDDEN == (int) api_get_configuration_value('hide_course_report_graph')) {
1425
            return '';
1426
        }
1427
1428
        $args = Session::read(
1429
            'course_log_args',
1430
            ['conditions' => [], 'parameters' => []]
1431
        );
1432
        Session::erase('course_log_args');
1433
1434
        $conditions = $args['conditions'];
1435
        $parameters = $args['parameters'];
1436
1437
        $courseCode = $courseInfo['code'];
1438
1439
        if (empty($sessionId)) {
1440
            // Registered students in a course outside session.
1441
            $studentList = CourseManager::get_student_list_from_course_code(
1442
                $courseCode,
1443
                false,
1444
                0,
1445
                null,
1446
                null,
1447
                true,
1448
                0,
1449
                false,
1450
                0,
1451
                0,
1452
                $parameters['user_active']
1453
            );
1454
        } else {
1455
            // Registered students in session.
1456
            $studentList = CourseManager::get_student_list_from_course_code(
1457
                $courseCode,
1458
                true,
1459
                $sessionId,
1460
                null,
1461
                null,
1462
                true,
1463
                0,
1464
                false,
1465
                0,
1466
                0,
1467
                $parameters['user_active']
1468
            );
1469
        }
1470
1471
        $GLOBALS['user_ids'] = array_keys($studentList);
1472
        $GLOBALS['course_code'] = $courseCode;
1473
        $GLOBALS['export_csv'] = false;
1474
        $GLOBALS['session_id'] = $sessionId;
1475
1476
        $nbStudents = count($studentList);
1477
1478
        $conditions['include_invited_users'] = false;
1479
1480
        $usersTracking = TrackingCourseLog::getUserData(
1481
            0,
1482
            $nbStudents,
1483
            0,
1484
            'ASC',
1485
            $conditions
1486
        );
1487
1488
        $numberStudentsCompletedLP = 0;
1489
        $averageStudentsTestScore = 0;
1490
        $scoresDistribution = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
1491
        $userScoreList = [];
1492
        $listStudentIds = [];
1493
        $timeStudent = [];
1494
        $certificateCount = 0;
1495
        $category = Category::load(
1496
            null,
1497
            null,
1498
            $courseCode,
1499
            null,
1500
            null,
1501
            $sessionId
1502
        );
1503
1504
        foreach ($usersTracking as $userTracking) {
1505
            $userInfo = api_get_user_info_from_username($userTracking[3]);
1506
1507
            if (empty($userInfo)) {
1508
                continue;
1509
            }
1510
1511
            $userId = $userInfo['user_id'];
1512
1513
            if ('100%' === $userTracking[5]) {
1514
                $numberStudentsCompletedLP++;
1515
            }
1516
1517
            $averageStudentTestScore = (float) $userTracking[7];
1518
            $averageStudentsTestScore += $averageStudentTestScore;
1519
1520
            $reducedAverage = $averageStudentTestScore === 100.0 ? 9 : floor($averageStudentTestScore / 10);
1521
1522
            if (isset($scoresDistribution[$reducedAverage])) {
1523
                $scoresDistribution[$reducedAverage]++;
1524
            }
1525
1526
            $scoreStudent = substr($userTracking[5], 0, -1) + substr($userTracking[7], 0, -1);
1527
            [$hours, $minutes, $seconds] = preg_split('/:/', $userTracking[4]);
1528
            $minutes = round((3600 * $hours + 60 * $minutes + $seconds) / 60);
1529
1530
            $certificate = false;
1531
1532
            if (isset($category[0]) && $category[0]->is_certificate_available($userId)) {
1533
                $certificate = true;
1534
                $certificateCount++;
1535
            }
1536
1537
            $listStudent = [
1538
                'id' => $userId,
1539
                'fullname' => $userInfo['complete_name'],
1540
                'score' => floor($scoreStudent / 2),
1541
                'total_time' => $minutes,
1542
                'avatar' => $userInfo['avatar'],
1543
                'certicate' => $certificate,
1544
            ];
1545
            $listStudentIds[] = $userId;
1546
            $userScoreList[] = $listStudent;
1547
        }
1548
1549
        uasort(
1550
            $userScoreList,
1551
            function ($a, $b) {
1552
                return $a['score'] <= $b['score'];
1553
            }
1554
        );
1555
1556
        $averageStudentsTestScore = 0;
1557
1558
        if ($nbStudents > 0) {
1559
            $averageStudentsTestScore = round($averageStudentsTestScore / $nbStudents);
1560
        }
1561
1562
        $colors = ChamiloApi::getColorPalette(true, true, 10);
1563
1564
        $tpl = new Template('', false, false, false, true, false, false);
1565
        $tpl->assign('chart_colors', json_encode($colors));
1566
        $tpl->assign('certificate_count', $certificateCount);
1567
        $tpl->assign('score_distribution', json_encode($scoresDistribution));
1568
        $tpl->assign('json_time_student', json_encode($userScoreList));
1569
        $tpl->assign('students_test_score', $averageStudentsTestScore);
1570
        $tpl->assign('students_completed_lp', $numberStudentsCompletedLP);
1571
        $tpl->assign('number_students', $nbStudents);
1572
        $tpl->assign('top_students', array_chunk($userScoreList, 3, true));
1573
1574
        return $tpl->fetch($tpl->get_template('tracking/tracking_course_log.tpl'));
1575
    }
1576
1577
    /**
1578
     * count the number of students in this course (used for SortableTable)
1579
     * Deprecated.
1580
     */
1581
    public function countStudentInCourse(): int
1582
    {
1583
        global $nbStudents;
1584
1585
        return $nbStudents;
1586
    }
1587
1588
    public function sortUsers($a, $b): int
1589
    {
1590
        $tracking = Session::read('tracking_column');
1591
1592
        return strcmp(
1593
            trim(api_strtolower($a[$tracking])),
1594
            trim(api_strtolower($b[$tracking]))
1595
        );
1596
    }
1597
1598
    public function sortUsersDesc($a, $b): int
1599
    {
1600
        $tracking = Session::read('tracking_column');
1601
1602
        return strcmp(
1603
            trim(api_strtolower($b[$tracking])),
1604
            trim(api_strtolower($a[$tracking]))
1605
        );
1606
    }
1607
}
1608